Skip to content

Commit

Permalink
Fix solution_summary when there are duplicate names
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Oct 10, 2024
1 parent 0a99d50 commit ecb8ffc
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 14 deletions.
50 changes: 36 additions & 14 deletions src/solution_summary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct _SolutionSummary{T}
objective_bound::Union{Missing,T,Vector{T}}
relative_gap::Union{Missing,T}
dual_objective_value::Union{Missing,T}
primal_solution::Union{Missing,Dict{String,T}}
primal_solution::Union{Missing,Dict{String,Union{Missing,T}}}
dual_solution::Union{Missing,Dict{String,Any}}
# Work counters
solve_time::Union{Missing,Float64}
Expand Down Expand Up @@ -182,21 +182,35 @@ function _show_candidate_solution_summary(io::IO, summary::_SolutionSummary)
if summary.verbose && summary.has_values
println(io, " Primal solution :")
for variable_name in sort(collect(keys(summary.primal_solution)))
_print_if_not_missing(
io,
" $(variable_name) : ",
summary.primal_solution[variable_name],
)
if summary.primal_solution[variable_name] === missing
println(
io,
" $variable_name : multiple variables with the same name",
)
else
_print_if_not_missing(
io,
" $(variable_name) : ",
summary.primal_solution[variable_name],
)
end
end
end
if summary.verbose && summary.has_duals
println(io, " Dual solution :")
for constraint_name in sort(collect(keys(summary.dual_solution)))
_print_if_not_missing(
io,
" $(constraint_name) : ",
summary.dual_solution[constraint_name],
)
if summary.dual_solution[constraint_name] === missing
println(
io,
" $constraint_name : multiple constraints with the same name",
)
else
_print_if_not_missing(
io,
" $(constraint_name) : ",
summary.dual_solution[constraint_name],
)
end
end
end
return
Expand All @@ -221,10 +235,14 @@ function _show_work_counters_summary(io::IO, summary::_SolutionSummary)
end

function _get_solution_dict(model, result)
dict = Dict{String,value_type(typeof(model))}()
dict = Dict{String,Union{Missing,value_type(typeof(model))}}()
for x in all_variables(model)
variable_name = name(x)
if !isempty(variable_name)
if isempty(variable_name)
continue

Check warning on line 242 in src/solution_summary.jl

View check run for this annotation

Codecov / codecov/patch

src/solution_summary.jl#L242

Added line #L242 was not covered by tests
elseif haskey(dict, variable_name)
dict[variable_name] = missing
else
dict[variable_name] = value(x; result = result)
end
end
Expand All @@ -236,7 +254,11 @@ function _get_constraint_dict(model, result)
for (F, S) in list_of_constraint_types(model)
for constraint in all_constraints(model, F, S)
constraint_name = name(constraint)
if !isempty(constraint_name)
if isempty(constraint_name)
continue
elseif haskey(dict, constraint_name)
dict[constraint_name] = missing
else
dict[constraint_name] = dual(constraint; result = result)
end
end
Expand Down
47 changes: 47 additions & 0 deletions test/test_solution_summary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,51 @@ function test_solution_summary_vector_dual()
return
end

function test_solution_summary_same_names()
model = Model() do
return MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}())
end
@variable(model, x[1:2])
@variable(model, y)
set_name.(x, "x")
@constraint(model, c, x .>= 0)
@constraint(model, d, 2x[1] <= 1)
optimize!(model)
mock = unsafe_backend(model)
MOI.set(mock, MOI.TerminationStatus(), MOI.OPTIMAL)
MOI.set(mock, MOI.RawStatusString(), "solver specific string")
MOI.set(mock, MOI.ResultCount(), 1)
MOI.set(mock, MOI.PrimalStatus(), MOI.FEASIBLE_POINT)
MOI.set(mock, MOI.DualStatus(), MOI.FEASIBLE_POINT)
MOI.set(mock, MOI.VariablePrimal(), optimizer_index.(x), [1.0, 2.0])
MOI.set(mock, MOI.VariablePrimal(), optimizer_index(y), 3.0)
MOI.set(mock, MOI.ConstraintDual(), optimizer_index.(c), [3.0, 4.0])
MOI.set(mock, MOI.ConstraintDual(), optimizer_index(d), 5.0)
ret = """
* Solver : Mock
* Status
Result count : 1
Termination status : OPTIMAL
Message from the solver:
"solver specific string"
* Candidate solution (result #1)
Primal status : FEASIBLE_POINT
Dual status : FEASIBLE_POINT
Objective value : 0.00000e+00
Dual objective value : 5.00000e+00
Primal solution :
x : multiple variables with the same name
y : 3.00000e+00
Dual solution :
c : multiple constraints with the same name
d : 5.00000e+00
* Work counters
"""
@test sprint(show, solution_summary(model; verbose = true)) == ret
return
end

end # module

0 comments on commit ecb8ffc

Please sign in to comment.