diff --git a/docs/src/usage.md b/docs/src/usage.md index a851f835..e9f0d525 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 ee5492b0..154ffdc8 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 9d813b8b..0de08f30 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 8273308e..1c546460 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 b6188442..3f3e02a7 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 d30c2628..64f43998 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 d46c3f7a..dd60b8c9 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 a41f40b1..7de3686d 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 b68180d5..a9ec9e1f 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 87239860..54716121 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)