Skip to content

Commit

Permalink
Add primal_feasibility_report(::Function
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Sep 27, 2021
1 parent a9890a4 commit be8591c
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 4 deletions.
11 changes: 10 additions & 1 deletion docs/src/manual/solutions.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ MOI.get(model, MOI.ConstraintConflictStatus(), c2)
new_model, reference_map = copy_conflict(model)
```

Conflicting constraints can be collected in a list and printed
Conflicting constraints can be collected in a list and printed
as follows:

```julia
Expand Down Expand Up @@ -564,3 +564,12 @@ julia> primal_feasibility_report(model, Dict(x => 2.1); skip_missing = true)
Dict{Any, Float64} with 1 entry:
x integer => 0.1
```

You can also use the functional form, where the first argument is a function
that maps variables to their primal values:
```jldoctest feasibility
julia> optimize!(model)
julia> primal_feasibility_report(v -> value(v), model)
Dict{Any, Float64}()
```
43 changes: 40 additions & 3 deletions src/feasibility_checker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ function primal_feasibility_report(
atol::Float64 = 0.0,
skip_missing::Bool = false,
)
function point_f(x::VariableRef)
return primal_feasibility_report(
model;
atol = atol,
skip_missing = skip_missing,
) do x
fx = get(point, x, missing)
if ismissing(fx) && !skip_missing
error(
Expand All @@ -67,14 +71,47 @@ function primal_feasibility_report(
end
return fx
end
end

"""
primal_feasibility_report(
point::Function,
model::Model;
atol::Float64 = 0.0,
skip_missing::Bool = false,
)
A form of `primal_feasibility_report` where a function is passed as the first
argument instead of a dictionary as the second argument.
## Examples
```jldoctest; setup=:(using JuMP)
julia> model = Model();
julia> @variable(model, 0.5 <= x <= 1);
julia> primal_feasibility_report(model) do v
return value(v)
end
Dict{Any,Float64} with 1 entry:
x ≥ 0.5 => 0.3
```
"""
function primal_feasibility_report(
point::Function,
model::Model;
atol::Float64 = 0.0,
skip_missing::Bool = false,
)
violated_constraints = Dict{Any,Float64}()
for (F, S) in list_of_constraint_types(model)
_add_infeasible_constraints(
model,
F,
S,
violated_constraints,
point_f,
point,
atol,
)
end
Expand All @@ -88,7 +125,7 @@ function primal_feasibility_report(
_add_infeasible_nonlinear_constraints(
model,
violated_constraints,
point_f,
point,
atol,
)
end
Expand Down
33 changes: 33 additions & 0 deletions test/feasibility_checker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,21 @@ function test_primal_solution()
@test isempty(report)
end

function test_primal_solution_func()
model = Model(() -> MOIU.MockOptimizer(MOIU.Model{Float64}()))
@variable(model, x, Bin)
optimize!(model)
mock = unsafe_backend(model)
MOI.set(mock, MOI.TerminationStatus(), MOI.OPTIMAL)
MOI.set(mock, MOI.PrimalStatus(), MOI.FEASIBLE_POINT)
MOI.set(mock, MOI.VariablePrimal(), optimizer_index(x), 1.0)
report = primal_feasibility_report(model) do xi
return value(xi)
end
@test isempty(report)
return
end

function test_feasible()
model = Model()
@variable(model, x, Bin)
Expand Down Expand Up @@ -181,6 +196,24 @@ function test_scalar_affine()
@test length(report) == 4
end

function test_scalar_affine_func()
model = Model()
@variable(model, x)
@constraint(model, c1, x <= 0.5)
@constraint(model, c2, x >= 1.25)
@constraint(model, c3, x == 1.1)
@constraint(model, c4, 0 <= x <= 0.5)
report = primal_feasibility_report(model) do _
return 1.0
end
@test report[c1] 0.5
@test report[c2] 0.25
@test report[c3] 0.1
@test report[c4] 0.5
@test length(report) == 4
return
end

function test_scalar_quadratic()
model = Model()
@variable(model, x)
Expand Down

0 comments on commit be8591c

Please sign in to comment.