diff --git a/docs/src/usage.md b/docs/src/usage.md index a851f835e..e9f0d5254 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -163,7 +163,7 @@ MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p), Parameter(3.0)) MOI.set(model, DiffOpt.ForwardConstraintSet(), ParameterRef(p_c), Parameter(3.0)) DiffOpt.forward_differentiate!(model) -MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) +MOI.get(model, DiffOpt.ForwardObjectiveValue()) ``` In reverse mode, we can calculate the parameter perturbation with respect to the objective perturbation: @@ -174,7 +174,7 @@ DiffOpt.empty_input_sensitivities!(model) MOI.set( model, - DiffOpt.ReverseObjectiveSensitivity(), + DiffOpt.ReverseObjectiveValue(), 0.1, ) @@ -183,4 +183,4 @@ DiffOpt.reverse_differentiate!(model) MOI.get(model, DiffOpt.ReverseConstraintSet(), ParameterRef(p)) ``` -It is important to note that the (reverse) parameter perturbation given an objective perturbation is somewhat equivalent to the perturbation with respect to solution (since one can be calculated from the other). Therefore, one cannot set both the objective sensitivity (`DiffOpt.ReverseObjectiveSensitivity`) and the solution sensitivity (e.g. `DiffOpt.ReverseVariablePrimal`) at the same time - the code will throw an error if you try to do so. +It is important to note that the (reverse) parameter perturbation given an objective perturbation is somewhat equivalent to the perturbation with respect to solution (since one can be calculated from the other). Therefore, one cannot set both the objective sensitivity (`DiffOpt.ReverseObjectiveValue`) and the solution sensitivity (e.g. `DiffOpt.ReverseVariablePrimal`) at the same time - the code will throw an error if you try to do so. diff --git a/src/ConicProgram/ConicProgram.jl b/src/ConicProgram/ConicProgram.jl index ee5492b07..154ffdc8d 100644 --- a/src/ConicProgram/ConicProgram.jl +++ b/src/ConicProgram/ConicProgram.jl @@ -467,10 +467,8 @@ end Method not supported for `DiffOpt.ConicProgram.Model` directly. However, a fallback is provided in `DiffOpt`. """ -function MOI.get(::Model, ::DiffOpt.ForwardObjectiveSensitivity) - return throw( - MOI.UnsupportedAttribute(DiffOpt.ForwardObjectiveSensitivity()), - ) +function MOI.get(::Model, ::DiffOpt.ForwardObjectiveValue) + return throw(MOI.UnsupportedAttribute(DiffOpt.ForwardObjectiveValue())) end """ diff --git a/src/NonLinearProgram/NonLinearProgram.jl b/src/NonLinearProgram/NonLinearProgram.jl index 9d813b8b1..0de08f301 100644 --- a/src/NonLinearProgram/NonLinearProgram.jl +++ b/src/NonLinearProgram/NonLinearProgram.jl @@ -646,7 +646,7 @@ function MOI.get( return MOI.Parameter{T}(model.back_grad_cache.Δp[ci]) end -function MOI.get(model::Model, ::DiffOpt.ForwardObjectiveSensitivity) +function MOI.get(model::Model, ::DiffOpt.ForwardObjectiveValue) return model.forw_grad_cache.objective_sensitivity_p end diff --git a/src/QuadraticProgram/QuadraticProgram.jl b/src/QuadraticProgram/QuadraticProgram.jl index 8273308e5..1c5464604 100644 --- a/src/QuadraticProgram/QuadraticProgram.jl +++ b/src/QuadraticProgram/QuadraticProgram.jl @@ -509,10 +509,8 @@ end Method not supported for `DiffOpt.QuadraticProgram.Model` directly. However, a fallback is provided in `DiffOpt`. """ -function MOI.get(::Model, ::DiffOpt.ForwardObjectiveSensitivity) - return throw( - MOI.UnsupportedAttribute(DiffOpt.ForwardObjectiveSensitivity()), - ) +function MOI.get(::Model, ::DiffOpt.ForwardObjectiveValue) + return throw(MOI.UnsupportedAttribute(DiffOpt.ForwardObjectiveValue())) end """ diff --git a/src/diff_opt.jl b/src/diff_opt.jl index b61884420..3f3e02a7f 100644 --- a/src/diff_opt.jl +++ b/src/diff_opt.jl @@ -249,19 +249,21 @@ struct ForwardConstraintDual <: MOI.AbstractConstraintAttribute end MOI.is_set_by_optimize(::ForwardConstraintDual) = true """ - ForwardObjectiveSensitivity <: MOI.AbstractModelAttribute + ForwardObjectiveValue <: MOI.AbstractModelAttribute A `MOI.AbstractModelAttribute` to get output objective sensitivity data from forward differentiation. For instance, to get the sensitivity of the objective function with respect to the parameter perturbation, do the following: ```julia -MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) +MOI.get(model, DiffOpt.ForwardObjectiveValue()) ``` """ -struct ForwardObjectiveSensitivity <: MOI.AbstractModelAttribute end +struct ForwardObjectiveValue <: MOI.AbstractModelAttribute end -MOI.is_set_by_optimize(::ForwardObjectiveSensitivity) = true +MOI.is_set_by_optimize(::ForwardObjectiveValue) = true + +Base.@deprecate_binding ForwardObjectiveSensitivity ForwardObjectiveValue """ ReverseObjectiveFunction <: MOI.AbstractModelAttribute diff --git a/src/jump_wrapper.jl b/src/jump_wrapper.jl index d30c26288..64f43998f 100644 --- a/src/jump_wrapper.jl +++ b/src/jump_wrapper.jl @@ -206,10 +206,10 @@ end Get the value of the objective output sensitivity for forward mode. -Equivalent to `get_attribute(model, DiffOpt.ForwardObjectiveSensitivity())`. +Equivalent to `get_attribute(model, DiffOpt.ForwardObjectiveValue())`. """ function get_forward_objective(model::JuMP.Model) - return MOI.get(model, ForwardObjectiveSensitivity()) + return MOI.get(model, ForwardObjectiveValue()) end """ diff --git a/src/moi_wrapper.jl b/src/moi_wrapper.jl index d46c3f7a6..dd60b8c93 100644 --- a/src/moi_wrapper.jl +++ b/src/moi_wrapper.jl @@ -927,7 +927,7 @@ function MOI.get( ) end -function MOI.get(model::Optimizer, attr::ForwardObjectiveSensitivity) +function MOI.get(model::Optimizer, attr::ForwardObjectiveValue) diff_model = _checked_diff(model, attr, :forward_differentiate!) val = 0.0 try diff --git a/test/jump_wrapper.jl b/test/jump_wrapper.jl index a41f40b1c..7de3686d2 100644 --- a/test/jump_wrapper.jl +++ b/test/jump_wrapper.jl @@ -445,7 +445,7 @@ function test_set_get_attribute() optimize!(model) # Forward via attributes: ForwardParameterValue (set), - # ForwardVariablePrimal (get), ForwardObjectiveSensitivity (get), + # ForwardVariablePrimal (get), ForwardObjectiveValue (get), # ForwardConstraintDual (get). DiffOpt.empty_input_sensitivities!(model) set_attribute(p, DiffOpt.ForwardParameterValue(), 1.0) @@ -456,7 +456,7 @@ function test_set_get_attribute() RTOL @test get_attribute(c1, DiffOpt.ForwardConstraintDual()) ≈ 1.75 atol = ATOL rtol = RTOL - @test get_attribute(model, DiffOpt.ForwardObjectiveSensitivity()) ≈ 2.75 atol = + @test get_attribute(model, DiffOpt.ForwardObjectiveValue()) ≈ 2.75 atol = ATOL rtol = RTOL # Reverse via attributes: ReverseVariablePrimal (set), @@ -546,7 +546,7 @@ function test_attribute_types() @test DiffOpt.ReverseVariablePrimal() isa MOI.AbstractVariableAttribute @test DiffOpt.ForwardObjectiveFunction() isa MOI.AbstractModelAttribute @test DiffOpt.ReverseObjectiveFunction() isa MOI.AbstractModelAttribute - @test DiffOpt.ForwardObjectiveSensitivity() isa MOI.AbstractModelAttribute + @test DiffOpt.ForwardObjectiveValue() isa MOI.AbstractModelAttribute @test DiffOpt.ReverseObjectiveValue() isa MOI.AbstractModelAttribute @test DiffOpt.ForwardConstraintFunction() isa MOI.AbstractConstraintAttribute diff --git a/test/nlp_program.jl b/test/nlp_program.jl index b68180d52..a9ec9e1fa 100644 --- a/test/nlp_program.jl +++ b/test/nlp_program.jl @@ -162,7 +162,7 @@ function test_analytical_simple(; P = 2) # Number of parameters DiffOpt.forward_differentiate!(m) # test Objective Sensitivity wrt parameters - df_dp = MOI.get(m, DiffOpt.ForwardObjectiveSensitivity()) + df_dp = MOI.get(m, DiffOpt.ForwardObjectiveValue()) @test isapprox(df_dp, dot(dual.(con), Δp); atol = 1e-4) @test all(isapprox.(dual.(ParameterRef.(p)), dual.(con); atol = 1e-8)) @@ -642,7 +642,7 @@ end =# ################################################ -function test_ObjectiveSensitivity_model1() +function test_ObjectiveValue_model1() # Model 1 model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer)) set_silent(model) @@ -669,7 +669,7 @@ function test_ObjectiveSensitivity_model1() DiffOpt.forward_differentiate!(model) # Test Objective Sensitivity wrt parameters - df_dp = MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) + df_dp = MOI.get(model, DiffOpt.ForwardObjectiveValue()) df = -2cos(p_val) / sin(p_val)^2 @test isapprox(df_dp, df * Δp; atol = 1e-4) @@ -678,7 +678,7 @@ function test_ObjectiveSensitivity_model1() # Test both obj and solution inputs Δf = 0.5 - MOI.set(model, DiffOpt.ReverseObjectiveSensitivity(), Δf) + MOI.set(model, DiffOpt.ReverseObjectiveValue(), Δf) MOI.set(model, DiffOpt.ReverseVariablePrimal(), x, Δp) msg = "Computing reverse differentiation with both solution sensitivities and objective sensitivities. Set `DiffOpt.AllowObjectiveAndSolutionInput()` to `true` to silence this warning." @@ -711,7 +711,7 @@ function test_ObjectiveSensitivity_model1() # Set Reverse Objective Sensitivity Δf = 0.5 - MOI.set(model, DiffOpt.ReverseObjectiveSensitivity(), Δf) + MOI.set(model, DiffOpt.ReverseObjectiveValue(), Δf) # Compute derivatives DiffOpt.reverse_differentiate!(model) @@ -722,7 +722,7 @@ function test_ObjectiveSensitivity_model1() @test isapprox(dp, df * Δf; atol = 1e-4) end -function test_ObjectiveSensitivity_model2() +function test_ObjectiveValue_model2() # Model 2 model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer)) set_silent(model) @@ -749,7 +749,7 @@ function test_ObjectiveSensitivity_model2() DiffOpt.forward_differentiate!(model) # Test Objective Sensitivity wrt parameters - df_dp = MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) + df_dp = MOI.get(model, DiffOpt.ForwardObjectiveValue()) @test isapprox(df_dp, -0.3; atol = 1e-4) # Clean up @@ -757,7 +757,7 @@ function test_ObjectiveSensitivity_model2() # Set Reverse Objective Sensitivity Δf = 0.5 - MOI.set(model, DiffOpt.ReverseObjectiveSensitivity(), Δf) + MOI.set(model, DiffOpt.ReverseObjectiveValue(), Δf) # Compute derivatives DiffOpt.reverse_differentiate!(model) @@ -768,7 +768,7 @@ function test_ObjectiveSensitivity_model2() @test isapprox(dp, -1.5; atol = 1e-4) end -function test_ObjectiveSensitivity_direct_param_contrib() +function test_ObjectiveValue_direct_param_contrib() model = DiffOpt.nonlinear_diff_model(Ipopt.Optimizer) set_silent(model) @@ -784,7 +784,7 @@ function test_ObjectiveSensitivity_direct_param_contrib() DiffOpt.set_forward_parameter(model, p, Δp) DiffOpt.forward_differentiate!(model) - df_dp = MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) + df_dp = MOI.get(model, DiffOpt.ForwardObjectiveValue()) @test isapprox(df_dp, 2 * p_val * Δp, atol = 1e-8) # ≈ 0.6 for p=3 ε = 1e-6 @@ -802,7 +802,7 @@ function test_ObjectiveSensitivity_direct_param_contrib() @test isapprox(df_dp, df_dp_fd, atol = 1e-4) end -function test_ObjectiveSensitivity_subset_parameters() +function test_ObjectiveValue_subset_parameters() # Model with 10 parameters, differentiate only w.r.t. 3rd and 7th model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer)) set_silent(model) @@ -830,7 +830,7 @@ function test_ObjectiveSensitivity_subset_parameters() DiffOpt.forward_differentiate!(model) # Objective sensitivity should equal sum over selected params only - df_dp = MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) + df_dp = MOI.get(model, DiffOpt.ForwardObjectiveValue()) @test isapprox(df_dp, 0.007109293; atol = 1e-4) end diff --git a/test/solver_native_diff.jl b/test/solver_native_diff.jl index 87239860e..54716121f 100644 --- a/test/solver_native_diff.jl +++ b/test/solver_native_diff.jl @@ -396,7 +396,7 @@ function MOI.set( return end -function MOI.set(s::EqQPSolver, ::DiffOpt.ReverseObjectiveSensitivity, value) +function MOI.set(s::EqQPSolver, ::DiffOpt.ReverseObjectiveValue, value) s.rev_dobj = value return end @@ -516,8 +516,8 @@ function MOI.get(s::EqQPSolver, ::DiffOpt.ForwardConstraintDual, ci::EQ_CI) return s.dnu_fwd[ci.value] end -# ForwardObjectiveSensitivity -function MOI.get(s::EqQPSolver, ::DiffOpt.ForwardObjectiveSensitivity) +# ForwardObjectiveValue +function MOI.get(s::EqQPSolver, ::DiffOpt.ForwardObjectiveValue) return s.fwd_obj_sensitivity end @@ -870,13 +870,13 @@ function test_three_variable_problem() end end -# ── Test reverse with dobj seed (ReverseObjectiveSensitivity) ───────────── +# ── Test reverse with dobj seed (ReverseObjectiveValue) ───────────── function test_reverse_dobj_seed() model, x1, x2, c1 = _setup_model() # Set dobj = 1.0: sensitivity of the objective value w.r.t. problem data - MOI.set(model, DiffOpt.ReverseObjectiveSensitivity(), 1.0) + MOI.set(model, DiffOpt.ReverseObjectiveValue(), 1.0) DiffOpt.reverse_differentiate!(model) # x* = [0, 1], nu* = [-3], Q = I, c = [3, 2] @@ -908,8 +908,7 @@ function test_forward_objective_sensitivity() # dobj/dt = (Qx + c)'dx + dc'x = [3,3]'*dx + [1,0]'*[0,1] expected = dot([3.0, 3.0], fwd[1:2]) + dot([1.0, 0.0], [0.0, 1.0]) - @test MOI.get(model, DiffOpt.ForwardObjectiveSensitivity()) ≈ expected atol = - ATOL + @test MOI.get(model, DiffOpt.ForwardObjectiveValue()) ≈ expected atol = ATOL end # ── Test forward twice (re-differentiation in forward mode) ────────────── @@ -979,7 +978,7 @@ function test_reverse_dobj_and_dx_seed() model, x1, x2, c1 = _setup_model() MOI.set(model, DiffOpt.AllowObjectiveAndSolutionInput(), true) - MOI.set(model, DiffOpt.ReverseObjectiveSensitivity(), 1.0) + MOI.set(model, DiffOpt.ReverseObjectiveValue(), 1.0) MOI.set(model, DiffOpt.ReverseVariablePrimal(), x1, 1.0) DiffOpt.reverse_differentiate!(model)