Skip to content

Commit dad3c7e

Browse files
authored
Improve codecov (#14)
* Improve codecov * remove unused * fix tests * more tests * format * fix test * add feasibility tests * improve numerical cov * format * more numerical * add stdout test with no print * fix redirect * add tests for numerical * more tests * add empty model test * add infeasibility tests * more tests * more tests * add intervals tests * simplify interval
1 parent c5e98cc commit dad3c7e

File tree

8 files changed

+1706
-341
lines changed

8 files changed

+1706
-341
lines changed

docs/src/numerical.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ ModelAnalyzer.Numerical.SmallObjectiveCoefficient
3131
ModelAnalyzer.Numerical.LargeObjectiveCoefficient
3232
ModelAnalyzer.Numerical.SmallObjectiveQuadraticCoefficient
3333
ModelAnalyzer.Numerical.LargeObjectiveQuadraticCoefficient
34-
ModelAnalyzer.Numerical.NonconvexQuadraticConstraint
3534
ModelAnalyzer.Numerical.SmallMatrixQuadraticCoefficient
3635
ModelAnalyzer.Numerical.LargeMatrixQuadraticCoefficient
36+
ModelAnalyzer.Numerical.NonconvexQuadraticObjective
37+
ModelAnalyzer.Numerical.NonconvexQuadraticConstraint
3738
```
3839

3940
These issues are saved in the data structure that is returned from the `ModelAnalyzer.analyze` function:

src/feasibility.jl

Lines changed: 13 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,8 @@ function _analyze_complementarity!(model, data)
815815
obj = JuMP.constraint_object(con)
816816
func = obj.func
817817
set = obj.set
818-
func_val = JuMP.value(x -> data.primal_point[x], func) - _set_value(set)
818+
func_val =
819+
JuMP.value.(x -> data.primal_point[x], func) - _set_value(set)
819820
comp_val = MOI.Utilities.set_dot(func_val, data.dual_point[con], set)
820821
if abs(comp_val) > data.atol
821822
push!(data.complementarity, ComplemetarityViolation(con, comp_val))
@@ -824,9 +825,14 @@ function _analyze_complementarity!(model, data)
824825
return
825826
end
826827

827-
function _set_value(set::MOI.AbstractScalarSet)
828-
return 0.0
829-
end
828+
# not needed because it would have stoped in dualization before
829+
# function _set_value(set::MOI.AbstractScalarSet)
830+
# return 0.0
831+
# end
832+
# function _set_value(set::MOI.Interval)
833+
# error("Interval sets are not supported.")
834+
# return (set.lower, set.upper)
835+
# end
830836

831837
function _set_value(set::MOI.AbstractVectorSet)
832838
return zeros(MOI.dimension(set))
@@ -844,11 +850,6 @@ function _set_value(set::MOI.EqualTo)
844850
return set.value
845851
end
846852

847-
function _set_value(set::MOI.Interval)
848-
error("Interval sets are not supported.")
849-
return (set.lower, set.upper)
850-
end
851-
852853
function _analyze_objectives!(
853854
model::JuMP.GenericModel{T},
854855
dual_model,
@@ -915,13 +916,9 @@ function _analyze_objectives!(
915916
end
916917

917918
###
919+
920+
# unsafe as is its checked upstream
918921
function _last_primal_solution(model::JuMP.GenericModel)
919-
if !JuMP.has_values(model)
920-
error(
921-
"No primal solution is available. You must provide a point at " *
922-
"which to check feasibility.",
923-
)
924-
end
925922
return Dict(v => JuMP.value(v) for v in JuMP.all_variables(model))
926923
end
927924

@@ -1138,137 +1135,13 @@ function _fix_ret(
11381135
return ret
11391136
end
11401137

1141-
function _add_with_resize!(vec, val, i)
1142-
if i > length(vec)
1143-
resize!(vec, i)
1144-
end
1145-
return vec[i] = val
1146-
end
1147-
1148-
"""
1149-
dual_feasibility_report(
1150-
point::Function,
1151-
model::GenericModel{T};
1152-
atol::T = zero(T),
1153-
skip_missing::Bool = false,
1154-
) where {T}
1155-
1156-
A form of `dual_feasibility_report` where a function is passed as the first
1157-
argument instead of a dictionary as the second argument.
1158-
1159-
## Example
1160-
1161-
```jldoctest
1162-
julia> model = Model();
1163-
1164-
julia> @variable(model, 0.5 <= x <= 1, start = 1.3); TODO
1165-
1166-
julia> dual_feasibility_report(model) do v
1167-
return dual_start_value(v)
1168-
end
1169-
Dict{Any, Float64} with 1 entry:
1170-
x ≤ 1 => 0.3 TODO
1171-
```
1172-
"""
1173-
# probablye remove this method
1174-
function dual_feasibility_report(
1175-
point::Function,
1176-
model::JuMP.GenericModel{T};
1177-
atol::T = zero(T),
1178-
skip_missing::Bool = false,
1179-
) where {T}
1180-
if JuMP.num_nonlinear_constraints(model) > 0
1181-
error(
1182-
"Nonlinear constraints are not supported. " *
1183-
"Use `dual_feasibility_report` instead.",
1184-
)
1185-
end
1186-
if !skip_missing
1187-
constraint_list = JuMP.all_constraints(
1188-
model;
1189-
include_variable_in_set_constraints = false,
1190-
)
1191-
for c in constraint_list
1192-
if !haskey(point, c)
1193-
error(
1194-
"point does not contain a dual for constraint $c. Provide " *
1195-
"a dual, or pass `skip_missing = true`.",
1196-
)
1197-
end
1198-
end
1199-
end
1200-
dual_model = _dualize2(model)
1201-
map = dual_model.ext[:dualization_primal_dual_map].primal_con_dual_var
1202-
1203-
dual_var_primal_con = _reverse_primal_con_dual_var_map(map)
1204-
1205-
function dual_point(jump_dual_var::JuMP.GenericVariableRef{T})
1206-
# v is a variable in the dual jump model
1207-
# we need the associated cosntraint in the primal jump model
1208-
moi_dual_var = JuMP.index(jump_dual_var)
1209-
moi_primal_con, i = dual_var_primal_con[moi_dual_var]
1210-
jump_primal_con = JuMP.constraint_ref_with_index(model, moi_primal_con)
1211-
pre_point = point(jump_primal_con)
1212-
if ismissing(pre_point)
1213-
if !skip_missing
1214-
error(
1215-
"point does not contain a dual for constraint $jump_primal_con. Provide " *
1216-
"a dual, or pass `skip_missing = true`.",
1217-
)
1218-
else
1219-
return missing
1220-
end
1221-
end
1222-
return point(jump_primal_con)[i]
1223-
end
1224-
1225-
dual_con_to_violation = JuMP.primal_feasibility_report(
1226-
dual_point,
1227-
dual_model;
1228-
atol = atol,
1229-
skip_missing = skip_missing,
1230-
)
1231-
1232-
# some dual model constraints are associated with primal model variables (primal_con_dual_var)
1233-
# if variable is free
1234-
primal_var_dual_con =
1235-
dual_model.ext[:dualization_primal_dual_map].primal_var_dual_con
1236-
# if variable is bounded
1237-
primal_convar_dual_con =
1238-
dual_model.ext[:dualization_primal_dual_map].constrained_var_dual
1239-
# other dual model constraints (bounds) are associated with primal model constraints (non-bounds)
1240-
primal_con_dual_con =
1241-
dual_model.ext[:dualization_primal_dual_map].primal_con_dual_con
1242-
1243-
dual_con_primal_all = _build_dual_con_primal_all(
1244-
primal_var_dual_con,
1245-
primal_convar_dual_con,
1246-
primal_con_dual_con,
1247-
)
1248-
1249-
ret = _fix_ret(dual_con_to_violation, model, dual_con_primal_all)
1250-
1251-
return ret
1252-
end
1253-
1254-
function _reverse_primal_con_dual_var_map(primal_con_dual_var)
1255-
dual_var_primal_con =
1256-
Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}()
1257-
for (moi_con, vec_vars) in primal_con_dual_var
1258-
for (i, moi_var) in enumerate(vec_vars)
1259-
dual_var_primal_con[moi_var] = (moi_con, i)
1260-
end
1261-
end
1262-
return dual_var_primal_con
1263-
end
1264-
12651138
function _dualize2(
12661139
model::JuMP.GenericModel,
12671140
optimizer_constructor = nothing;
12681141
kwargs...,
12691142
)
12701143
mode = JuMP.mode(model)
1271-
if mode != JuMP.AUTOMATIC
1144+
if mode == JuMP.MANUAL
12721145
error("Dualization does not support solvers in $(mode) mode")
12731146
end
12741147
dual_model = JuMP.Model()

src/infeasibility.jl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,9 @@ function ModelAnalyzer.analyze(
261261
end
262262
iis = iis_elastic_filter(model, optimizer)
263263
# for now, only one iis is computed
264-
push!(out.iis, IrreducibleInfeasibleSubset(iis))
264+
if iis !== nothing
265+
push!(out.iis, IrreducibleInfeasibleSubset(iis))
266+
end
265267

266268
return out
267269
end
@@ -278,7 +280,7 @@ function iis_elastic_filter(original_model::JuMP.GenericModel, optimizer)
278280
println(
279281
"iis resolver cannot continue because model is found to be $(status) by the solver",
280282
)
281-
return
283+
return nothing
282284
end
283285

284286
model, reference_map = JuMP.copy_model(original_model)
@@ -315,19 +317,20 @@ function iis_elastic_filter(original_model::JuMP.GenericModel, optimizer)
315317
var = collect(keys(func.terms))
316318
coef1 = func.terms[var[1]]
317319
coef2 = func.terms[var[2]]
318-
if JuMP.value(var1) > tolerance && JuMP.value(var2) > tolerance
320+
if JuMP.value(var[1]) > tolerance &&
321+
JuMP.value(var[2]) > tolerance
319322
error("IIS failed due numerical instability")
320323
elseif JuMP.value(var[1]) > tolerance
321324
has_lower = JuMP.has_lower_bound(var[1])
322325
JuMP.fix(var[1], 0.0; force = true)
323-
# or delete(model, var1)
326+
# or delete(model, var[1])
324327
delete!(constraint_to_affine, con)
325328
constraint_to_affine[con] = coef2 * var[2]
326329
push!(de_elastisized, (con, var[1], has_lower))
327330
elseif JuMP.value(var[2]) > tolerance
328331
has_lower = JuMP.has_lower_bound(var[2])
329332
JuMP.fix(var[2], 0.0; force = true)
330-
# or delete(model, var2)
333+
# or delete(model, var[2])
331334
delete!(constraint_to_affine, con)
332335
constraint_to_affine[con] = coef1 * var[1]
333336
push!(de_elastisized, (con, var[2], has_lower))
@@ -570,7 +573,7 @@ function ModelAnalyzer._summarize(
570573
end
571574

572575
function ModelAnalyzer._summarize(io::IO, issue::IrreducibleInfeasibleSubset)
573-
return print(io, "IIS: ", join(map(issue.constraint, _name), ", "))
576+
return print(io, "IIS: ", join(map(_name, issue.constraint), ", "))
574577
end
575578

576579
function ModelAnalyzer._verbose_summarize(
@@ -629,7 +632,7 @@ function ModelAnalyzer._verbose_summarize(
629632
return print(
630633
io,
631634
"Irreducible Infeasible Subset: ",
632-
join(map(issue.constraint, _name), ", "),
635+
join(map(_name, issue.constraint), ", "),
633636
)
634637
end
635638

src/intervals.jl

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,28 +66,31 @@ function Base.iszero(a::Interval)
6666
return iszero(a.hi) && iszero(a.lo)
6767
end
6868

69-
Base.:+(a::Interval) = a
70-
Base.:-(a::Interval) = Interval(-a.hi, -a.lo)
69+
# this code is only used for interval += scalar_coef * interval
70+
# so only bivariate + and * are needes
7171

72-
function Base.:+(a::Interval{T}, b::T) where {T<:Real}
73-
return Interval(a.lo + b, a.hi + b)
74-
end
75-
Base.:+(b::T, a::Interval{T}) where {T<:Real} = a + b
72+
# Base.:+(a::Interval) = a
73+
# Base.:-(a::Interval) = Interval(-a.hi, -a.lo)
7674

77-
function Base.:-(a::Interval{T}, b::T) where {T<:Real}
78-
return Interval(a.lo - b, a.hi - b)
79-
end
80-
function Base.:-(b::T, a::Interval{T}) where {T<:Real}
81-
return Interval(b - a.hi, b - a.lo)
82-
end
75+
# function Base.:+(a::Interval{T}, b::T) where {T<:Real}
76+
# return Interval(a.lo + b, a.hi + b)
77+
# end
78+
# Base.:+(b::T, a::Interval{T}) where {T<:Real} = a + b
79+
80+
# function Base.:-(a::Interval{T}, b::T) where {T<:Real}
81+
# return Interval(a.lo - b, a.hi - b)
82+
# end
83+
# function Base.:-(b::T, a::Interval{T}) where {T<:Real}
84+
# return Interval(b - a.hi, b - a.lo)
85+
# end
8386

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

88-
function Base.:-(a::Interval{T}, b::Interval{T}) where {T<:Real}
89-
return Interval(a.lo - b.hi, a.hi - b.lo)
90-
end
91+
# function Base.:-(a::Interval{T}, b::Interval{T}) where {T<:Real}
92+
# return Interval(a.lo - b.hi, a.hi - b.lo)
93+
# end
9194

9295
## Multiplication
9396
function Base.:*(x::T, a::Interval{T}) where {T<:Real}
@@ -99,6 +102,6 @@ function Base.:*(x::T, a::Interval{T}) where {T<:Real}
99102
end
100103
end
101104

102-
Base.:*(a::Interval{T}, x::T) where {T<:Real} = x * a
105+
# Base.:*(a::Interval{T}, x::T) where {T<:Real} = x * a
103106

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

0 commit comments

Comments
 (0)