From ee243fdb91f5d61798317edb088650234ca3b374 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Sun, 22 Oct 2023 21:49:11 -0300 Subject: [PATCH 1/6] Protect query of multiplicative duals --- src/duals.jl | 2 +- test/jump_tests.jl | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/duals.jl b/src/duals.jl index 5b368d0..3bbd844 100644 --- a/src/duals.jl +++ b/src/duals.jl @@ -129,7 +129,7 @@ function MOI.get( end function is_additive(model::Optimizer, cp::MOI.ConstraintIndex) - if cp.value in model.multiplicative_parameters + if p_val(cp) in model.multiplicative_parameters return false end return true diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 9d355b5..06a93a1 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -500,6 +500,17 @@ function test_jump_dual_basic() return end +function test_jump_dual_multiplicative_fail() + model = Model(() -> POI.Optimizer(HiGHS.Optimizer())) + @variable(model, x) + @variable(model, p in POI.Parameter(1.0)) + @constraint(model, cons, x * p >= 3) + @objective(model, Min, 2x) + optimize!(model) + @test_throws ErrorException("Cannot compute the dual of a multiplicative parameter") MOI.get(model, POI.ParameterDual(), p) + return +end + function test_jump_dual_multiple_parameters_1() model = Model(() -> POI.Optimizer(GLPK.Optimizer())) @variable(model, x[1:6] in POI.Parameter.(ones(6) .* 4.0)) From 07782add31d56b91b2a4c735f141a97ff21bc82a Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 23 Oct 2023 00:41:41 -0300 Subject: [PATCH 2/6] fix parameter dual sign in objective --- src/duals.jl | 6 +++--- test/jump_tests.jl | 24 +++++++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/duals.jl b/src/duals.jl index 3bbd844..c12ffad 100644 --- a/src/duals.jl +++ b/src/duals.jl @@ -83,11 +83,11 @@ function compute_parameters_in_ci!( return end -# this one seem to be the same as the next function update_duals_from_objective!(model::Optimizer{T}, pf) where {T} + is_min = MOI.get(model.optimizer, MOI.ObjectiveSense()) == MOI.MIN_SENSE for param in pf.p - model.dual_value_of_parameters[p_val(param.variable)] -= - param.coefficient + model.dual_value_of_parameters[p_val(param.variable)] += + ifelse(is_min, 1, -1) * param.coefficient end return end diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 06a93a1..4edd316 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -501,7 +501,7 @@ function test_jump_dual_basic() end function test_jump_dual_multiplicative_fail() - model = Model(() -> POI.Optimizer(HiGHS.Optimizer())) + model = Model(() -> POI.Optimizer(GLPK.Optimizer())) @variable(model, x) @variable(model, p in POI.Parameter(1.0)) @constraint(model, cons, x * p >= 3) @@ -511,6 +511,28 @@ function test_jump_dual_multiplicative_fail() return end +function test_jump_dual_objective_min() + model = Model(() -> POI.Optimizer(GLPK.Optimizer())) + @variable(model, x) + @variable(model, p in POI.Parameter(1.0)) + @constraint(model, cons, x >= 3 * p) + @objective(model, Min, 2x + p) + optimize!(model) + @test MOI.get(model, POI.ParameterDual(), p) == 7 + return +end + +function test_jump_dual_objective_max() + model = Model(() -> POI.Optimizer(GLPK.Optimizer())) + @variable(model, x) + @variable(model, p in POI.Parameter(1.0)) + @constraint(model, cons, x >= 3 * p) + @objective(model, Max, -2x + p) + optimize!(model) + @test MOI.get(model, POI.ParameterDual(), p) == 5 + return +end + function test_jump_dual_multiple_parameters_1() model = Model(() -> POI.Optimizer(GLPK.Optimizer())) @variable(model, x[1:6] in POI.Parameter.(ones(6) .* 4.0)) From e338bd24685d34df28a99eb3c40cbe1e5dca42b3 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 23 Oct 2023 02:40:24 -0300 Subject: [PATCH 3/6] format tests --- test/jump_tests.jl | 4 +++- test/moi_tests.jl | 20 -------------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 4edd316..b99a169 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -507,7 +507,9 @@ function test_jump_dual_multiplicative_fail() @constraint(model, cons, x * p >= 3) @objective(model, Min, 2x) optimize!(model) - @test_throws ErrorException("Cannot compute the dual of a multiplicative parameter") MOI.get(model, POI.ParameterDual(), p) + @test_throws ErrorException( + "Cannot compute the dual of a multiplicative parameter", + ) MOI.get(model, POI.ParameterDual(), p) return end diff --git a/test/moi_tests.jl b/test/moi_tests.jl index ed79fa9..8c9057e 100644 --- a/test/moi_tests.jl +++ b/test/moi_tests.jl @@ -1410,29 +1410,9 @@ function test_qp_objective_affine_parameter() 0, atol = ATOL, ) - @test isapprox( - MOI.get(optimizer, MOI.ConstraintDual(), cy), - -2.0, - atol = ATOL, - ) - @test isapprox( - MOI.get(optimizer, MOI.ConstraintDual(), cz), - -1.0, - atol = ATOL, - ) MOI.set(optimizer, MOI.ConstraintSet(), cy, POI.Parameter(2.0)) MOI.optimize!(optimizer) @test isapprox(MOI.get(optimizer, MOI.ObjectiveValue()), 5.0, atol = ATOL) - @test isapprox( - MOI.get(optimizer, MOI.ConstraintDual(), cy), - -2.0, - atol = ATOL, - ) - @test isapprox( - MOI.get(optimizer, MOI.ConstraintDual(), cz), - -1.0, - atol = ATOL, - ) MOI.set(optimizer, MOI.ConstraintSet(), cz, POI.Parameter(3.0)) MOI.optimize!(optimizer) @test isapprox(MOI.get(optimizer, MOI.ObjectiveValue()), 7.0, atol = ATOL) From 7d4059a75cab9d41aee6a0f6804cd97243620521 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 23 Oct 2023 02:40:31 -0300 Subject: [PATCH 4/6] format docs --- docs/make.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 660047f..894f8f1 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,12 +21,16 @@ makedocs( pages = [ "Home" => "index.md", "manual.md", - "Examples" => ["Examples/example.md","Examples/benders.md", "Examples/markowitz.md"], - "reference.md" + "Examples" => [ + "Examples/example.md", + "Examples/benders.md", + "Examples/markowitz.md", + ], + "reference.md", ], ) deploydocs( repo = "github.com/jump-dev/ParametricOptInterface.jl.git", - push_preview = true + push_preview = true, ) From 515d8f998d63e80cef4d55fff907088e02951c60 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 23 Oct 2023 02:40:39 -0300 Subject: [PATCH 5/6] format benchmarks --- benchmark/MOI_benchmarks.jl | 188 ++++--- benchmark/run_benchmarks.jl | 460 +++++++++++------- benchmark/run_benchmarks_jump.jl | 54 +- ...n_benders_quantile_regression_benchmark.jl | 1 - 4 files changed, 432 insertions(+), 271 deletions(-) diff --git a/benchmark/MOI_benchmarks.jl b/benchmark/MOI_benchmarks.jl index ed803c1..6038654 100644 --- a/benchmark/MOI_benchmarks.jl +++ b/benchmark/MOI_benchmarks.jl @@ -10,11 +10,11 @@ using GLPK import Random #using SparseArrays using TimerOutputs -​ + const MOI = MathOptInterface const POI = ParametricOptInterface SOLVER = GLPK -​ + if SOLVER == GLPK MAX_ITER_PARAM = "it_lim" elseif SOLVER == Gurobi @@ -22,43 +22,43 @@ elseif SOLVER == Gurobi elseif SOLVER == Xpress MAX_ITER_PARAM = "LPITERLIMIT" end -​ + struct PMedianData num_facilities::Int num_customers::Int num_locations::Int customer_locations::Vector{Float64} end -​ + # This is the LP relaxation. function generate_poi_problem(model, data::PMedianData, add_parameters::Bool) NL = data.num_locations NC = data.num_customers -​ + ### ### 0 <= facility_variables <= 1 ### -​ + facility_variables = MOI.add_variables(model, NL) -​ + for v in facility_variables MOI.add_constraint(model, v, MOI.Interval(0.0, 1.0)) end -​ + ### ### assignment_variables >= 0 ### -​ + assignment_variables = reshape(MOI.add_variables(model, NC * NL), NC, NL) for v in assignment_variables MOI.add_constraint(model, v, MOI.GreaterThan(0.0)) # "Less than 1.0" constraint is redundant. end -​ + ### ### Objective function ### -​ + MOI.set( model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), @@ -66,64 +66,66 @@ function generate_poi_problem(model, data::PMedianData, add_parameters::Bool) [ MOI.ScalarAffineTerm( data.customer_locations[i] - j, - assignment_variables[i, j] - ) - for i in 1:NC for j in 1:NL + assignment_variables[i, j], + ) for i in 1:NC for j in 1:NL ], 0.0, ), ) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) -​ + ### ### assignment_variables[i, j] <= facility_variables[j] ### -​ + for i in 1:NC, j in 1:NL MOI.add_constraint( model, MOI.ScalarAffineFunction( [ MOI.ScalarAffineTerm(1.0, assignment_variables[i, j]), - MOI.ScalarAffineTerm(-1.0, facility_variables[j]) + MOI.ScalarAffineTerm(-1.0, facility_variables[j]), ], 0.0, ), MOI.LessThan(0.0), ) end -​ + ### ### sum_j assignment_variables[i, j] = 1 ### -​ + for i in 1:NC MOI.add_constraint( model, MOI.ScalarAffineFunction( [ - MOI.ScalarAffineTerm(1.0, assignment_variables[i, j]) - for j in 1:NL + MOI.ScalarAffineTerm(1.0, assignment_variables[i, j]) for + j in 1:NL ], 0.0, ), MOI.EqualTo(1.0), ) end -​ + ### ### sum_j facility_variables[j] == num_facilities ### -​ + if add_parameters - d, cd = MOI.add_constrained_variable(model, POI.Parameter(data.num_facilities)) + d, cd = MOI.add_constrained_variable( + model, + POI.Parameter(data.num_facilities), + ) end -​ + if add_parameters MOI.add_constraint( model, MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.(1.0, vcat(facility_variables,d)), + MOI.ScalarAffineTerm.(1.0, vcat(facility_variables, d)), 0.0, ), MOI.EqualTo{Float64}(0), @@ -138,11 +140,17 @@ function generate_poi_problem(model, data::PMedianData, add_parameters::Bool) MOI.EqualTo{Float64}(data.num_facilities), ) end -​ + return assignment_variables, facility_variables end -​ -function solve_moi(data::PMedianData, optimizer; vector_version, params, add_parameters=false) + +function solve_moi( + data::PMedianData, + optimizer; + vector_version, + params, + add_parameters = false, +) model = optimizer() for (param, value) in params MOI.set(model, param, value) @@ -155,16 +163,22 @@ function solve_moi(data::PMedianData, optimizer; vector_version, params, add_par @timeit "solve" MOI.optimize!(model) return MOI.get(model, MOI.ObjectiveValue()) end -​ + function POI_OPTIMIZER() return POI.Optimizer(SOLVER.Optimizer()) end -​ + function MOI_OPTIMIZER() return SOLVER.Optimizer() end -​ -function solve_moi_loop(data::PMedianData; vector_version, max_iters=Inf, time_limit_sec=Inf, loops) + +function solve_moi_loop( + data::PMedianData; + vector_version, + max_iters = Inf, + time_limit_sec = Inf, + loops, +) params = [] if isfinite(time_limit_sec) push!(params, (MOI.TimeLimitSec(), time_limit_sec)) @@ -172,20 +186,29 @@ function solve_moi_loop(data::PMedianData; vector_version, max_iters=Inf, time_l if isfinite(max_iters) push!(params, (MOI.RawOptimizerAttribute(MAX_ITER_PARAM), max_iters)) end - push!(params, (MOI.Silent(),true)) + push!(params, (MOI.Silent(), true)) s_type = vector_version ? "vector" : "scalar" -​ + @timeit( "$(SOLVER) MOI $(s_type)", for _ in 1:loops solve_moi( - data, MOI_OPTIMIZER; vector_version=vector_version, params=params + data, + MOI_OPTIMIZER; + vector_version = vector_version, + params = params, ) end ) end -​ -function solve_poi_no_params_loop(data::PMedianData; vector_version, max_iters=Inf, time_limit_sec=Inf, loops) + +function solve_poi_no_params_loop( + data::PMedianData; + vector_version, + max_iters = Inf, + time_limit_sec = Inf, + loops, +) params = [] if isfinite(time_limit_sec) push!(params, (MOI.TimeLimitSec(), time_limit_sec)) @@ -193,19 +216,28 @@ function solve_poi_no_params_loop(data::PMedianData; vector_version, max_iters=I if isfinite(max_iters) push!(params, (MOI.RawOptimizerAttribute(MAX_ITER_PARAM), max_iters)) end - push!(params, (MOI.Silent(),true)) + push!(params, (MOI.Silent(), true)) s_type = vector_version ? "vector" : "scalar" @timeit( "$(SOLVER) POI NO PARAMS $(s_type)", for _ in 1:loops solve_moi( - data,POI_OPTIMIZER; vector_version=vector_version, params=params + data, + POI_OPTIMIZER; + vector_version = vector_version, + params = params, ) end ) end -​ -function solve_poi_loop(data::PMedianData; vector_version, max_iters=Inf, time_limit_sec=Inf, loops=1) + +function solve_poi_loop( + data::PMedianData; + vector_version, + max_iters = Inf, + time_limit_sec = Inf, + loops = 1, +) params = [] if isfinite(time_limit_sec) push!(params, (MOI.TimeLimitSec(), time_limit_sec)) @@ -213,50 +245,82 @@ function solve_poi_loop(data::PMedianData; vector_version, max_iters=Inf, time_l if isfinite(max_iters) push!(params, (MOI.RawOptimizerAttribute(MAX_ITER_PARAM), max_iters)) end - push!(params, (MOI.Silent(),true)) + push!(params, (MOI.Silent(), true)) s_type = vector_version ? "vector" : "scalar" -​ + @timeit( "$(SOLVER) POI $(s_type)", for _ in 1:loops solve_moi( - data,POI_OPTIMIZER; vector_version=vector_version, params=params, add_parameters = true + data, + POI_OPTIMIZER; + vector_version = vector_version, + params = params, + add_parameters = true, ) end ) end -​ + function run_benchmark(; - num_facilities, num_customers, num_locations, time_limit_sec, max_iters, loops + num_facilities, + num_customers, + num_locations, + time_limit_sec, + max_iters, + loops, ) Random.seed!(10) reset_timer!() - data = PMedianData(num_facilities, num_customers, num_locations, rand(num_customers) .* num_locations) + data = PMedianData( + num_facilities, + num_customers, + num_locations, + rand(num_customers) .* num_locations, + ) GC.gc() - solve_moi_loop(data, vector_version=false, max_iters=max_iters, time_limit_sec=time_limit_sec, loops=loops) + solve_moi_loop( + data, + vector_version = false, + max_iters = max_iters, + time_limit_sec = time_limit_sec, + loops = loops, + ) GC.gc() - solve_poi_no_params_loop(data, vector_version=false, max_iters=max_iters, time_limit_sec=time_limit_sec, loops=loops) + solve_poi_no_params_loop( + data, + vector_version = false, + max_iters = max_iters, + time_limit_sec = time_limit_sec, + loops = loops, + ) GC.gc() - solve_poi_loop(data, vector_version=false, max_iters=max_iters, time_limit_sec=time_limit_sec, loops=loops) + solve_poi_loop( + data, + vector_version = false, + max_iters = max_iters, + time_limit_sec = time_limit_sec, + loops = loops, + ) GC.gc() print_timer() - println() + return println() end -​ + run_benchmark( - num_facilities = 100, - num_customers = 100, - num_locations = 100, - time_limit_sec = 0.0001, - max_iters = 1, - loops = 1 - ) -​ + num_facilities = 100, + num_customers = 100, + num_locations = 100, + time_limit_sec = 0.0001, + max_iters = 1, + loops = 1, +) + run_benchmark( num_facilities = 100, num_customers = 100, num_locations = 100, time_limit_sec = 0.0001, max_iters = 1, - loops = 100 + loops = 100, ) diff --git a/benchmark/run_benchmarks.jl b/benchmark/run_benchmarks.jl index 0e70fd9..3ca572a 100644 --- a/benchmark/run_benchmarks.jl +++ b/benchmark/run_benchmarks.jl @@ -24,20 +24,20 @@ end function poi_add_parameters(N::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - MOI.add_constrained_variable.(model, POI.Parameter.(ones(N))); + MOI.add_constrained_variable.(model, POI.Parameter.(ones(N))) return nothing end function poi_add_parameters_and_variables(N::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - MOI.add_variables(model, N/2) - MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2)))) + MOI.add_variables(model, N / 2) + MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N / 2)))) return nothing end function poi_add_parameters_and_variables_alternating(N::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - for i in 1:Int(N/2) + for i in 1:Int(N / 2) MOI.add_variable(model) MOI.add_constrained_variable(model, POI.Parameter(1)) end @@ -49,13 +49,10 @@ function moi_add_saf_ctr(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.(1.0, x), - 0.0, - ), - MOI.GreaterThan(1.0), - ) + model, + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0), + MOI.GreaterThan(1.0), + ) end return nothing end @@ -65,47 +62,59 @@ function poi_add_saf_ctr(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.add_constraint( - model, - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.(1.0, x), - 0.0, - ), - MOI.GreaterThan(1.0), - ) + model, + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0), + MOI.GreaterThan(1.0), + ) end return nothing end function poi_add_saf_variables_and_parameters_ctr(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.add_constraint( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ScalarAffineFunction( - [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], - 0.0, - ), - MOI.GreaterThan(1.0), + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end return nothing end -function poi_add_saf_variables_and_parameters_ctr_parameter_update(N::Int, M::Int) +function poi_add_saf_variables_and_parameters_ctr_parameter_update( + N::Int, + M::Int, +) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.add_constraint( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ScalarAffineFunction( - [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], - 0.0, - ), - MOI.GreaterThan(1.0), + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.add_constraint( + model, + MOI.ScalarAffineFunction( + [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end MOI.set.(model, POI.ParameterValue(), y, 0.5) POI.update_parameters!(model) @@ -117,14 +126,14 @@ function moi_add_sqf_variables_ctr(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.add_constraint( - model, - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, x), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ), - MOI.GreaterThan(1.0), - ) + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, x), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end return nothing end @@ -134,50 +143,62 @@ function poi_add_sqf_variables_ctr(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.add_constraint( - model, - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, x), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ), - MOI.GreaterThan(1.0), - ) + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, x), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end return nothing end function poi_add_sqf_variables_parameters_ctr(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.add_constraint( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ), - MOI.GreaterThan(1.0), + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.add_constraint( + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end return nothing end function poi_add_sqf_variables_parameters_ctr_parameter_update(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.add_constraint( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ), - MOI.GreaterThan(1.0), + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.add_constraint( + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end MOI.set.(model, POI.ParameterValue(), y, 0.5) POI.update_parameters!(model) @@ -186,36 +207,48 @@ end function poi_add_sqf_parameters_parameters_ctr(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.add_constraint( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, y, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ), - MOI.GreaterThan(1.0), + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.add_constraint( + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, y, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end return nothing end function poi_add_sqf_parameters_parameters_ctr_parameter_update(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.add_constraint( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, y, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ), - MOI.GreaterThan(1.0), + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.add_constraint( + model, + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, y, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + MOI.GreaterThan(1.0), + ) end MOI.set.(model, POI.ParameterValue(), y, 0.5) POI.update_parameters!(model) @@ -228,12 +261,9 @@ function moi_add_saf_obj(N::Int, M::Int) for _ in 1:M MOI.set( model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.(1.0, x), - 0.0, - ) - ) + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0), + ) end return nothing end @@ -243,47 +273,59 @@ function poi_add_saf_obj(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.(1.0, x), - 0.0, - ) - ) + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0), + ) end return nothing end function poi_add_saf_variables_and_parameters_obj(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.set( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction( - [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], - 0.0, - ) + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction( + [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], + 0.0, + ), + ) end return nothing end -function poi_add_saf_variables_and_parameters_obj_parameter_update(N::Int, M::Int) +function poi_add_saf_variables_and_parameters_obj_parameter_update( + N::Int, + M::Int, +) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.set( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - MOI.ScalarAffineFunction( - [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], - 0.0, - ) + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction( + [MOI.ScalarAffineTerm.(1.0, x); MOI.ScalarAffineTerm.(1.0, y)], + 0.0, + ), + ) end for _ in 1:M MOI.set.(model, POI.ParameterValue(), y, 0.5) @@ -297,14 +339,14 @@ function moi_add_sqf_variables_obj(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, x), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) - ) + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, x), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + ) end return nothing end @@ -314,50 +356,62 @@ function poi_add_sqf_variables_obj(N::Int, M::Int) x = MOI.add_variables(model, N) for _ in 1:M MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, x), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) - ) + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, x), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + ) end return nothing end function poi_add_sqf_variables_parameters_obj(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.set( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + ) end return nothing end function poi_add_sqf_variables_parameters_obj_parameter_update(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.set( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, x, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, x, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + ) end for _ in 1:M MOI.set.(model, POI.ParameterValue(), y, 0.5) @@ -368,36 +422,48 @@ end function poi_add_sqf_parameters_parameters_obj(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.set( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, y, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, y, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + ) end return nothing end function poi_add_sqf_parameters_parameters_obj_parameter_update(N::Int, M::Int) model = POI.Optimizer(MOI.Utilities.Model{Float64}()) - x = MOI.add_variables(model, N/2) - y = first.(MOI.add_constrained_variable.(model, POI.Parameter.(ones(Int(N/2))))) - for _ in 1:M - MOI.set( + x = MOI.add_variables(model, N / 2) + y = + first.( + MOI.add_constrained_variable.( model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - MOI.ScalarQuadraticFunction( - MOI.ScalarQuadraticTerm.(1.0, y, y), - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) + POI.Parameter.(ones(Int(N / 2))), ) + ) + for _ in 1:M + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.(1.0, y, y), + MOI.ScalarAffineTerm{Float64}[], + 0.0, + ), + ) end for _ in 1:M MOI.set.(model, POI.ParameterValue(), y, 0.5) @@ -441,7 +507,9 @@ function run_benchmarks(N::Int, M::Int) println("SQF constraint with variables on a POI.Optimizer.") @btime poi_add_sqf_variables_ctr($N, $M) GC.gc() - println("SQF constraint with product of variables and parameters on a POI.Optimizer.") + println( + "SQF constraint with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_ctr($N, $M) GC.gc() println("SQF constraint with product of parameters on a POI.Optimizer.") @@ -462,28 +530,42 @@ function run_benchmarks(N::Int, M::Int) println("SQF objective with variables on a POI.Optimizer.") @btime poi_add_sqf_variables_obj($N, $M) GC.gc() - println("SQF objective with product of variables and parameters on a POI.Optimizer.") + println( + "SQF objective with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_obj($N, $M) GC.gc() println("SQF objective with product of parameters on a POI.Optimizer.") @btime poi_add_sqf_parameters_parameters_obj($N, $M) GC.gc() - println("Update parameters in SAF constraint with variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SAF constraint with variables and parameters on a POI.Optimizer.", + ) @btime poi_add_saf_variables_and_parameters_ctr_parameter_update($N, $M) GC.gc() - println("Update parameters in SAF objective with variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SAF objective with variables and parameters on a POI.Optimizer.", + ) @btime poi_add_saf_variables_and_parameters_obj_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF constraint with product of variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SQF constraint with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_ctr_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF constraint with product of parameters on a POI.Optimizer.") + println( + "Update parameters in SQF constraint with product of parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_parameters_parameters_ctr_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF objective with product of variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SQF objective with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_obj_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF objective with product of parameters on a POI.Optimizer.") + println( + "Update parameters in SQF objective with product of parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_parameters_parameters_obj_parameter_update($N, $M) return nothing end diff --git a/benchmark/run_benchmarks_jump.jl b/benchmark/run_benchmarks_jump.jl index d1b9ccb..9ddc49e 100644 --- a/benchmark/run_benchmarks_jump.jl +++ b/benchmark/run_benchmarks_jump.jl @@ -145,7 +145,7 @@ function moi_add_sqf_variables_ctr(N::Int, M::Int) ), ) @variable(model, x[i = 1:N]) - @constraint(model, con[i = 1:M], dot(x , x) >= 1) + @constraint(model, con[i = 1:M], dot(x, x) >= 1) return nothing end @@ -174,7 +174,7 @@ function poi_add_sqf_variables_parameters_ctr(N::Int, M::Int) ) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) - @constraint(model, con[i = 1:M], dot(x , p) >= 1) + @constraint(model, con[i = 1:M], dot(x, p) >= 1) return nothing end @@ -189,7 +189,7 @@ function poi_add_sqf_variables_parameters_ctr_parameter_update(N::Int, M::Int) ) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) - @constraint(model, con[i = 1:M], dot(x , p) >= 1) + @constraint(model, con[i = 1:M], dot(x, p) >= 1) MOI.set.(model, POI.ParameterValue(), p, 0.5) POI.update_parameters!(backend(model)) return nothing @@ -206,7 +206,7 @@ function poi_add_sqf_parameters_parameters_ctr(N::Int, M::Int) ) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) - @constraint(model, con[i = 1:M], dot(p , p) >= 1) + @constraint(model, con[i = 1:M], dot(p, p) >= 1) return nothing end @@ -221,7 +221,7 @@ function poi_add_sqf_parameters_parameters_ctr_parameter_update(N::Int, M::Int) ) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) - @constraint(model, con[i = 1:M], dot(p , p) >= 1) + @constraint(model, con[i = 1:M], dot(p, p) >= 1) MOI.set.(model, POI.ParameterValue(), p, 0.5) POI.update_parameters!(backend(model)) return nothing @@ -305,7 +305,7 @@ function moi_add_sqf_variables_obj(N::Int, M::Int) ) @variable(model, x[i = 1:N]) for _ in 1:M - @objective(model, Min, dot(x , x)) + @objective(model, Min, dot(x, x)) end return nothing end @@ -321,7 +321,7 @@ function poi_add_sqf_variables_obj(N::Int, M::Int) ) @variable(model, x[i = 1:N]) for _ in 1:M - @objective(model, Min, dot(x , x)) + @objective(model, Min, dot(x, x)) end return nothing end @@ -338,7 +338,7 @@ function poi_add_sqf_variables_parameters_obj(N::Int, M::Int) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) for _ in 1:M - @objective(model, Min, dot(x , p)) + @objective(model, Min, dot(x, p)) end return nothing end @@ -355,7 +355,7 @@ function poi_add_sqf_variables_parameters_obj_parameter_update(N::Int, M::Int) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) for _ in 1:M - @objective(model, Min, dot(x , p)) + @objective(model, Min, dot(x, p)) end for _ in 1:M MOI.set.(model, POI.ParameterValue(), p, 0.5) @@ -376,7 +376,7 @@ function poi_add_sqf_parameters_parameters_obj(N::Int, M::Int) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) for _ in 1:M - @objective(model, Min, dot(p , p)) + @objective(model, Min, dot(p, p)) end return nothing end @@ -393,7 +393,7 @@ function poi_add_sqf_parameters_parameters_obj_parameter_update(N::Int, M::Int) @variable(model, x[i = 1:Int(N / 2)]) @variable(model, p[i = 1:Int(N / 2)] in POI.Parameter.(1)) for _ in 1:M - @objective(model, Min, dot(p , p)) + @objective(model, Min, dot(p, p)) end for _ in 1:M MOI.set.(model, POI.ParameterValue(), p, 0.5) @@ -437,7 +437,9 @@ function run_benchmarks(N::Int, M::Int) println("SQF constraint with variables on a POI.Optimizer.") @btime poi_add_sqf_variables_ctr($N, $M) GC.gc() - println("SQF constraint with product of variables and parameters on a POI.Optimizer.") + println( + "SQF constraint with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_ctr($N, $M) GC.gc() println("SQF constraint with product of parameters on a POI.Optimizer.") @@ -458,28 +460,42 @@ function run_benchmarks(N::Int, M::Int) println("SQF objective with variables on a POI.Optimizer.") @btime poi_add_sqf_variables_obj($N, $M) GC.gc() - println("SQF objective with product of variables and parameters on a POI.Optimizer.") + println( + "SQF objective with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_obj($N, $M) GC.gc() println("SQF objective with product of parameters on a POI.Optimizer.") @btime poi_add_sqf_parameters_parameters_obj($N, $M) GC.gc() - println("Update parameters in SAF constraint with variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SAF constraint with variables and parameters on a POI.Optimizer.", + ) @btime poi_add_saf_variables_and_parameters_ctr_parameter_update($N, $M) GC.gc() - println("Update parameters in SAF objective with variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SAF objective with variables and parameters on a POI.Optimizer.", + ) @btime poi_add_saf_variables_and_parameters_obj_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF constraint with product of variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SQF constraint with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_ctr_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF constraint with product of parameters on a POI.Optimizer.") + println( + "Update parameters in SQF constraint with product of parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_parameters_parameters_ctr_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF objective with product of variables and parameters on a POI.Optimizer.") + println( + "Update parameters in SQF objective with product of variables and parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_variables_parameters_obj_parameter_update($N, $M) GC.gc() - println("Update parameters in SQF objective with product of parameters on a POI.Optimizer.") + println( + "Update parameters in SQF objective with product of parameters on a POI.Optimizer.", + ) @btime poi_add_sqf_parameters_parameters_obj_parameter_update($N, $M) return nothing end diff --git a/benchmark/run_benders_quantile_regression_benchmark.jl b/benchmark/run_benders_quantile_regression_benchmark.jl index e1e1718..fcebb0c 100644 --- a/benchmark/run_benders_quantile_regression_benchmark.jl +++ b/benchmark/run_benders_quantile_regression_benchmark.jl @@ -322,7 +322,6 @@ function decomposed_model(PARAM; print_timer_outputs::Bool = true) return best_sol[1] end - println("ParameterJuMP") GC.gc() β1 = decomposed_model(0; print_timer_outputs = false); From 26f0bdc44cc141ef243882dfa6bec5404ca3aee7 Mon Sep 17 00:00:00 2001 From: Joaquim Garcia Date: Mon, 23 Oct 2023 03:35:31 -0300 Subject: [PATCH 6/6] fix multiplicative params --- src/duals.jl | 2 +- src/utils.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/duals.jl b/src/duals.jl index c12ffad..7c2140b 100644 --- a/src/duals.jl +++ b/src/duals.jl @@ -129,7 +129,7 @@ function MOI.get( end function is_additive(model::Optimizer, cp::MOI.ConstraintIndex) - if p_val(cp) in model.multiplicative_parameters + if cp.value in model.multiplicative_parameters return false end return true diff --git a/src/utils.jl b/src/utils.jl index c6034b0..c381d5b 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -534,7 +534,7 @@ function cache_multiplicative_params!( f::ParametricQuadraticFunction{T}, ) where {T} for term in f.pv - push!(model.multiplicative_parameters, term.variable_2.value) + push!(model.multiplicative_parameters, term.variable_1.value) end # TODO compute these duals might be feasible for term in f.pp