diff --git a/README.md b/README.md index b14f13be..51a8caca 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ optimize!(model) # differentiate w.r.t. p direction_p = 3.0 -MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), direction_p) +MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), Parameter(direction_p)) DiffOpt.forward_differentiate!(model) @show MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) == direction_p * 3 / pc_val @@ -82,7 +82,7 @@ optimize!(model) DiffOpt.empty_input_sensitivities!(model) # differentiate w.r.t. pc direction_pc = 10.0 -MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), direction_pc) +MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), Parameter(direction_pc)) DiffOpt.forward_differentiate!(model) @show abs(MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) - -direction_pc * 3 * p_val / pc_val^2) < 1e-5 @@ -93,8 +93,8 @@ DiffOpt.empty_input_sensitivities!(model) direction_x = 10.0 MOI.set(model, DiffOpt.ReverseVariablePrimal(), x, direction_x) DiffOpt.reverse_differentiate!(model) -@show MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) == direction_x * 3 / pc_val -@show abs(MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(pc)) - +@show MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) == MOI.Parameter(direction_x * 3 / pc_val) +@show abs(MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(pc)).value - -direction_x * 3 * p_val / pc_val^2) < 1e-5 ``` diff --git a/src/DiffOpt.jl b/src/DiffOpt.jl index f09d2608..71dd1a00 100644 --- a/src/DiffOpt.jl +++ b/src/DiffOpt.jl @@ -19,8 +19,8 @@ include("utils.jl") include("product_of_sets.jl") include("diff_opt.jl") include("moi_wrapper.jl") -include("jump_moi_overloads.jl") include("parameters.jl") +include("jump_moi_overloads.jl") include("copy_dual.jl") include("bridges.jl") diff --git a/src/jump_moi_overloads.jl b/src/jump_moi_overloads.jl index 6f8c29ae..32ab6dad 100644 --- a/src/jump_moi_overloads.jl +++ b/src/jump_moi_overloads.jl @@ -80,6 +80,37 @@ function MOI.get( return _moi_get_result(JuMP.backend(model), attr, JuMP.index(var_ref)) end +# extras to handle model_dirty + +function MOI.get( + model::JuMP.Model, + attr::ReverseConstraintSet, + var_ref::JuMP.ConstraintRef, +) + JuMP.check_belongs_to_model(var_ref, model) + return _moi_get_result(JuMP.backend(model), attr, JuMP.index(var_ref)) +end + +function MOI.set( + model::JuMP.Model, + attr::ForwardConstraintSet, + con_ref::JuMP.ConstraintRef, + set::MOI.AbstractScalarSet, +) + JuMP.check_belongs_to_model(con_ref, model) + return MOI.set(JuMP.backend(model), attr, JuMP.index(con_ref), set) +end + +function MOI.set( + model::JuMP.Model, + attr::ForwardConstraintSet, + con_ref::JuMP.ConstraintRef, + set::JuMP.AbstractScalarSet, +) + JuMP.check_belongs_to_model(con_ref, model) + return MOI.set(JuMP.backend(model), attr, JuMP.index(con_ref), JuMP.moi_set(set)) +end + """ abstract type AbstractLazyScalarFunction <: MOI.AbstractScalarFunction end diff --git a/src/parameters.jl b/src/parameters.jl index 5deebb03..65798949 100644 --- a/src/parameters.jl +++ b/src/parameters.jl @@ -308,14 +308,14 @@ function MOI.set( model::POI.Optimizer, ::ForwardConstraintSet, ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Parameter{T}}, - value::Number, + set::MOI.Parameter, ) where {T} variable = MOI.VariableIndex(ci.value) if _is_variable(model, variable) error("Trying to set a forward parameter sensitivity for a variable") end sensitivity_data = _get_sensitivity_data(model) - sensitivity_data.parameter_input_forward[variable] = value + sensitivity_data.parameter_input_forward[variable] = set.value return end @@ -573,16 +573,7 @@ function MOI.get( error("Trying to get a backward parameter sensitivity for a variable") end sensitivity_data = _get_sensitivity_data(model) - return get(sensitivity_data.parameter_output_backward, variable, 0.0) -end - -# extras to handle model_dirty - -function MOI.get( - model::JuMP.Model, - attr::ReverseConstraintSet, - var_ref::JuMP.ConstraintRef, -) - JuMP.check_belongs_to_model(var_ref, model) - return _moi_get_result(JuMP.backend(model), attr, JuMP.index(var_ref)) + return MOI.Parameter{T}( + get(sensitivity_data.parameter_output_backward, variable, 0.0), + ) end diff --git a/test/parameters.jl b/test/parameters.jl index 9c901d57..8d879430 100644 --- a/test/parameters.jl +++ b/test/parameters.jl @@ -14,6 +14,9 @@ import MathOptInterface as MOI import HiGHS import SCS +Base.isapprox(x::MOI.Parameter, y::MOI.Parameter; atol = 1e-10) = + isapprox(x.value, y.value, atol = atol) + function runtests() for name in names(@__MODULE__; all = true) if startswith("$name", "test_") @@ -42,11 +45,21 @@ function test_diff_rhs() # the function is # x(p) = 3p, hence x'(p) = 3 # differentiate w.r.t. p - MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), 1) + MOI.set( + model, + DiffOpt.ForwardConstraintSet(), + ParameterRef(p), + Parameter(1.0), + ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ 3 # again with different "direction" - MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), 2) + MOI.set( + model, + DiffOpt.ForwardConstraintSet(), + ParameterRef(p), + Parameter(2.0), + ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ 6 # @@ -54,11 +67,21 @@ function test_diff_rhs() optimize!(model) @test value(x) ≈ 6 # differentiate w.r.t. p - MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), 1) + MOI.set( + model, + DiffOpt.ForwardConstraintSet(), + ParameterRef(p), + Parameter(1.0), + ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ 3 # again with different "direction" - MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), 2) + MOI.set( + model, + DiffOpt.ForwardConstraintSet(), + ParameterRef(p), + Parameter(2.0), + ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ 6 # @@ -66,11 +89,13 @@ function test_diff_rhs() # MOI.set(model, DiffOpt.ReverseVariablePrimal(), x, 1) DiffOpt.reverse_differentiate!(model) - @test MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) ≈ 3 + @test MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) ≈ + MOI.Parameter(3.0) # again with different "direction" MOI.set(model, DiffOpt.ReverseVariablePrimal(), x, 2) DiffOpt.reverse_differentiate!(model) - @test MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) ≈ 6 + @test MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) ≈ + MOI.Parameter(6.0) return end @@ -99,12 +124,12 @@ function test_diff_vector_rhs() set_parameter_value(p, p_val) optimize!(model) @test isapprox(value(x), 3 * p_val, atol = 1e-3) - for direction in 0:3 + for direction in 0.0:3.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction, + Parameter(direction), ) DiffOpt.forward_differentiate!(model) @test isapprox( @@ -117,7 +142,7 @@ function test_diff_vector_rhs() DiffOpt.reverse_differentiate!(model) @test isapprox( MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)), - direction * 3, + MOI.Parameter(direction * 3), atol = 1e-3, ) end @@ -145,12 +170,12 @@ function test_affine_changes() # the function is # x(p, pc) = 3p / pc, hence dx/dp = 3 / pc, dx/dpc = -3p / pc^2 # differentiate w.r.t. p - for direction_p in 1:1#2 + for direction_p in 1.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -162,12 +187,12 @@ function test_affine_changes() optimize!(model) @test value(x) ≈ 3 * p_val / pc_val # differentiate w.r.t. p - for direction_p in 1:2 + for direction_p in 1.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -176,13 +201,18 @@ function test_affine_changes() # differentiate w.r.t. pc # stop differentiating with respect to p direction_p = 0.0 - MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), direction_p) - for direction_pc in 1:2 + MOI.set( + model, + DiffOpt.ForwardConstraintSet(), + ParameterRef(p), + Parameter(direction_p), + ) + for direction_pc in 1.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), - direction_pc, + Parameter(direction_pc), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -193,12 +223,12 @@ function test_affine_changes() set_parameter_value(pc, pc_val) optimize!(model) @test value(x) ≈ 3 * p_val / pc_val - for direction_pc in 1:1#2 + for direction_pc in 1.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), - direction_pc, + Parameter(direction_pc), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -210,13 +240,13 @@ function test_affine_changes() model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), - direction_pc, + Parameter(direction_pc), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -247,18 +277,18 @@ function test_affine_changes_compact() set_parameter_value(pc, pc_val) optimize!(model) @test value(x) ≈ 3 * p_val / pc_val - for direction_pc in 0:2, direction_p in 0:2 + for direction_pc in 0.0:2.0, direction_p in 0.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), - direction_pc, + Parameter(direction_pc), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -272,12 +302,12 @@ function test_affine_changes_compact() model, DiffOpt.ReverseConstraintSet(), ParameterRef(p), - ) ≈ direction_x * 3 / pc_val + ) ≈ MOI.Parameter(direction_x * 3 / pc_val) @test MOI.get( model, DiffOpt.ReverseConstraintSet(), ParameterRef(pc), - ) ≈ -direction_x * 3 * p_val / pc_val^2 + ) ≈ MOI.Parameter(-direction_x * 3 * p_val / pc_val^2) end end return @@ -324,36 +354,41 @@ function test_quadratic_rhs_changes() optimize!(model) @test value(x) ≈ (1 + 3 * p_val * q_val + 5 * r_val^2 + 7 * s_val) / (11 * t_val) - for dir_p in 0:2, dir_q in 0:2, dir_r in 0:2, dir_s in 0:2, dir_t in 0:2 + for dir_p in 0.0:2.0, + dir_q in 0.0:2.0, + dir_r in 0.0:2.0, + dir_s in 0.0:2.0, + dir_t in 0.0:2.0 + MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - dir_p, + Parameter(dir_p), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(q), - dir_q, + Parameter(dir_q), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(r), - dir_r, + Parameter(dir_r), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(s), - dir_s, + Parameter(dir_s), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(t), - dir_t, + Parameter(dir_t), ) DiffOpt.forward_differentiate!(model) @test isapprox( @@ -374,29 +409,31 @@ function test_quadratic_rhs_changes() DiffOpt.reverse_differentiate!(model) @test isapprox( MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)), - dir_x * 3 * q_val / (11 * t_val), + MOI.Parameter(dir_x * 3 * q_val / (11 * t_val)), atol = 1e-10, ) @test isapprox( MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(q)), - dir_x * 3 * p_val / (11 * t_val), + MOI.Parameter(dir_x * 3 * p_val / (11 * t_val)), atol = 1e-10, ) @test isapprox( MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(r)), - dir_x * 10 * r_val / (11 * t_val), + MOI.Parameter(dir_x * 10 * r_val / (11 * t_val)), atol = 1e-10, ) @test isapprox( MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(s)), - dir_x * 7 / (11 * t_val), + MOI.Parameter(dir_x * 7 / (11 * t_val)), atol = 1e-10, ) @test isapprox( MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(t)), - dir_x * ( - -(1 + 3 * p_val * q_val + 5 * r_val^2 + 7 * s_val) / - (11 * t_val^2) + MOI.Parameter( + dir_x * ( + -(1 + 3 * p_val * q_val + 5 * r_val^2 + 7 * s_val) / + (11 * t_val^2) + ), ), atol = 1e-10, ) @@ -427,18 +464,18 @@ function test_affine_changes_compact_max() set_parameter_value(pc, pc_val) optimize!(model) @test value(x) ≈ 3 * p_val / pc_val - for direction_pc in 0:2, direction_p in 0:2 + for direction_pc in 0.0:2.0, direction_p in 0.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(pc), - direction_pc, + Parameter(direction_pc), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -467,12 +504,12 @@ function test_diff_affine_objective() set_parameter_value(p, p_val) optimize!(model) @test value(x) ≈ 3 - for direction_p in 0:2 + for direction_p in 0.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ 0.0 @@ -483,7 +520,7 @@ function test_diff_affine_objective() model, DiffOpt.ReverseConstraintSet(), ParameterRef(p), - ) ≈ 0.0 + ) ≈ MOI.Parameter(0.0) end end return @@ -507,12 +544,12 @@ function test_diff_quadratic_objective() set_parameter_value(p, p_val) optimize!(model) @test value(x) ≈ 3 - for direction_p in 0:2 + for direction_p in 0.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ 0.0 @@ -523,7 +560,7 @@ function test_diff_quadratic_objective() model, DiffOpt.ReverseConstraintSet(), ParameterRef(p), - ) ≈ 0.0 + ) ≈ MOI.Parameter(0.0) end end return @@ -548,12 +585,12 @@ function test_quadratic_objective_qp() set_parameter_value(p, p_val) optimize!(model) @test value(x) ≈ -3p_val / 2 atol = 1e-4 - for direction_p in 0:2 + for direction_p in 0.0:2.0 MOI.set( model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), - direction_p, + Parameter(direction_p), ) DiffOpt.forward_differentiate!(model) @test MOI.get(model, DiffOpt.ForwardVariablePrimal(), x) ≈ @@ -565,7 +602,7 @@ function test_quadratic_objective_qp() model, DiffOpt.ReverseConstraintSet(), ParameterRef(p), - ) ≈ direction_p * (-3 / 2) atol = 1e-4 + ) ≈ MOI.Parameter(direction_p * (-3 / 2)) atol = 1e-4 end end return @@ -590,7 +627,7 @@ function test_diff_errors() model, DiffOpt.ForwardConstraintSet(), ParameterRef(x), - 1, + Parameter(1.0), ) @test_throws ErrorException MOI.set( model,