diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..fd952a6 --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,3 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +ModelAnalyzer = "d1179b25-476b-425c-b826-c7787f0fff83" \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..b280ffc --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,8 @@ +using Documenter, ModelAnalyzer + +makedocs(; sitename = "ModelAnalyzer.jl documentation") + +deploydocs(; + repo = "github.com/jump-dev/ModelAnalyzer.jl.git", + push_preview = true, +) diff --git a/docs/src/analyzer.md b/docs/src/analyzer.md new file mode 100644 index 0000000..ab8c463 --- /dev/null +++ b/docs/src/analyzer.md @@ -0,0 +1,24 @@ + +# ModelAnalyzer main API + +All the analysis modules in `ModelAnalyzer` follow the same main API. +The main function to perform an analysis is: + +```@docs +ModelAnalyzer.analyze +``` + +Once the analysis is performed, the resulting data structure can be summarized +using: + +```@docs +ModelAnalyzer.summarize +``` + +Alternatively, you can also query the types of issues found in the analysis +and summarize them individually. The following functions are useful for this: + +```@docs +ModelAnalyzer.list_of_issue_types +ModelAnalyzer.list_of_issues +``` diff --git a/docs/src/feasibility.md b/docs/src/feasibility.md new file mode 100644 index 0000000..8ce2b76 --- /dev/null +++ b/docs/src/feasibility.md @@ -0,0 +1,33 @@ + +# Feasibility Analysis + +This module provides functionality to perform feasibility analysis on a JuMP model. +This module follows the main API and is activated by the struct: + +```@docs +ModelAnalyzer.Feasibility.Analyzer +``` + +The analysis will return issues of the abstract type: + +```@docs +ModelAnalyzer.Feasibility.AbstractFeasibilityIssue +``` +Specifically, the possible issues are: + +```@docs +ModelAnalyzer.Feasibility.PrimalViolation +ModelAnalyzer.Feasibility.DualViolation +ModelAnalyzer.Feasibility.ComplemetarityViolation +ModelAnalyzer.Feasibility.DualObjectiveMismatch +ModelAnalyzer.Feasibility.PrimalObjectiveMismatch +ModelAnalyzer.Feasibility.PrimalDualMismatch +ModelAnalyzer.Feasibility.PrimalDualSolverMismatch +``` + +These issues are saved in the data structure that is returned from the +`ModelAnalyzer.analyze` function: + +```@docs +ModelAnalyzer.Feasibility.Data +``` \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..941b750 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,114 @@ +```@meta +CurrentModule = ModelAnalyzer +DocTestSetup = quote + using ModelAnalyzer +end +``` + +# ModelAnalyzer.jl + +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. + +## Installation + +You can install the package using the Julia package manager. In the Julia REPL, run: + +```julia +using Pkg +Pkg.add("https://github.com/jump-dev/ModelAnalyzer.jl") +``` + +## Usage + +### Basic usage + +Here is a simple example of how to use the package: + +```julia +using JuMP +using ModelAnalyzer +using HiGHS # or any other supported solver +# Create a simple JuMP model +model = Model(HiGHS.Optimizer) +@variable(model, x >= 0) +@variable(model, y >= 0) +@constraint(model, c1, 2x + 3y == 5) +@constraint(model, c2, x + 2y <= 3) +@objective(model, Min, x + y) +# Optimize the model +optimize!(model) + +# either + +# Perform a numerical analysis of the model +data = ModelAnalyzer.analyze(ModelAnalyzer.Numerical.Analyzer(), model) +# print report +ModelAnalyzer.summarize(data) + +# or + +# Check for solution feasibility and optimality +data = ModelAnalyzer.analyze(ModelAnalyzer.Feasibility.Analyzer(), model) +# print report +ModelAnalyzer.summarize(data) + +# or + +# Infeasibility analysis (if the model was infeasible) +data = ModelAnalyzer.analyze( + ModelAnalyzer.Infeasibility.Analyzer(), + model, + optimizer = HiGHS.Optimizer, +) +# print report +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. + +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, +or it can be further inspected programmatically. + +```julia +# given a `data` object obtained from `ModelAnalyzer.analyze(...)` + +# query the types os issues found in the analysis +list = ModelAnalyzer.list_of_issue_types(data) + +# information about the types of issues found can be printed out +ModelAnalyzer.summarize(data, list[1]) + +# for each issue type, you can get the actual issues found in the analysis +issues = ModelAnalyzer.list_of_issues(data, list[1]) + +# the list of issues of the given type can be summarized with: +ModelAnalyzer.summarize(data, issues) + +# infdividual issues can also be summarized +ModelAnalyzer.summarize(data, issues[1]) +``` diff --git a/docs/src/infeasibility.md b/docs/src/infeasibility.md new file mode 100644 index 0000000..befe1a1 --- /dev/null +++ b/docs/src/infeasibility.md @@ -0,0 +1,31 @@ + +# Infeasibility Analysis + +This module provides functionality to perform infeasibility analysis on a JuMP model. +This module follows the main API and is activated by the struct: + +```@docs +ModelAnalyzer.Infeasibility.Analyzer +``` + +The analysis will return issues of the abstract type: + +```@docs +ModelAnalyzer.Infeasibility.AbstractInfeasibilitylIssue +``` + +Specifically, the possible issues are: + +```@docs +ModelAnalyzer.Infeasibility.InfeasibleBounds +ModelAnalyzer.Infeasibility.InfeasibleIntegrality +ModelAnalyzer.Infeasibility.InfeasibleConstraintRange +ModelAnalyzer.Infeasibility.IrreducibleInfeasibleSubset +``` + +These issues are saved in the data structure that is returned from the +`ModelAnalyzer.analyze` function: + +```@docs +ModelAnalyzer.Infeasibility.Data +``` \ No newline at end of file diff --git a/docs/src/numerical.md b/docs/src/numerical.md new file mode 100644 index 0000000..f8a5a67 --- /dev/null +++ b/docs/src/numerical.md @@ -0,0 +1,43 @@ + +# Numerical Analysis + +This module provides functionality to perform numerical analysis on a JuMP model. +This module follows the main API and is activated by the struct: + +```@docs +ModelAnalyzer.Numerical.Analyzer +``` + +The analysis will return issues of the abstract type: + +```@docs +ModelAnalyzer.Numerical.AbstractNumericalIssue +``` + +Specifically the possible issues are: + +```@docs +ModelAnalyzer.Numerical.VariableNotInConstraints +ModelAnalyzer.Numerical.EmptyConstraint +ModelAnalyzer.Numerical.VariableBoundAsConstraint +ModelAnalyzer.Numerical.DenseConstraint +ModelAnalyzer.Numerical.SmallMatrixCoefficient +ModelAnalyzer.Numerical.LargeMatrixCoefficient +ModelAnalyzer.Numerical.SmallBoundCoefficient +ModelAnalyzer.Numerical.LargeBoundCoefficient +ModelAnalyzer.Numerical.SmallRHSCoefficient +ModelAnalyzer.Numerical.LargeRHSCoefficient +ModelAnalyzer.Numerical.SmallObjectiveCoefficient +ModelAnalyzer.Numerical.LargeObjectiveCoefficient +ModelAnalyzer.Numerical.SmallObjectiveQuadraticCoefficient +ModelAnalyzer.Numerical.LargeObjectiveQuadraticCoefficient +ModelAnalyzer.Numerical.NonconvexQuadraticConstraint +ModelAnalyzer.Numerical.SmallMatrixQuadraticCoefficient +ModelAnalyzer.Numerical.LargeMatrixQuadraticCoefficient +``` + +These issues are saved in the data structure that is returned from the `ModelAnalyzer.analyze` function: + +```@docs +ModelAnalyzer.Numerical.Data +``` \ No newline at end of file diff --git a/src/ModelAnalyzer.jl b/src/ModelAnalyzer.jl index 5156c3a..7f4040b 100644 --- a/src/ModelAnalyzer.jl +++ b/src/ModelAnalyzer.jl @@ -11,6 +11,59 @@ abstract type AbstractData end abstract type AbstractAnalyzer end +""" + analyze(analyzer::AbstractAnalyzer, model::JuMP.Model; kwargs...) + +Analyze a JuMP model using the specified analyzer. +Depending on the analyzer, this keyword arguments might vary. +This function will return an instance of `AbstractData` which contains the +results of the analysis that can be further summarized or queried for issues. + +See [`summarize`](@ref), [`list_of_issues`](@ref), and +[`list_of_issue_types`](@ref). +""" +function analyze end + +""" + summarize([io::IO,] AbstractData; verbose = true, max_issues = typemax(Int), kwargs...) + +Print a summary of the analysis results contained in `AbstractData` to the +specified IO stream. If no IO stream is provided, it defaults to `stdout`. +The `verbose` flag controls whether to print detailed information about each +issue (if `true`) or a concise summary (if `false`). The `max_issues` argument +controls the maximum number of issues to display in the summary. If there are +more issues than `max_issues`, only the first `max_issues` will be displayed. + + summarize([io::IO,] ::Type{T}; verbose = true) where {T<:AbstractIssue} + +This variant allows summarizing information of a specific type `T` (which must +be a subtype of `AbstractIssue`). In the verbose case it will provide a text +explaning the issue. In the non-verbose case it will provide just the issue +name. + + summarize([io::IO,] issue::AbstractIssue; verbose = true) + +This variant allows summarizing a single issue instance of type `AbstractIssue`. +""" +function summarize end + +""" + list_of_issue_types(data::AbstractData) + +Return a vector of `DataType` containing the types of issues found in the +analysis results contained in `data`. +""" +function list_of_issue_types end + +""" + list_of_issues(data::AbstractData, issue_type::Type{T}) where {T<:AbstractIssue} + +Return a vector of instances of `T` (which must be a subtype of `AbstractIssue`) +found in the analysis results contained in `data`. This allows you to retrieve +all instances of a specific issue type from the analysis results. +""" +function list_of_issues end + function summarize(io::IO, ::Type{T}; verbose = true) where {T<:AbstractIssue} if verbose return _verbose_summarize(io, T) @@ -49,10 +102,6 @@ function summarize(data::AbstractData; kwargs...) return summarize(stdout, data; kwargs...) end -function analyze end -function list_of_issues end -function list_of_issue_types end - function _verbose_summarize end function _summarize end diff --git a/src/feasibility.jl b/src/feasibility.jl index d02b47e..5f1d03f 100644 --- a/src/feasibility.jl +++ b/src/feasibility.jl @@ -17,45 +17,137 @@ import JuMP import JuMP.MOI as MOI import Printf +""" + Analyzer() <: ModelAnalyzer.AbstractAnalyzer + +The `Analyzer` type is used to perform feasibility analysis on a JuMP model. + +## Example + +```julia +julia> data = ModelAnalyzer.analyze( + ModelAnalyzer.Feasibility.Analyzer(), + model; + primal_point::Union{Nothing, Dict} = nothing, + dual_point::Union{Nothing, Dict} = nothing, + atol::Float64 = 1e-6, + skip_missing::Bool = false, + dual_check = true, +); +``` + +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. +- `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. +- `atol`: The absolute tolerance for feasibility checking. +- `skip_missing`: If `true`, constraints with missing variables in the provided +point will be ignored. +- `dual_check`: If `true`, it will perform dual feasibility checking. Disabling +the dual check will also disable complementarity checking. +""" struct Analyzer <: ModelAnalyzer.AbstractAnalyzer end +""" + AbstractFeasibilityIssue <: AbstractNumericalIssue + +Abstract type for feasibility issues found during the analysis of a JuMP model. +""" abstract type AbstractFeasibilityIssue <: ModelAnalyzer.AbstractIssue end +""" + PrimalViolation <: AbstractFeasibilityIssue + +The `PrimalViolation` issue is identified when a primal constraint has a +left-hand-side value that is not within the constraint's set. +""" struct PrimalViolation <: AbstractFeasibilityIssue ref::JuMP.ConstraintRef violation::Float64 end +""" + DualViolation <: AbstractFeasibilityIssue + +The `DualViolation` issue is identified when a constraint has a dual value +that is not within the dual constraint's set. +""" struct DualViolation <: AbstractFeasibilityIssue ref::Union{JuMP.ConstraintRef,JuMP.VariableRef} violation::Float64 end +""" + ComplemetarityViolation <: AbstractFeasibilityIssue + +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. +""" struct ComplemetarityViolation <: AbstractFeasibilityIssue ref::JuMP.ConstraintRef violation::Float64 end +""" + DualObjectiveMismatch <: AbstractFeasibilityIssue + +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. +""" struct DualObjectiveMismatch <: AbstractFeasibilityIssue obj::Float64 obj_solver::Float64 end +""" + PrimalObjectiveMismatch <: AbstractFeasibilityIssue + +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. +""" struct PrimalObjectiveMismatch <: AbstractFeasibilityIssue obj::Float64 obj_solver::Float64 end +""" + PrimalDualMismatch <: AbstractFeasibilityIssue + +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. +""" struct PrimalDualMismatch <: AbstractFeasibilityIssue primal::Float64 dual::Float64 end +""" + PrimalDualSolverMismatch <: AbstractFeasibilityIssue + +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. +""" struct PrimalDualSolverMismatch <: AbstractFeasibilityIssue primal::Float64 dual::Float64 end +""" + Data + +The `Data` structure holds the results of the feasibility analysis performed +by the `ModelAnalyzer.analyze` function for a JuMP model. It contains +the configuration used for the analysis, the primal and dual points, and +the lists of various feasibility issues found during the analysis. +""" Base.@kwdef mutable struct Data <: ModelAnalyzer.AbstractData # analysis configuration primal_point::Union{Nothing,AbstractDict} diff --git a/src/infeasibility.jl b/src/infeasibility.jl index 8d117ce..f9b329b 100644 --- a/src/infeasibility.jl +++ b/src/infeasibility.jl @@ -11,16 +11,53 @@ import JuMP.MOI as MOI include("intervals.jl") +""" + Analyzer() <: ModelAnalyzer.AbstractAnalyzer + +The `Analyzer` type is used to perform infeasibility analysis on a JuMP model. + +## Example +```julia +julia> data = ModelAnalyzer.analyze( + Analyzer(), + model, + optimizer = nothing,, +) +``` + +The additional keyword argument `optimizer` is used to specify the optimizer to +use for the IIS resolver. +""" struct Analyzer <: ModelAnalyzer.AbstractAnalyzer end +""" + AbstractInfeasibilitylIssue + +Abstract type for infeasibility issues found during the analysis of a JuMP +model. +""" abstract type AbstractInfeasibilitylIssue <: ModelAnalyzer.AbstractIssue end +""" + InfeasibleBounds{T} <: AbstractInfeasibilitylIssue + +The `InfeasibleBounds` issue is identified when a variable has a lower bound +that is greater than its upper bound. +""" struct InfeasibleBounds{T} <: AbstractInfeasibilitylIssue variable::JuMP.VariableRef lb::T ub::T end +""" + InfeasibleIntegrality{T} <: AbstractInfeasibilitylIssue + +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. +""" struct InfeasibleIntegrality{T} <: AbstractInfeasibilitylIssue variable::JuMP.VariableRef lb::T @@ -28,6 +65,16 @@ struct InfeasibleIntegrality{T} <: AbstractInfeasibilitylIssue set::Union{MOI.Integer,MOI.ZeroOne}#, MOI.Semicontinuous{T}, MOI.Semiinteger{T}} end +""" + InfeasibleConstraintRange{T} <: AbstractInfeasibilitylIssue + +The `InfeasibleConstraintRange` issue is identified when a constraint cannot +be satisfied given the variable bounds. This analysis only considers one +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. +""" struct InfeasibleConstraintRange{T} <: AbstractInfeasibilitylIssue constraint::JuMP.ConstraintRef lb::T @@ -35,10 +82,27 @@ struct InfeasibleConstraintRange{T} <: AbstractInfeasibilitylIssue set::Union{MOI.EqualTo{T},MOI.LessThan{T},MOI.GreaterThan{T}} end +""" + IrreducibleInfeasibleSubset <: AbstractInfeasibilitylIssue + +The `IrreducibleInfeasibleSubset` issue is identified when a subset of +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. +""" struct IrreducibleInfeasibleSubset <: AbstractInfeasibilitylIssue constraint::Vector{JuMP.ConstraintRef} end +""" + Data <: ModelAnalyzer.AbstractData + +The `Data` type is used to store the results of the infeasibility analysis. +This type contains vectors of the various infeasibility issues found during +the analysis, including `InfeasibleBounds`, `InfeasibleIntegrality`, +`InfeasibleConstraintRange`, and `IrreducibleInfeasibleSubset`. +""" Base.@kwdef mutable struct Data <: ModelAnalyzer.AbstractData infeasible_bounds::Vector{InfeasibleBounds} = InfeasibleBounds[] infeasible_integrality::Vector{InfeasibleIntegrality} = diff --git a/src/numerical.jl b/src/numerical.jl index 2f327c0..734609c 100644 --- a/src/numerical.jl +++ b/src/numerical.jl @@ -11,85 +11,216 @@ import LinearAlgebra import JuMP.MOI as MOI import Printf +""" + Analyzer() <: ModelAnalyzer.AbstractAnalyzer + +The `Analyzer` type is used to analyze the coefficients of a JuMP model for + numerical issues. + +## Example + +```julia +julia> data = ModelAnalyzer.analyze( + ModelAnalyzer.Numerical.Analyzer(), + model; + threshold_dense_fill_in = 0.10, + threshold_dense_entries = 1000, + threshold_small = 1e-5, + threshold_large = 1e+5, +) +``` + +The additional parameters: +- `threshold_dense_fill_in`: The threshold for the fraction of non-zero entries +in a constraint to be considered dense. +- `threshold_dense_entries`: The minimum number of non-zero entries for a +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. + +""" struct Analyzer <: ModelAnalyzer.AbstractAnalyzer end +""" + AbstractNumericalIssue <: AbstractNumericalIssue + +Abstract type for numerical issues found during the analysis of a JuMP model. +""" abstract type AbstractNumericalIssue <: ModelAnalyzer.AbstractIssue end +""" + VariableNotInConstraints <: AbstractNumericalIssue + +The `VariableNotInConstraints` issue is identified when a variable appears in no +constraints. +""" struct VariableNotInConstraints <: AbstractNumericalIssue ref::JuMP.VariableRef end +""" + EmptyConstraint <: AbstractNumericalIssue + +The `EmptyConstraint` issue is identified when a constraint has no coefficients +different from zero. +""" struct EmptyConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef end +""" + VariableBoundAsConstraint <: AbstractNumericalIssue + +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. +""" struct VariableBoundAsConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef end +""" + DenseConstraint <: AbstractNumericalIssue + +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`. +""" struct DenseConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef nnz::Int end +""" + SmallMatrixCoefficient <: AbstractNumericalIssue + +The `SmallMatrixCoefficient` issue is identified when a matrix coefficient in a +constraint is smaller than `threshold_small`. +""" struct SmallMatrixCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef variable::JuMP.VariableRef coefficient::Float64 end +""" + LargeMatrixCoefficient <: AbstractNumericalIssue + +The `LargeMatrixCoefficient` issue is identified when a matrix coefficient in a +constraint is larger than `threshold_large`. +""" struct LargeMatrixCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef variable::JuMP.VariableRef coefficient::Float64 end +""" + SmallBoundCoefficient <: AbstractNumericalIssue + +The `SmallBoundCoefficient` issue is identified when a variable's bound +(coefficient) is smaller than `threshold_small`. +""" struct SmallBoundCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef coefficient::Float64 end +""" + LargeBoundCoefficient <: AbstractNumericalIssue + +The `LargeBoundCoefficient` issue is identified when a variable's bound +(coefficient) is larger than `threshold_large`. +""" struct LargeBoundCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef coefficient::Float64 end +""" + SmallRHSCoefficient <: AbstractNumericalIssue + +The `SmallRHSCoefficient` issue is identified when the right-hand-side (RHS) +coefficient of a constraint is smaller than `threshold_small`. +""" struct SmallRHSCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef coefficient::Float64 end +""" + LargeRHSCoefficient <: AbstractNumericalIssue + +The `LargeRHSCoefficient` issue is identified when the right-hand-side (RHS) +coefficient of a constraint is larger than `threshold_large`. +""" struct LargeRHSCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef coefficient::Float64 end +""" + SmallObjectiveCoefficient <: AbstractNumericalIssue + +The `SmallObjectiveCoefficient` issue is identified when a coefficient in the +objective function is smaller than `threshold_small`. +""" struct SmallObjectiveCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef coefficient::Float64 end +""" + LargeObjectiveCoefficient <: AbstractNumericalIssue + +The `LargeObjectiveCoefficient` issue is identified when a coefficient in the +objective function is larger than `threshold_large`. +""" struct LargeObjectiveCoefficient <: AbstractNumericalIssue variable::JuMP.VariableRef coefficient::Float64 end +""" + SmallObjectiveQuadraticCoefficient <: AbstractNumericalIssue + +The `SmallObjectiveQuadraticCoefficient` issue is identified when a quadratic +coefficient in the objective function is smaller than `threshold_small`. +""" struct SmallObjectiveQuadraticCoefficient <: AbstractNumericalIssue variable1::JuMP.VariableRef variable2::JuMP.VariableRef coefficient::Float64 end +""" + LargeObjectiveQuadraticCoefficient <: AbstractNumericalIssue + +The `LargeObjectiveQuadraticCoefficient` issue is identified when a quadratic +coefficient in the objective function is larger than `threshold_large`. +""" struct LargeObjectiveQuadraticCoefficient <: AbstractNumericalIssue variable1::JuMP.VariableRef variable2::JuMP.VariableRef coefficient::Float64 end +""" + NonconvexQuadraticConstraint + +The `NonconvexQuadraticConstraint` issue is identified when a quadratic +constraint is non-convex. +""" struct NonconvexQuadraticConstraint <: AbstractNumericalIssue ref::JuMP.ConstraintRef end +""" + SmallMatrixQuadraticCoefficient <: AbstractNumericalIssue + +The `SmallMatrixQuadraticCoefficient` issue is identified when a quadratic +coefficient in a constraint is smaller than `threshold_small`. +""" struct SmallMatrixQuadraticCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef variable1::JuMP.VariableRef @@ -97,6 +228,12 @@ struct SmallMatrixQuadraticCoefficient <: AbstractNumericalIssue coefficient::Float64 end +""" + LargeMatrixQuadraticCoefficient <: AbstractNumericalIssue + +The `LargeMatrixQuadraticCoefficient` issue is identified when a quadratic +coefficient in a constraint is larger than `threshold_large`. +""" struct LargeMatrixQuadraticCoefficient <: AbstractNumericalIssue ref::JuMP.ConstraintRef variable1::JuMP.VariableRef @@ -104,6 +241,13 @@ struct LargeMatrixQuadraticCoefficient <: AbstractNumericalIssue coefficient::Float64 end +""" + Data + +The `Data` structure holds the results of the analysis performed by the +`ModelAnalyzer.Numerical.Analyzer`. It contains various thresholds and the +information about the model's variables, constraints, and objective function. +""" Base.@kwdef mutable struct Data <: ModelAnalyzer.AbstractData # analysis configuration threshold_dense_fill_in::Float64 = 0.10