Skip to content

Commit

Permalink
[breaking] Fix setting constant objective function (#581)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Jan 22, 2024
1 parent dea1d81 commit 946d18d
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/Convex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,13 @@ for (root, _, files) in walkdir(joinpath(@__DIR__, "constraints"))
end
end

include("problems.jl")
include("SparseTape.jl")
include("VectorAffineFunctionAsMatrix.jl")
include("ComplexTape.jl")
include("operate.jl")
include("complex_operate.jl")
include("real_operate.jl")
include("problems.jl")
include("solution.jl")
include("MOI_wrapper.jl")

Expand Down
6 changes: 3 additions & 3 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,12 @@ function MOI.supports(
end

function MOI.set(
model::Optimizer,
model::Optimizer{T},
::MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction},
func::MOI.ScalarNonlinearFunction,
)
) where {T}
cfp = conic_form!(model.context, _expr(model, func))
obj = scalar_fn(cfp)
obj = _to_scalar_moi(T, cfp)
MOI.set(model, MOI.ObjectiveFunction{typeof(obj)}(), obj)
return
end
Expand Down
2 changes: 1 addition & 1 deletion src/problem_depot/problems/lp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ end
end
handle_problem!(p)
if test
@test p.optval === nothing
@test p.optval === 1.0
@test evaluate(sum(neg(x))) 6 atol = atol rtol = rtol
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/problem_depot/problems/sdp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1499,7 +1499,7 @@ function sdp_quantum_relative_entropy_impl(
atol rtol = rtol
elseif mode == 5
# Satisfiability problem
@test p.optval === nothing
@test p.optval 0 atol = atol rtol = rtol
end
end
end
Expand Down
40 changes: 31 additions & 9 deletions src/problems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,20 @@ function new_conic_form!(context::Context, p::Problem)
return conic_form!(context, p.objective)
end

function _to_scalar_moi(::Type{T}, x) where {T}
return _to_scalar_moi(T, only(MOI.Utilities.scalarize(x)))
end

function _to_scalar_moi(::Type{T}, x::SparseTape) where {T}
return _to_scalar_moi(T, to_vaf(x))
end

function _to_scalar_moi(::Type{T}, x::Number) where {T<:Real}
return MOI.ScalarAffineFunction{T}(MOI.ScalarAffineTerm{T}[], x)
end

_to_scalar_moi(::Type{T}, f::MOI.AbstractScalarFunction) where {T} = f

function Context(p::Problem{T}, optimizer_factory) where {T}
context = Context{T}(optimizer_factory)
cfp = conic_form!(context, p)
Expand All @@ -153,7 +167,7 @@ function Context(p::Problem{T}, optimizer_factory) where {T}
if p.head == :satisfy
MOI.set(context.model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
else
obj = scalar_fn(cfp)
obj = _to_scalar_moi(T, cfp)
MOI.set(context.model, MOI.ObjectiveFunction{typeof(obj)}(), obj)
MOI.set(
context.model,
Expand Down Expand Up @@ -189,16 +203,20 @@ function minimize(
return Problem{numeric_type}(:minimize, objective, constraints)
end

function minimize(::Value, constraints::Constraint...; numeric_type = Float64)
return satisfy(collect(constraints); numeric_type = numeric_type)
function minimize(
objective::Value,
constraints::Constraint...;
numeric_type = Float64,
)
return minimize(objective, collect(constraints); numeric_type)
end

function minimize(
::Value,
objective::Value,
constraints::Array{<:Constraint} = Constraint[];
numeric_type = Float64,
)
return satisfy(constraints; numeric_type = numeric_type)
return minimize(constant(objective), constraints; numeric_type)
end

# Allow users to simply type maximize
Expand All @@ -218,16 +236,20 @@ function maximize(
return Problem{numeric_type}(:maximize, objective, constraints)
end

function maximize(::Value, constraints::Constraint...; numeric_type = Float64)
return satisfy(collect(constraints); numeric_type = numeric_type)
function maximize(
objective::Value,
constraints::Constraint...;
numeric_type = Float64,
)
return maximize(objective, collect(constraints); numeric_type)
end

function maximize(
::Value,
objective::Value,
constraints::Array{<:Constraint} = Constraint[];
numeric_type = Float64,
)
return satisfy(constraints; numeric_type = numeric_type)
return maximize(constant(objective), constraints; numeric_type)
end

# Allow users to simply type satisfy (if there is no objective)
Expand Down
5 changes: 0 additions & 5 deletions src/solution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ function add_variables!(model, var::AbstractVariable)
end
end

scalar_fn(x::Number) = x # for `satisfy` problems? Not sure...
scalar_fn(x) = only(MOI.Utilities.scalarize(x))
scalar_fn(x::SparseTape) = scalar_fn(to_vaf(x))
scalar_fn(v::MOI.AbstractScalarFunction) = v

"""
solve!(problem::Problem, optimizer_factory;
silent_solver = false,
Expand Down
65 changes: 54 additions & 11 deletions test/test_utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,12 @@ function test_complex_objective_function_errors()
return
end

function test_constant_objective()
function test_satisfy_constant_objective()
x = Variable()
for p in [
satisfy(x == 0, x == 1),
satisfy(Constraint[]),
minimize(0, x == 0),
minimize(0, Constraint[]),
maximize(0, x == 0),
maximize(0, Constraint[]),
]
@test isnothing(p.objective)
end
p = satisfy(x == 0, x == 1)
@test isnothing(p.objective)
p = satisfy(Constraint[])
@test isnothing(p.objective)
return
end

Expand Down Expand Up @@ -1142,6 +1136,55 @@ function test_tree_interface()
return
end

function test_scalar_fn_constant_objective()
x = Variable()
p = minimize(2.1, [x >= 1])
solve!(p, SCS.Optimizer; silent_solver = true)
@test isapprox(p.optval, 2.1; atol = 1e-5)
p = minimize(2.2, x >= 1)
solve!(p, SCS.Optimizer; silent_solver = true)
@test isapprox(p.optval, 2.2; atol = 1e-5)
p = maximize(2.3, [x >= 1])
solve!(p, SCS.Optimizer; silent_solver = true)
@test isapprox(p.optval, 2.3; atol = 1e-5)
p = maximize(2.4, x >= 1)
solve!(p, SCS.Optimizer; silent_solver = true)
@test isapprox(p.optval, 2.4; atol = 1e-5)
return
end

function test_scalar_fn_objective_number()
x = Variable()
p = minimize(constant(2), [x >= 1])
solve!(p, SCS.Optimizer)
@test isapprox(p.optval, 2.0; atol = 1e-5)
return
end

function test_scalar_fn_objective_variable()
x = Variable()
p = minimize(x, [x >= 1])
solve!(p, SCS.Optimizer)
@test isapprox(p.optval, 1.0; atol = 1e-5)
return
end

function test_scalar_fn_objective_affine()
x = Variable()
p = minimize(x + 1, [x >= 1])
solve!(p, SCS.Optimizer)
@test isapprox(p.optval, 2.0; atol = 1e-5)
return
end

function test_scalar_fn_objective_square()
x = Variable()
p = minimize(square(x - 2), [x >= 1])
solve!(p, SCS.Optimizer)
@test isapprox(p.optval, 0.0; atol = 1e-3)
return
end

end # TestUtilities

TestUtilities.runtests()

0 comments on commit 946d18d

Please sign in to comment.