diff --git a/docs/src/index.md b/docs/src/index.md index 941b750..780a434 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -7,29 +7,35 @@ end # ModelAnalyzer.jl -This package provides tools for analyzing (and debugging) [JuMP](https://github.com/jump-dev/JuMP.jl) models. +This package provides tools for analyzing (and debugging) +[JuMP](https://github.com/jump-dev/JuMP.jl) models. Three main functionalities are provided: -1. **Numerical Analysis**: Check for numerical issues in the model, such as large and small coefficients, -empty constraints, non-convex quadratic functions. - -2. **Feasibility Analysis**: Given an optimized model, or a candidate solution, check if the solutions is -feasible and optimal (when possible). This includes checking the feasibility of the primal model and -also the dual model (if available). Complementary slackness conditions are also checked (if applicable). - -3. **Infeasibility Analysis**: Given an unsolved of solved model, three steps are made to check for - infeasibility: - - Check bounds, integers and binaries consistency is also checked at this point. - - Propagate bounds in constraints individually, to check if each constraint is infeasible - given the current variable bounds. This is only done if bounds are ok. - - Run an IIS (Irreducible Inconsistent Subsystem / irreducible infeasible sets) - resolver algorithm to find a minimal infeasible subset of constraints. - This is only done if no issues are found in the previous two steps. + 1. **Numerical Analysis**: Check for numerical issues in the model, such as + large and small coefficients, empty constraints, non-convex quadratic + functions. + 2. **Feasibility Analysis**: Given an optimized model, or a candidate solution, + check if the solutions is feasible and optimal (when possible). This + includes checking the feasibility of the primal model and also the dual + model (if available). Complementary slackness conditions are also checked + (if applicable). + 3. **Infeasibility Analysis**: Given an unsolved of solved model, three steps + are made to check for infeasibility: + - Check bounds, integers and binaries consistency is also checked at this + point. + - Propagate bounds in constraints individually, to check if each constraint + is infeasible given the current variable bounds. This is only done if + bounds are ok. + - Run an IIS (Irreducible Inconsistent Subsystem / irreducible infeasible + sets) resolver algorithm to find a minimal infeasible subset of + constraints. This is only done if no issues are found in the previous two + steps. ## Installation -You can install the package using the Julia package manager. In the Julia REPL, run: +You can install the package using the Julia package manager. In the Julia REPL, +run: ```julia using Pkg @@ -84,14 +90,16 @@ ModelAnalyzer.summarize(data) The `ModelAnalyzer.analyze(...)` function can always take the keyword arguments: * `verbose = false` to condense the print output. - * `max_issues = n` to limit the maximum number of issues to report for each type. + * `max_issues = n` to limit the maximum number of issues to report for each + type. -For certain analysis modes, the `summarize` function can take additional arguments. +For certain analysis modes, the `summarize` function can take additional +arguments. ### Advanced usage -After any `ModelAnalyzer.analyze(...)` call is performed, the resulting data structure -can be summarized using `ModelAnalyzer.summarize(data)` as show above, +After any `ModelAnalyzer.analyze(...)` call is performed, the resulting data +structure can be summarized using `ModelAnalyzer.summarize(data)` as show above, or it can be further inspected programmatically. ```julia diff --git a/src/feasibility.jl b/src/feasibility.jl index 5f1d03f..e96a04b 100644 --- a/src/feasibility.jl +++ b/src/feasibility.jl @@ -38,15 +38,15 @@ julia> data = ModelAnalyzer.analyze( The additional parameters: - `primal_point`: The primal solution point to use for feasibility checking. -If `nothing`, it will use the current primal solution from optimized model. + If `nothing`, it will use the current primal solution from optimized model. - `dual_point`: The dual solution point to use for feasibility checking. -If `nothing` and the model can be dualized, it will use the current dual -solution from the model. + If `nothing` and the model can be dualized, it will use the current dual + solution from the model. - `atol`: The absolute tolerance for feasibility checking. - `skip_missing`: If `true`, constraints with missing variables in the provided -point will be ignored. + point will be ignored. - `dual_check`: If `true`, it will perform dual feasibility checking. Disabling -the dual check will also disable complementarity checking. + the dual check will also disable complementarity checking. """ struct Analyzer <: ModelAnalyzer.AbstractAnalyzer end @@ -62,6 +62,11 @@ abstract type AbstractFeasibilityIssue <: ModelAnalyzer.AbstractIssue end The `PrimalViolation` issue is identified when a primal constraint has a left-hand-side value that is not within the constraint's set. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.PrimalViolation) +``` """ struct PrimalViolation <: AbstractFeasibilityIssue ref::JuMP.ConstraintRef @@ -73,6 +78,11 @@ end The `DualViolation` issue is identified when a constraint has a dual value that is not within the dual constraint's set. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.DualViolation) +``` """ struct DualViolation <: AbstractFeasibilityIssue ref::Union{JuMP.ConstraintRef,JuMP.VariableRef} @@ -86,6 +96,11 @@ The `ComplemetarityViolation` issue is identified when a pair of primal constraint and dual variable has a nonzero complementarity value, i.e., the inner product of the primal constraint's slack and the dual variable's violation is not zero. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.ComplemetarityViolation) +``` """ struct ComplemetarityViolation <: AbstractFeasibilityIssue ref::JuMP.ConstraintRef @@ -98,6 +113,11 @@ end The `DualObjectiveMismatch` issue is identified when the dual objective value computed from problem data and the dual solution does not match the solver's dual objective value. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.DualObjectiveMismatch) +``` """ struct DualObjectiveMismatch <: AbstractFeasibilityIssue obj::Float64 @@ -110,6 +130,11 @@ end The `PrimalObjectiveMismatch` issue is identified when the primal objective value computed from problem data and the primal solution does not match the solver's primal objective value. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.PrimalObjectiveMismatch) +``` """ struct PrimalObjectiveMismatch <: AbstractFeasibilityIssue obj::Float64 @@ -122,6 +147,11 @@ end The `PrimalDualMismatch` issue is identified when the primal objective value computed from problem data and the primal solution does not match the dual objective value computed from problem data and the dual solution. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.PrimalDualMismatch) +``` """ struct PrimalDualMismatch <: AbstractFeasibilityIssue primal::Float64 @@ -134,6 +164,11 @@ end The `PrimalDualSolverMismatch` issue is identified when the primal objective value reported by the solver does not match the dual objective value reported by the solver. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Feasibility.PrimalDualSolverMismatch) +``` """ struct PrimalDualSolverMismatch <: AbstractFeasibilityIssue primal::Float64 diff --git a/src/infeasibility.jl b/src/infeasibility.jl index f9b329b..419d2ed 100644 --- a/src/infeasibility.jl +++ b/src/infeasibility.jl @@ -43,6 +43,11 @@ abstract type AbstractInfeasibilitylIssue <: ModelAnalyzer.AbstractIssue end The `InfeasibleBounds` issue is identified when a variable has a lower bound that is greater than its upper bound. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Infeasibility.InfeasibleBounds) +```` """ struct InfeasibleBounds{T} <: AbstractInfeasibilitylIssue variable::JuMP.VariableRef @@ -57,6 +62,13 @@ The `InfeasibleIntegrality` issue is identified when a variable has an integrality constraint (like `MOI.Integer` or `MOI.ZeroOne`) that is not consistent with its bounds. That is, the bounds do not allow for any integer value to be feasible. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Infeasibility.InfeasibleIntegrality +) +``` """ struct InfeasibleIntegrality{T} <: AbstractInfeasibilitylIssue variable::JuMP.VariableRef @@ -74,6 +86,13 @@ constraint at a time and all variable bounds of variables involved in the constraint. This issue can only be found is all variable bounds are consistent, that is, no issues of type `InfeasibleBounds` were found in the first layer of analysis. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Infeasibility.InfeasibleConstraintRange +) +``` """ struct InfeasibleConstraintRange{T} <: AbstractInfeasibilitylIssue constraint::JuMP.ConstraintRef @@ -90,6 +109,13 @@ constraints cannot be satisfied simultaneously. This is typically found by the IIS resolver after the first two layers of infeasibility analysis have been completed with no issues, that is, no issues of any other type were found. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Infeasibility.IrreducibleInfeasibleSubset +) +``` """ struct IrreducibleInfeasibleSubset <: AbstractInfeasibilitylIssue constraint::Vector{JuMP.ConstraintRef} diff --git a/src/numerical.jl b/src/numerical.jl index 734609c..62ba782 100644 --- a/src/numerical.jl +++ b/src/numerical.jl @@ -15,7 +15,7 @@ import Printf Analyzer() <: ModelAnalyzer.AbstractAnalyzer The `Analyzer` type is used to analyze the coefficients of a JuMP model for - numerical issues. +numerical issues. ## Example @@ -32,9 +32,9 @@ julia> data = ModelAnalyzer.analyze( The additional parameters: - `threshold_dense_fill_in`: The threshold for the fraction of non-zero entries -in a constraint to be considered dense. + in a constraint to be considered dense. - `threshold_dense_entries`: The minimum number of non-zero entries for a -constraint to be considered dense. + constraint to be considered dense. - `threshold_small`: The threshold for small coefficients in the model. - `threshold_large`: The threshold for large coefficients in the model. @@ -53,6 +53,11 @@ abstract type AbstractNumericalIssue <: ModelAnalyzer.AbstractIssue end The `VariableNotInConstraints` issue is identified when a variable appears in no constraints. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.VariableNotInConstraints) +``` """ struct VariableNotInConstraints <: AbstractNumericalIssue ref::JuMP.VariableRef @@ -63,6 +68,11 @@ end The `EmptyConstraint` issue is identified when a constraint has no coefficients different from zero. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.EmptyConstraint) +``` """ struct EmptyConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -74,6 +84,11 @@ end The `VariableBoundAsConstraint` issue is identified when a constraint is equivalent to a variable bound, that is, the constraint has only one non-zero coefficient, and this coefficient is equal to one. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.VariableBoundAsConstraint) +``` """ struct VariableBoundAsConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -85,6 +100,11 @@ end The `DenseConstraint` issue is identified when a constraint has a fraction of non-zero entries greater than `threshold_dense_fill_in` and the number of non-zero entries is greater than `threshold_dense_entries`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.DenseConstraint) +``` """ struct DenseConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -96,6 +116,11 @@ end The `SmallMatrixCoefficient` issue is identified when a matrix coefficient in a constraint is smaller than `threshold_small`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.SmallMatrixCoefficient) +``` """ struct SmallMatrixCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -108,6 +133,11 @@ end The `LargeMatrixCoefficient` issue is identified when a matrix coefficient in a constraint is larger than `threshold_large`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.LargeMatrixCoefficient) +``` """ struct LargeMatrixCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -120,6 +150,11 @@ end The `SmallBoundCoefficient` issue is identified when a variable's bound (coefficient) is smaller than `threshold_small`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.SmallBoundCoefficient) +``` """ struct SmallBoundCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef @@ -131,6 +166,11 @@ end The `LargeBoundCoefficient` issue is identified when a variable's bound (coefficient) is larger than `threshold_large`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.LargeBoundCoefficient) +``` """ struct LargeBoundCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef @@ -142,6 +182,11 @@ end The `SmallRHSCoefficient` issue is identified when the right-hand-side (RHS) coefficient of a constraint is smaller than `threshold_small`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.SmallRHSCoefficient) +``` """ struct SmallRHSCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -153,6 +198,11 @@ end The `LargeRHSCoefficient` issue is identified when the right-hand-side (RHS) coefficient of a constraint is larger than `threshold_large`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.LargeRHSCoefficient) +``` """ struct LargeRHSCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -164,6 +214,11 @@ end The `SmallObjectiveCoefficient` issue is identified when a coefficient in the objective function is smaller than `threshold_small`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.SmallObjectiveCoefficient) +``` """ struct SmallObjectiveCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef @@ -175,6 +230,11 @@ end The `LargeObjectiveCoefficient` issue is identified when a coefficient in the objective function is larger than `threshold_large`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize(ModelAnalyzer.Numerical.LargeObjectiveCoefficient) +``` """ struct LargeObjectiveCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef @@ -186,6 +246,13 @@ end The `SmallObjectiveQuadraticCoefficient` issue is identified when a quadratic coefficient in the objective function is smaller than `threshold_small`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Numerical.SmallObjectiveQuadraticCoefficient +) +``` """ struct SmallObjectiveQuadraticCoefficient <: AbstractNumericalIssue variable1::JuMP.VariableRef @@ -198,6 +265,13 @@ end The `LargeObjectiveQuadraticCoefficient` issue is identified when a quadratic coefficient in the objective function is larger than `threshold_large`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Numerical.LargeObjectiveQuadraticCoefficient +) +``` """ struct LargeObjectiveQuadraticCoefficient <: AbstractNumericalIssue variable1::JuMP.VariableRef @@ -210,6 +284,13 @@ end The `NonconvexQuadraticConstraint` issue is identified when a quadratic constraint is non-convex. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Numerical.NonconvexQuadraticConstraint +) +``` """ struct NonconvexQuadraticConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -220,6 +301,13 @@ end The `SmallMatrixQuadraticCoefficient` issue is identified when a quadratic coefficient in a constraint is smaller than `threshold_small`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Numerical.SmallMatrixQuadraticCoefficient +) +``` """ struct SmallMatrixQuadraticCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef @@ -233,6 +321,13 @@ end The `LargeMatrixQuadraticCoefficient` issue is identified when a quadratic coefficient in a constraint is larger than `threshold_large`. + +For more information, run: +```julia +julia> ModelAnalyzer.summarize( + ModelAnalyzer.Numerical.LargeMatrixQuadraticCoefficient +) +``` """ struct LargeMatrixQuadraticCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef