Skip to content

Refactor filenames and move some code around #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ext/ModelAnalyzerJuMPExt/ModelAnalyzerJuMPExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

module ModelAnalyzerJuMPExt

import ModelAnalyzer
import JuMP
import MathOptInterface as MOI
import ModelAnalyzer
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like alphabetical imports


function ModelAnalyzer.analyze(
analyzer::T,
analyzer::ModelAnalyzer.AbstractAnalyzer,
model::JuMP.GenericModel;
kwargs...,
) where {T<:ModelAnalyzer.AbstractAnalyzer}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use where unless:

  • it is needed in the body of the function
  • its needed across multiple input arguments
  • it's hard/impossible to write the method without it

)
moi_model = JuMP.backend(model)
result = ModelAnalyzer.analyze(analyzer, moi_model; kwargs...)
return result
Expand Down
6 changes: 3 additions & 3 deletions src/ModelAnalyzer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ function _name(ref, ::Nothing)
return "$ref"
end

include("numerical.jl")
include("feasibility.jl")
include("infeasibility.jl")
include("analyzers/Numerical.jl")
include("analyzers/Feasibility.jl")
include("analyzers/Infeasibility.jl")

end # module ModelAnalyzer
24 changes: 0 additions & 24 deletions src/_eval_variables.jl

This file was deleted.

8 changes: 4 additions & 4 deletions src/feasibility.jl → src/analyzers/Feasibility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

module Feasibility

import ModelAnalyzer
import Dualization
import MathOptInterface as MOI
import ModelAnalyzer
import Printf

"""
Expand Down Expand Up @@ -103,7 +103,7 @@ ModelAnalyzer.value(issue::DualConstraintViolation) = issue.violation
"""
DualConstrainedVariableViolation <: AbstractFeasibilityIssue

The `DualConstrainedVariableViolation` issue is identified when a dual
The `DualConstrainedVariableViolation` issue is identified when a dual
constraint, which is a constrained varaible constraint, has a value
that is not within the dual constraint's set.
During the dualization process, each primal constraint is mapped to a dual
Expand Down Expand Up @@ -301,7 +301,7 @@ function ModelAnalyzer._verbose_summarize(io::IO, ::Type{PrimalViolation})

## What

A `PrimalViolation` issue is identified when a constraint has
A `PrimalViolation` issue is identified when a constraint has
function , i.e., a left-hand-side value, that is not within
the constraint's set.

Expand Down Expand Up @@ -1025,7 +1025,7 @@ function _dual_point_to_dual_model_ref(
# if !(primal_con isa MOI.ConstraintIndex{MOI.VariableIndex,<:MOI.EqualTo} ||
# primal_con isa MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Zeros}
# SAF in EQ, etc...
#)
#)
# error("Problem with dualization, see: $primal_con")
# end
end
Expand Down
64 changes: 55 additions & 9 deletions src/infeasibility.jl → src/analyzers/Infeasibility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,57 @@

module Infeasibility

import ModelAnalyzer
import MathOptInterface as MOI
import ModelAnalyzer

# This type and the associated function were inspired by IntervalArithmetic.jl
# Copyright (c) 2014-2021: David P. Sanders & Luis Benet
struct Interval{T<:Real}
lo::T
hi::T

function Interval(lo::T, hi::T) where {T<:Real}
@assert lo <= hi
return new{T}(lo, hi)
end
end

Base.convert(::Type{Interval{T}}, x::T) where {T<:Real} = Interval(x, x)

Base.zero(::Type{Interval{T}}) where {T<:Real} = Interval(zero(T), zero(T))

Base.iszero(a::Interval) = iszero(a.hi) && iszero(a.lo)

function Base.:+(a::Interval{T}, b::Interval{T}) where {T<:Real}
return Interval(a.lo + b.lo, a.hi + b.hi)
end

function Base.:*(x::T, a::Interval{T}) where {T<:Real}
if iszero(a) || iszero(x)
return Interval(zero(T), zero(T))
elseif x >= zero(T)
return Interval(a.lo * x, a.hi * x)
end
return Interval(a.hi * x, a.lo * x)
end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's questionable that a very simple type like this and a few methods need a license attribution. Since both packages are MIT, we're in the clear, so I just moved into this file.


function _eval_variables(value_fn::Function, t::MOI.ScalarAffineTerm)
return t.coefficient * value_fn(t.variable)
end

include("intervals.jl")
include("_eval_variables.jl")
function _eval_variables(
value_fn::Function,
f::MOI.ScalarAffineFunction{T},
) where {T}
# TODO: this conversion exists in JuMP, but not in MOI
S = Base.promote_op(value_fn, MOI.VariableIndex)
U = MOI.MA.promote_operation(*, T, S)
out = convert(U, f.constant)
for t in f.terms
out += _eval_variables(value_fn, t)
end
return out
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here: why is this _eval_variales needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because MOI assumes the output type is the same as the ScalarAffineFunction (Float64), but we need the output to be an interval. JuMP does is correctly: #19 (comment)

end

"""
Analyzer() <: ModelAnalyzer.AbstractAnalyzer
Expand Down Expand Up @@ -63,7 +109,7 @@ ModelAnalyzer.values(issue::InfeasibleBounds) = [issue.lb, issue.ub]
InfeasibleIntegrality{T} <: AbstractInfeasibilitylIssue

The `InfeasibleIntegrality` issue is identified when a variable has an
integrality constraint (like `MOI.Integer` or `MOI.ZeroOne`) that is not
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.

Expand Down Expand Up @@ -580,7 +626,7 @@ function ModelAnalyzer._verbose_summarize(
## Why

This can be a sign of a mistake in the model formulation. This error
will lead to infeasibility in the optimization problem.
will lead to infeasibility in the optimization problem.

## How to fix

Expand Down Expand Up @@ -611,7 +657,7 @@ function ModelAnalyzer._verbose_summarize(
## Why

This can be a sign of a mistake in the model formulation. This error
will lead to infeasibility in the optimization problem.
will lead to infeasibility in the optimization problem.

## How to fix

Expand Down Expand Up @@ -642,7 +688,7 @@ function ModelAnalyzer._verbose_summarize(
## Why

This can be a sign of a mistake in the model formulation. This error
will lead to infeasibility in the optimization problem.
will lead to infeasibility in the optimization problem.

## How to fix

Expand All @@ -667,12 +713,12 @@ function ModelAnalyzer._verbose_summarize(
## What

An `IrreducibleInfeasibleSubset` issue is identified when a subset of constraints
cannot be satisfied simultaneously.
cannot be satisfied simultaneously.

## Why

This can be a sign of a mistake in the model formulation. This error
will lead to infeasibility in the optimization problem.
will lead to infeasibility in the optimization problem.

## How to fix

Expand Down
4 changes: 2 additions & 2 deletions src/numerical.jl → src/analyzers/Numerical.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

module Numerical

import ModelAnalyzer
import LinearAlgebra
import Printf
import MathOptInterface as MOI
import ModelAnalyzer
import Printf

"""
Analyzer() <: ModelAnalyzer.AbstractAnalyzer
Expand Down
112 changes: 0 additions & 112 deletions src/intervals.jl

This file was deleted.

9 changes: 4 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ using Test

@testset "ModelAnalyzer" begin
for file in readdir(@__DIR__)
if !endswith(file, ".jl") || file in ("runtests.jl",)
continue
end
@testset "$file" begin
include(file)
if startswith(file, "test_") && endswith(file, ".jl")
@testset "$file" begin
include(file)
end
end
end
end
10 changes: 5 additions & 5 deletions test/feasibility.jl → test/test_Feasibility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

module TestDualFeasibilityChecker
module TestFeasibility

using ModelAnalyzer
using Test
Expand All @@ -30,7 +30,7 @@ function test_no_solution()
model,
)
# no dual solutions available
# @test_throws ErrorException
# @test_throws ErrorException
data = ModelAnalyzer.analyze(
ModelAnalyzer.Feasibility.Analyzer(),
model,
Expand Down Expand Up @@ -195,7 +195,7 @@ function test_no_lb()
# the dual is:
# Max 0
# Subject to
# y == 1 (as a constraint)
# y == 1 (as a constraint)
# y >= 0 (as a bound)
# mayber force fail here
# @test_throws ErrorException
Expand Down Expand Up @@ -1066,6 +1066,6 @@ function test_nl_obj()
return
end

end # module
end # module TestFeasibility

TestDualFeasibilityChecker.runtests()
TestFeasibility.runtests()
Loading