From 4b397a6ab1291fcb03378155dece535b32bc0ef3 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 28 Jun 2024 10:14:25 -0600 Subject: [PATCH 001/108] working script sipopt --- testing_barrier.jl | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 testing_barrier.jl diff --git a/testing_barrier.jl b/testing_barrier.jl new file mode 100644 index 00000000..4bc63268 --- /dev/null +++ b/testing_barrier.jl @@ -0,0 +1,78 @@ +################################################ +# sIPOPT test +################################################ + +using JuMP +using Ipopt +# using MadNLP +# using KNITRO + +############ +# Test Case +############ + +# Define the problem +model = Model(Ipopt.Optimizer) + +# Parameters +@variable(model, p ∈ MOI.Parameter(1.0)) + +# Variables +@variable(model, x) +@variable(model, y) + +# Constraints +@constraint(model, con1, y == sin(p)) # NLP Constraint +@constraint(model, con2, x >= p) +@constraint(model, con3, y >= 0) +@objective(model, Min, (1 - x)^2 + 100 * (y - x^2)^2) # NLP Objective +optimize!(model) + +# Check local optimality +termination_status(model) + +############ +# Retrieve important quantities +############ + +# Primal Solution +_values = value.([x, y]) + +# `Evaluator`: Object that helps evaluating functions and calculating related values (Hessian, Jacobian, ...) +evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_model, JuMP.MOI.Nonlinear.SparseReverseMode(), [model.moi_backend.model_to_optimizer_map[index(x)], model.moi_backend.model_to_optimizer_map[index(y)]]) + +# Define what we will need to evaluate +MOI.initialize(evaluator, [:Grad, :Jac, :Hess, :JacVec]) + +# Hessian "Symetric" structure values - Placeholder that will be modified to during the evaluation of the hessian +W = [NaN, NaN, NaN] + +# Modify H with the values for the hessian of the lagrangian +MOI.eval_hessian_lagrangian(evaluator, W, _values, 1.0, dual.([con1; con2; con3])) + +# Jacobian of the constraints Placeholder +jacobian_sparsity = MOI.jacobian_structure(evaluator) +A = zeros(length(jacobian_sparsity)) + +# Evaluate Jacobian +MOI.eval_constraint_jacobian(evaluator, A, _values) + + +############ +# (WORK IN PROGRESS) - Non working code + +# Calculate Sensitivity +############ +V = diag(dual.([con1; con2; con3])) +X = diag(_values) + +M = [ + [W A -I]; + [A' 0 0]; + [V 0 X] +] + +N = [∇ₓₚL ; ∇ₚC] + +# sesitivity of the solution (primal-dual) with respect to the parameter +∂s = inv(M) * N \ No newline at end of file From 8098c3e31bbb70369b7df1ed0340324e50b8578d Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 28 Jun 2024 10:42:19 -0600 Subject: [PATCH 002/108] add todo Np --- testing_barrier.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing_barrier.jl b/testing_barrier.jl index 4bc63268..2b7feded 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -57,6 +57,8 @@ A = zeros(length(jacobian_sparsity)) # Evaluate Jacobian MOI.eval_constraint_jacobian(evaluator, A, _values) +# TODO: ∇ₓₚL (Partial second derivative of the lagrangian wrt primal solution and parameters) ; +# TODO: ∇ₚC (partial derivative of the equality constraintswith wrt parameters). ############ # (WORK IN PROGRESS) - Non working code From 1ac01c4b2e0e7173fab8b785dbd34a02ccefcdb3 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 28 Jun 2024 14:45:58 -0600 Subject: [PATCH 003/108] test dense hessian --- testing_barrier.jl | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index 2b7feded..27d3f4a9 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -3,6 +3,8 @@ ################################################ using JuMP +using SparseArrays +using LinearAlgebra using Ipopt # using MadNLP # using KNITRO @@ -35,8 +37,20 @@ termination_status(model) # Retrieve important quantities ############ +function _dense_hessian(hessian_sparsity, V, n) + I = [i for (i, _) in hessian_sparsity] + J = [j for (_, j) in hessian_sparsity] + raw = SparseArrays.sparse(I, J, V, n, n) + return Matrix( + raw + raw' - + SparseArrays.sparse(LinearAlgebra.diagm(0 => LinearAlgebra.diag(raw))), + ) +end + # Primal Solution -_values = value.([x, y]) +primal_values = value.([x, y]) +dual_values = dual.([con1; con2; con3]) +num_vars = length(primal_values) # `Evaluator`: Object that helps evaluating functions and calculating related values (Hessian, Jacobian, ...) evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_model, JuMP.MOI.Nonlinear.SparseReverseMode(), [model.moi_backend.model_to_optimizer_map[index(x)], model.moi_backend.model_to_optimizer_map[index(y)]]) @@ -46,16 +60,18 @@ MOI.initialize(evaluator, [:Grad, :Jac, :Hess, :JacVec]) # Hessian "Symetric" structure values - Placeholder that will be modified to during the evaluation of the hessian W = [NaN, NaN, NaN] +hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) # Modify H with the values for the hessian of the lagrangian -MOI.eval_hessian_lagrangian(evaluator, W, _values, 1.0, dual.([con1; con2; con3])) +MOI.eval_hessian_lagrangian(evaluator, W, primal_values, 1.0, dual_values) +W = _dense_hessian(hessian_sparsity, W, num_vars) # Jacobian of the constraints Placeholder jacobian_sparsity = MOI.jacobian_structure(evaluator) A = zeros(length(jacobian_sparsity)) # Evaluate Jacobian -MOI.eval_constraint_jacobian(evaluator, A, _values) +MOI.eval_constraint_jacobian(evaluator, A, primal_values) # TODO: ∇ₓₚL (Partial second derivative of the lagrangian wrt primal solution and parameters) ; # TODO: ∇ₚC (partial derivative of the equality constraintswith wrt parameters). @@ -65,8 +81,8 @@ MOI.eval_constraint_jacobian(evaluator, A, _values) # Calculate Sensitivity ############ -V = diag(dual.([con1; con2; con3])) -X = diag(_values) +V = diag(dual_values) +X = diag(primal_values) M = [ [W A -I]; From ac8a2394f61b9d3b5caed149e29ddccd752982b3 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 28 Jun 2024 15:27:15 -0600 Subject: [PATCH 004/108] add dense jacobian --- testing_barrier.jl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index 27d3f4a9..dfa8fb00 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -18,15 +18,16 @@ model = Model(Ipopt.Optimizer) # Parameters @variable(model, p ∈ MOI.Parameter(1.0)) +@variable(model, p2 ∈ MOI.Parameter(2.0)) # Variables @variable(model, x) @variable(model, y) # Constraints -@constraint(model, con1, y == sin(p)) # NLP Constraint -@constraint(model, con2, x >= p) -@constraint(model, con3, y >= 0) +@constraint(model, con1, y >= p*sin(x)) # NLP Constraint +@constraint(model, con2, x + y == p) +@constraint(model, con3, p2 * x >= 0.1) @objective(model, Min, (1 - x)^2 + 100 * (y - x^2)^2) # NLP Objective optimize!(model) @@ -47,10 +48,18 @@ function _dense_hessian(hessian_sparsity, V, n) ) end +function _dense_jacobian(jacobian_sparsity, V, m, n) + I = [i for (i, j) in jacobian_sparsity] + J = [j for (i, j) in jacobian_sparsity] + raw = SparseArrays.sparse(I, J, V, m, n) + return Matrix(raw) +end + # Primal Solution primal_values = value.([x, y]) dual_values = dual.([con1; con2; con3]) num_vars = length(primal_values) +num_cons = length(dual_values) # `Evaluator`: Object that helps evaluating functions and calculating related values (Hessian, Jacobian, ...) evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_model, JuMP.MOI.Nonlinear.SparseReverseMode(), [model.moi_backend.model_to_optimizer_map[index(x)], model.moi_backend.model_to_optimizer_map[index(y)]]) @@ -59,8 +68,8 @@ evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_m MOI.initialize(evaluator, [:Grad, :Jac, :Hess, :JacVec]) # Hessian "Symetric" structure values - Placeholder that will be modified to during the evaluation of the hessian -W = [NaN, NaN, NaN] hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) +W = fill(NaN, length(hessian_sparsity)) # Modify H with the values for the hessian of the lagrangian MOI.eval_hessian_lagrangian(evaluator, W, primal_values, 1.0, dual_values) @@ -72,6 +81,7 @@ A = zeros(length(jacobian_sparsity)) # Evaluate Jacobian MOI.eval_constraint_jacobian(evaluator, A, primal_values) +A = _dense_jacobian(jacobian_sparsity, A, num_cons, num_vars) # TODO: ∇ₓₚL (Partial second derivative of the lagrangian wrt primal solution and parameters) ; # TODO: ∇ₚC (partial derivative of the equality constraintswith wrt parameters). @@ -81,8 +91,8 @@ MOI.eval_constraint_jacobian(evaluator, A, primal_values) # Calculate Sensitivity ############ -V = diag(dual_values) -X = diag(primal_values) +V = diagm(dual_values) +X = diagm(primal_values) M = [ [W A -I]; From 188ab6f4f383d52ed7ff74cf91b9c44acf1eb07b Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 28 Jun 2024 15:36:51 -0600 Subject: [PATCH 005/108] add parameters to the calculations --- testing_barrier.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index dfa8fb00..60eb05f4 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -56,13 +56,19 @@ function _dense_jacobian(jacobian_sparsity, V, m, n) end # Primal Solution -primal_values = value.([x, y]) +primal_values = value.([x, y, p, p2]) dual_values = dual.([con1; con2; con3]) num_vars = length(primal_values) num_cons = length(dual_values) # `Evaluator`: Object that helps evaluating functions and calculating related values (Hessian, Jacobian, ...) -evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_model, JuMP.MOI.Nonlinear.SparseReverseMode(), [model.moi_backend.model_to_optimizer_map[index(x)], model.moi_backend.model_to_optimizer_map[index(y)]]) +evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_model, JuMP.MOI.Nonlinear.SparseReverseMode(), + [ model.moi_backend.model_to_optimizer_map[index(x)], + model.moi_backend.model_to_optimizer_map[index(y)], + model.moi_backend.model_to_optimizer_map[index(p)], + model.moi_backend.model_to_optimizer_map[index(p2)], + ] +) # Define what we will need to evaluate MOI.initialize(evaluator, [:Grad, :Jac, :Hess, :JacVec]) From 2b30d5d0438930efd9ec8b133ecab94a7b69a909 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 28 Jun 2024 16:45:55 -0600 Subject: [PATCH 006/108] add overload script (wip) --- MOI.jl | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 MOI.jl diff --git a/MOI.jl b/MOI.jl new file mode 100644 index 00000000..04362561 --- /dev/null +++ b/MOI.jl @@ -0,0 +1,282 @@ +using MathOptInterface +import MathOptInterface: Evaluator, _forward_eval_ϵ +const MOI = MathOptInterface +using MathOptInterface.Nonlinear + +mutable struct MOI.Evaluator{B} <: MOI.AbstractMathOptInterface.NLPEvaluator + # The internal datastructure. + model::Model + # The abstract-differentiation backend + backend::B + # ordered_constraints is needed because `OrderedDict` doesn't support + # looking up a key by the linear index. + ordered_constraints::Vector{ConstraintIndex} + # Storage for the NLPBlockDual, so that we can query the dual of individual + # constraints without needing to query the full vector each time. + constraint_dual::Vector{Float64} + # Timers + initialize_timer::Float64 + eval_objective_timer::Float64 + eval_constraint_timer::Float64 + eval_objective_gradient_timer::Float64 + eval_constraint_gradient_timer::Float64 + eval_constraint_jacobian_timer::Float64 + eval_hessian_objective_timer::Float64 + eval_hessian_constraint_timer::Float64 + eval_hessian_lagrangian_timer::Float64 + parameters_as_variables::Bool + + function MOI.Evaluator( + model::Model, + backend::B = nothing, + ) where {B<:Union{Nothing,MOI.AbstractMathOptInterface.NLPEvaluator}} + return new{B}( + model, + backend, + ConstraintIndex[], + Float64[], + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + false + ) + end +end + +function Evaluator{B}( + # The internal datastructure. + model::Model, + # The abstract-differentiation backend + backend::B, + # ordered_constraints is needed because `OrderedDict` doesn't support + # looking up a key by the linear index. + ordered_constraints::Vector{ConstraintIndex}, + # Storage for the NLPBlockDual, so that we can query the dual of individual + # constraints without needing to query the full vector each time. + constraint_dual::Vector{Float64}, + # Timers + initialize_timer::Float64, + eval_objective_timer::Float64, + eval_constraint_timer::Float64, + eval_objective_gradient_timer::Float64, + eval_constraint_gradient_timer::Float64, + eval_constraint_jacobian_timer::Float64, + eval_hessian_objective_timer::Float64, + eval_hessian_constraint_timer::Float64, + eval_hessian_lagrangian_timer::Float64, +) where {B<:Union{Nothing,MOI.AbstractMathOptInterface.NLPEvaluator}} + return Evaluator{B}( + # The internal datastructure. + model::Model, + # The abstract-differentiation backend + backend::B, + # ordered_constraints is needed because `OrderedDict` doesn't support + # looking up a key by the linear index. + ordered_constraints::Vector{ConstraintIndex}, + # Storage for the NLPBlockDual, so that we can query the dual of individual + # constraints without needing to query the full vector each time. + constraint_dual::Vector{Float64}, + # Timers + initialize_timer::Float64, + eval_objective_timer::Float64, + eval_constraint_timer::Float64, + eval_objective_gradient_timer::Float64, + eval_constraint_gradient_timer::Float64, + eval_constraint_jacobian_timer::Float64, + eval_hessian_objective_timer::Float64, + eval_hessian_constraint_timer::Float64, + eval_hessian_lagrangian_timer::Float64, + false + ) +end + + +function MOI.Nonlinear.ReverseAD._forward_eval_ϵ( + d::MathOptInterface.Nonlinear.NLPEvaluator, + ex::Union{_FunctionStorage,_SubexpressionStorage}, + storage_ϵ::AbstractVector{ForwardDiff.Partials{N,T}}, + partials_storage_ϵ::AbstractVector{ForwardDiff.Partials{N,T}}, + x_values_ϵ, + subexpression_values_ϵ, + user_operators::Nonlinear.OperatorRegistry, +) where {N,T} + @assert length(storage_ϵ) >= length(ex.nodes) + @assert length(partials_storage_ϵ) >= length(ex.nodes) + zero_ϵ = zero(ForwardDiff.Partials{N,T}) + # ex.nodes is already in order such that parents always appear before children + # so a backwards pass through ex.nodes is a forward pass through the tree + children_arr = SparseArrays.rowvals(ex.adj) + for k in length(ex.nodes):-1:1 + node = ex.nodes[k] + partials_storage_ϵ[k] = zero_ϵ + if node.type == Nonlinear.NODE_VARIABLE + storage_ϵ[k] = x_values_ϵ[node.index] + elseif node.type == Nonlinear.NODE_VALUE + storage_ϵ[k] = zero_ϵ + elseif node.type == Nonlinear.NODE_SUBEXPRESSION + storage_ϵ[k] = subexpression_values_ϵ[node.index] + elseif node.type == Nonlinear.NODE_PARAMETER + storage_ϵ[k] = x_values_ϵ[node.index] + # elseif !(d.parameters_as_variables) && node.type == Nonlinear.NODE_PARAMETER + # storage_ϵ[k] = zero_ϵ + else + @assert node.type != Nonlinear.NODE_MOI_VARIABLE + ϵtmp = zero_ϵ + @inbounds children_idx = SparseArrays.nzrange(ex.adj, k) + for c_idx in children_idx + @inbounds ix = children_arr[c_idx] + @inbounds partial = ex.partials_storage[ix] + @inbounds storage_val = storage_ϵ[ix] + # TODO: This "if" statement can take 8% of the hessian + # evaluation time. Find a more efficient way. + if !isfinite(partial) && storage_val == zero_ϵ + continue + end + ϵtmp += storage_val * ex.partials_storage[ix] + end + storage_ϵ[k] = ϵtmp + if node.type == Nonlinear.NODE_CALL_MULTIVARIATE + # TODO(odow): consider how to refactor this into Nonlinear. + op = node.index + n_children = length(children_idx) + if op == 3 # :* + # Lazy approach for now. + anyzero = false + tmp_prod = one(ForwardDiff.Dual{TAG,T,N}) + for c_idx in children_idx + ix = children_arr[c_idx] + sval = ex.forward_storage[ix] + gnum = ForwardDiff.Dual{TAG}(sval, storage_ϵ[ix]) + tmp_prod *= gnum + anyzero = ifelse(sval * sval == zero(T), true, anyzero) + end + # By a quirk of floating-point numbers, we can have + # anyzero == true && ForwardDiff.value(tmp_prod) != zero(T) + if anyzero || n_children <= 2 + for c_idx in children_idx + prod_others = one(ForwardDiff.Dual{TAG,T,N}) + for c_idx2 in children_idx + (c_idx == c_idx2) && continue + ix = children_arr[c_idx2] + gnum = ForwardDiff.Dual{TAG}( + ex.forward_storage[ix], + storage_ϵ[ix], + ) + prod_others *= gnum + end + partials_storage_ϵ[children_arr[c_idx]] = + ForwardDiff.partials(prod_others) + end + else + for c_idx in children_idx + ix = children_arr[c_idx] + prod_others = + tmp_prod / ForwardDiff.Dual{TAG}( + ex.forward_storage[ix], + storage_ϵ[ix], + ) + partials_storage_ϵ[ix] = + ForwardDiff.partials(prod_others) + end + end + elseif op == 4 # :^ + @assert n_children == 2 + idx1 = first(children_idx) + idx2 = last(children_idx) + @inbounds ix1 = children_arr[idx1] + @inbounds ix2 = children_arr[idx2] + @inbounds base = ex.forward_storage[ix1] + @inbounds base_ϵ = storage_ϵ[ix1] + @inbounds exponent = ex.forward_storage[ix2] + @inbounds exponent_ϵ = storage_ϵ[ix2] + base_gnum = ForwardDiff.Dual{TAG}(base, base_ϵ) + exponent_gnum = ForwardDiff.Dual{TAG}(exponent, exponent_ϵ) + if exponent == 2 + partials_storage_ϵ[ix1] = 2 * base_ϵ + elseif exponent == 1 + partials_storage_ϵ[ix1] = zero_ϵ + else + partials_storage_ϵ[ix1] = ForwardDiff.partials( + exponent_gnum * pow(base_gnum, exponent_gnum - 1), + ) + end + result_gnum = ForwardDiff.Dual{TAG}( + ex.forward_storage[k], + storage_ϵ[k], + ) + # TODO(odow): fix me to use NaNMath.jl instead + log_base_gnum = base_gnum < 0 ? NaN : log(base_gnum) + partials_storage_ϵ[ix2] = + ForwardDiff.partials(result_gnum * log_base_gnum) + elseif op == 5 # :/ + @assert n_children == 2 + idx1 = first(children_idx) + idx2 = last(children_idx) + @inbounds ix1 = children_arr[idx1] + @inbounds ix2 = children_arr[idx2] + @inbounds numerator = ex.forward_storage[ix1] + @inbounds numerator_ϵ = storage_ϵ[ix1] + @inbounds denominator = ex.forward_storage[ix2] + @inbounds denominator_ϵ = storage_ϵ[ix2] + recip_denominator = + 1 / ForwardDiff.Dual{TAG}(denominator, denominator_ϵ) + partials_storage_ϵ[ix1] = + ForwardDiff.partials(recip_denominator) + partials_storage_ϵ[ix2] = ForwardDiff.partials( + -ForwardDiff.Dual{TAG}(numerator, numerator_ϵ) * + recip_denominator * + recip_denominator, + ) + elseif op > 6 + f_input = _UnsafeVectorView(d.jac_storage, n_children) + for (i, c) in enumerate(children_idx) + f_input[i] = ex.forward_storage[children_arr[c]] + end + H = _UnsafeLowerTriangularMatrixView( + d.user_output_buffer, + n_children, + ) + has_hessian = Nonlinear.eval_multivariate_hessian( + user_operators, + user_operators.multivariate_operators[node.index], + H, + f_input, + ) + # This might be `false` if we extend this code to all + # multivariate functions. + @assert has_hessian + for col in 1:n_children + dual = zero(ForwardDiff.Partials{N,T}) + for row in 1:n_children + # Make sure we get the lower-triangular component. + h = row >= col ? H[row, col] : H[col, row] + # Performance optimization: hessians can be quite + # sparse + if !iszero(h) + i = children_arr[children_idx[row]] + dual += h * storage_ϵ[i] + end + end + i = children_arr[children_idx[col]] + partials_storage_ϵ[i] = dual + end + end + elseif node.type == Nonlinear.NODE_CALL_UNIVARIATE + @inbounds child_idx = children_arr[ex.adj.colptr[k]] + f′′ = Nonlinear.eval_univariate_hessian( + user_operators, + user_operators.univariate_operators[node.index], + ex.forward_storage[child_idx], + ) + partials_storage_ϵ[child_idx] = f′′ * storage_ϵ[child_idx] + end + end + end + return storage_ϵ[1] +end \ No newline at end of file From 540b7927914de525affece88e671e023f531fdde Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 10:10:41 -0600 Subject: [PATCH 007/108] start nlp change --- MOI.jl | 282 --------------------------------------------- jump_copy.jl | 133 +++++++++++++++++++++ testing_barrier.jl | 39 ++++--- 3 files changed, 155 insertions(+), 299 deletions(-) delete mode 100644 MOI.jl create mode 100644 jump_copy.jl diff --git a/MOI.jl b/MOI.jl deleted file mode 100644 index 04362561..00000000 --- a/MOI.jl +++ /dev/null @@ -1,282 +0,0 @@ -using MathOptInterface -import MathOptInterface: Evaluator, _forward_eval_ϵ -const MOI = MathOptInterface -using MathOptInterface.Nonlinear - -mutable struct MOI.Evaluator{B} <: MOI.AbstractMathOptInterface.NLPEvaluator - # The internal datastructure. - model::Model - # The abstract-differentiation backend - backend::B - # ordered_constraints is needed because `OrderedDict` doesn't support - # looking up a key by the linear index. - ordered_constraints::Vector{ConstraintIndex} - # Storage for the NLPBlockDual, so that we can query the dual of individual - # constraints without needing to query the full vector each time. - constraint_dual::Vector{Float64} - # Timers - initialize_timer::Float64 - eval_objective_timer::Float64 - eval_constraint_timer::Float64 - eval_objective_gradient_timer::Float64 - eval_constraint_gradient_timer::Float64 - eval_constraint_jacobian_timer::Float64 - eval_hessian_objective_timer::Float64 - eval_hessian_constraint_timer::Float64 - eval_hessian_lagrangian_timer::Float64 - parameters_as_variables::Bool - - function MOI.Evaluator( - model::Model, - backend::B = nothing, - ) where {B<:Union{Nothing,MOI.AbstractMathOptInterface.NLPEvaluator}} - return new{B}( - model, - backend, - ConstraintIndex[], - Float64[], - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - false - ) - end -end - -function Evaluator{B}( - # The internal datastructure. - model::Model, - # The abstract-differentiation backend - backend::B, - # ordered_constraints is needed because `OrderedDict` doesn't support - # looking up a key by the linear index. - ordered_constraints::Vector{ConstraintIndex}, - # Storage for the NLPBlockDual, so that we can query the dual of individual - # constraints without needing to query the full vector each time. - constraint_dual::Vector{Float64}, - # Timers - initialize_timer::Float64, - eval_objective_timer::Float64, - eval_constraint_timer::Float64, - eval_objective_gradient_timer::Float64, - eval_constraint_gradient_timer::Float64, - eval_constraint_jacobian_timer::Float64, - eval_hessian_objective_timer::Float64, - eval_hessian_constraint_timer::Float64, - eval_hessian_lagrangian_timer::Float64, -) where {B<:Union{Nothing,MOI.AbstractMathOptInterface.NLPEvaluator}} - return Evaluator{B}( - # The internal datastructure. - model::Model, - # The abstract-differentiation backend - backend::B, - # ordered_constraints is needed because `OrderedDict` doesn't support - # looking up a key by the linear index. - ordered_constraints::Vector{ConstraintIndex}, - # Storage for the NLPBlockDual, so that we can query the dual of individual - # constraints without needing to query the full vector each time. - constraint_dual::Vector{Float64}, - # Timers - initialize_timer::Float64, - eval_objective_timer::Float64, - eval_constraint_timer::Float64, - eval_objective_gradient_timer::Float64, - eval_constraint_gradient_timer::Float64, - eval_constraint_jacobian_timer::Float64, - eval_hessian_objective_timer::Float64, - eval_hessian_constraint_timer::Float64, - eval_hessian_lagrangian_timer::Float64, - false - ) -end - - -function MOI.Nonlinear.ReverseAD._forward_eval_ϵ( - d::MathOptInterface.Nonlinear.NLPEvaluator, - ex::Union{_FunctionStorage,_SubexpressionStorage}, - storage_ϵ::AbstractVector{ForwardDiff.Partials{N,T}}, - partials_storage_ϵ::AbstractVector{ForwardDiff.Partials{N,T}}, - x_values_ϵ, - subexpression_values_ϵ, - user_operators::Nonlinear.OperatorRegistry, -) where {N,T} - @assert length(storage_ϵ) >= length(ex.nodes) - @assert length(partials_storage_ϵ) >= length(ex.nodes) - zero_ϵ = zero(ForwardDiff.Partials{N,T}) - # ex.nodes is already in order such that parents always appear before children - # so a backwards pass through ex.nodes is a forward pass through the tree - children_arr = SparseArrays.rowvals(ex.adj) - for k in length(ex.nodes):-1:1 - node = ex.nodes[k] - partials_storage_ϵ[k] = zero_ϵ - if node.type == Nonlinear.NODE_VARIABLE - storage_ϵ[k] = x_values_ϵ[node.index] - elseif node.type == Nonlinear.NODE_VALUE - storage_ϵ[k] = zero_ϵ - elseif node.type == Nonlinear.NODE_SUBEXPRESSION - storage_ϵ[k] = subexpression_values_ϵ[node.index] - elseif node.type == Nonlinear.NODE_PARAMETER - storage_ϵ[k] = x_values_ϵ[node.index] - # elseif !(d.parameters_as_variables) && node.type == Nonlinear.NODE_PARAMETER - # storage_ϵ[k] = zero_ϵ - else - @assert node.type != Nonlinear.NODE_MOI_VARIABLE - ϵtmp = zero_ϵ - @inbounds children_idx = SparseArrays.nzrange(ex.adj, k) - for c_idx in children_idx - @inbounds ix = children_arr[c_idx] - @inbounds partial = ex.partials_storage[ix] - @inbounds storage_val = storage_ϵ[ix] - # TODO: This "if" statement can take 8% of the hessian - # evaluation time. Find a more efficient way. - if !isfinite(partial) && storage_val == zero_ϵ - continue - end - ϵtmp += storage_val * ex.partials_storage[ix] - end - storage_ϵ[k] = ϵtmp - if node.type == Nonlinear.NODE_CALL_MULTIVARIATE - # TODO(odow): consider how to refactor this into Nonlinear. - op = node.index - n_children = length(children_idx) - if op == 3 # :* - # Lazy approach for now. - anyzero = false - tmp_prod = one(ForwardDiff.Dual{TAG,T,N}) - for c_idx in children_idx - ix = children_arr[c_idx] - sval = ex.forward_storage[ix] - gnum = ForwardDiff.Dual{TAG}(sval, storage_ϵ[ix]) - tmp_prod *= gnum - anyzero = ifelse(sval * sval == zero(T), true, anyzero) - end - # By a quirk of floating-point numbers, we can have - # anyzero == true && ForwardDiff.value(tmp_prod) != zero(T) - if anyzero || n_children <= 2 - for c_idx in children_idx - prod_others = one(ForwardDiff.Dual{TAG,T,N}) - for c_idx2 in children_idx - (c_idx == c_idx2) && continue - ix = children_arr[c_idx2] - gnum = ForwardDiff.Dual{TAG}( - ex.forward_storage[ix], - storage_ϵ[ix], - ) - prod_others *= gnum - end - partials_storage_ϵ[children_arr[c_idx]] = - ForwardDiff.partials(prod_others) - end - else - for c_idx in children_idx - ix = children_arr[c_idx] - prod_others = - tmp_prod / ForwardDiff.Dual{TAG}( - ex.forward_storage[ix], - storage_ϵ[ix], - ) - partials_storage_ϵ[ix] = - ForwardDiff.partials(prod_others) - end - end - elseif op == 4 # :^ - @assert n_children == 2 - idx1 = first(children_idx) - idx2 = last(children_idx) - @inbounds ix1 = children_arr[idx1] - @inbounds ix2 = children_arr[idx2] - @inbounds base = ex.forward_storage[ix1] - @inbounds base_ϵ = storage_ϵ[ix1] - @inbounds exponent = ex.forward_storage[ix2] - @inbounds exponent_ϵ = storage_ϵ[ix2] - base_gnum = ForwardDiff.Dual{TAG}(base, base_ϵ) - exponent_gnum = ForwardDiff.Dual{TAG}(exponent, exponent_ϵ) - if exponent == 2 - partials_storage_ϵ[ix1] = 2 * base_ϵ - elseif exponent == 1 - partials_storage_ϵ[ix1] = zero_ϵ - else - partials_storage_ϵ[ix1] = ForwardDiff.partials( - exponent_gnum * pow(base_gnum, exponent_gnum - 1), - ) - end - result_gnum = ForwardDiff.Dual{TAG}( - ex.forward_storage[k], - storage_ϵ[k], - ) - # TODO(odow): fix me to use NaNMath.jl instead - log_base_gnum = base_gnum < 0 ? NaN : log(base_gnum) - partials_storage_ϵ[ix2] = - ForwardDiff.partials(result_gnum * log_base_gnum) - elseif op == 5 # :/ - @assert n_children == 2 - idx1 = first(children_idx) - idx2 = last(children_idx) - @inbounds ix1 = children_arr[idx1] - @inbounds ix2 = children_arr[idx2] - @inbounds numerator = ex.forward_storage[ix1] - @inbounds numerator_ϵ = storage_ϵ[ix1] - @inbounds denominator = ex.forward_storage[ix2] - @inbounds denominator_ϵ = storage_ϵ[ix2] - recip_denominator = - 1 / ForwardDiff.Dual{TAG}(denominator, denominator_ϵ) - partials_storage_ϵ[ix1] = - ForwardDiff.partials(recip_denominator) - partials_storage_ϵ[ix2] = ForwardDiff.partials( - -ForwardDiff.Dual{TAG}(numerator, numerator_ϵ) * - recip_denominator * - recip_denominator, - ) - elseif op > 6 - f_input = _UnsafeVectorView(d.jac_storage, n_children) - for (i, c) in enumerate(children_idx) - f_input[i] = ex.forward_storage[children_arr[c]] - end - H = _UnsafeLowerTriangularMatrixView( - d.user_output_buffer, - n_children, - ) - has_hessian = Nonlinear.eval_multivariate_hessian( - user_operators, - user_operators.multivariate_operators[node.index], - H, - f_input, - ) - # This might be `false` if we extend this code to all - # multivariate functions. - @assert has_hessian - for col in 1:n_children - dual = zero(ForwardDiff.Partials{N,T}) - for row in 1:n_children - # Make sure we get the lower-triangular component. - h = row >= col ? H[row, col] : H[col, row] - # Performance optimization: hessians can be quite - # sparse - if !iszero(h) - i = children_arr[children_idx[row]] - dual += h * storage_ϵ[i] - end - end - i = children_arr[children_idx[col]] - partials_storage_ϵ[i] = dual - end - end - elseif node.type == Nonlinear.NODE_CALL_UNIVARIATE - @inbounds child_idx = children_arr[ex.adj.colptr[k]] - f′′ = Nonlinear.eval_univariate_hessian( - user_operators, - user_operators.univariate_operators[node.index], - ex.forward_storage[child_idx], - ) - partials_storage_ϵ[child_idx] = f′′ * storage_ϵ[child_idx] - end - end - end - return storage_ϵ[1] -end \ No newline at end of file diff --git a/jump_copy.jl b/jump_copy.jl new file mode 100644 index 00000000..6b5379db --- /dev/null +++ b/jump_copy.jl @@ -0,0 +1,133 @@ +using JuMP.MOI +function add_par_model_vars!(model::JuMP.Model, par_model::JuMP.Model, var_src_to_dest::Dict{VariableRef, VariableRef}) + allvars = all_variables(par_model) + x = @variable(model, [1:length(allvars)]) + for (src, dest) in zip(allvars, x) + var_src_to_dest[src] = dest + JuMP.set_name(dest, JuMP.name(src)) + end + return var_src_to_dest +end + + +function copy_and_replace_variables( + src::Vector, + map::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) + return copy_and_replace_variables.(src, Ref(map)) +end + +function copy_and_replace_variables( + src::Real, + ::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) + return src +end + +function copy_and_replace_variables( + src::JuMP.VariableRef, + src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) + return src_to_dest_variable[src] +end + +function copy_and_replace_variables( + src::JuMP.GenericAffExpr, + src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) + return JuMP.GenericAffExpr( + src.constant, + Pair{VariableRef,Float64}[ + src_to_dest_variable[key] => val for (key, val) in src.terms + ], + ) +end + +function copy_and_replace_variables( + src::JuMP.GenericQuadExpr, + src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) + return JuMP.GenericQuadExpr( + copy_and_replace_variables(src.aff, src_to_dest_variable), + Pair{UnorderedPair{VariableRef},Float64}[ + UnorderedPair{VariableRef}( + src_to_dest_variable[pair.a], + src_to_dest_variable[pair.b], + ) => coef for (pair, coef) in src.terms + ], + ) +end + +function copy_and_replace_variables( + src::JuMP.GenericNonlinearExpr{V}, + src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) where {V} + num_args = length(src.args) + args = Vector{Any}(undef, num_args) + for i = 1:num_args + args[i] = copy_and_replace_variables(src.args[i], src_to_dest_variable) + end + + return @expression(owner_model(first(src_to_dest_variable)[2]), eval(src.head)(args...)) +end + +function copy_and_replace_variables( + src::Any, + ::Dict{JuMP.VariableRef,JuMP.VariableRef}, +) + return error( + "`copy_and_replace_variables` is not implemented for functions like `$(src)`.", + ) +end + +function create_constraint(model, obj, var_src_to_dest) + new_func = copy_and_replace_variables(obj.func, var_src_to_dest) + return @constraint(model, new_func in obj.set) +end + +function create_constraint(model, obj::ScalarConstraint{NonlinearExpr, MOI.EqualTo{Float64}}, var_src_to_dest) + new_func = copy_and_replace_variables(obj.func, var_src_to_dest) + return @constraint(model, new_func == obj.set.value) +end + +function create_constraint(model, obj::ScalarConstraint{NonlinearExpr, MOI.LessThan{Float64}}, var_src_to_dest) + new_func = copy_and_replace_variables(obj.func, var_src_to_dest) + return @constraint(model, new_func <= obj.set.upper) +end + +function create_constraint(model, obj::ScalarConstraint{NonlinearExpr, MOI.GreaterThan{Float64}}, var_src_to_dest) + new_func = copy_and_replace_variables(obj.func, var_src_to_dest) + return @constraint(model, new_func >= obj.set.lower) +end + +function add_child_model_exps!(model::JuMP.Model, par_model::JuMP.Model, var_src_to_dest::Dict{VariableRef, VariableRef}) + # Add constraints: + cons_to_cons = Dict() + for (F, S) in JuMP.list_of_constraint_types(par_model) + S <: MathOptInterface.Parameter && continue + for con in JuMP.all_constraints(par_model, F, S) + obj = JuMP.constraint_object(con) + cons_to_cons[con] = create_constraint(model, obj, var_src_to_dest) + end + end + # Add objective: + current = JuMP.objective_function(model) + par_model_objective = + copy_and_replace_variables(JuMP.objective_function(par_model), var_src_to_dest) + JuMP.set_objective_function( + model, + current + par_model_objective, + ) + return cons_to_cons +end + + +function copy_jump_no_parameters(par_model::JuMP.Model, model = JuMP.Model()) + set_objective_sense(model, objective_sense(par_model)) + var_src_to_dest = Dict{VariableRef, VariableRef}() + add_par_model_vars!(model, par_model, var_src_to_dest) + + cons_to_cons = add_child_model_exps!(model, par_model, var_src_to_dest) + + return model, var_src_to_dest, cons_to_cons +end \ No newline at end of file diff --git a/testing_barrier.jl b/testing_barrier.jl index 60eb05f4..ed2db531 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -9,30 +9,32 @@ using Ipopt # using MadNLP # using KNITRO +include("jump_copy.jl") + ############ # Test Case ############ # Define the problem -model = Model(Ipopt.Optimizer) +par_model = Model(Ipopt.Optimizer) # Parameters -@variable(model, p ∈ MOI.Parameter(1.0)) -@variable(model, p2 ∈ MOI.Parameter(2.0)) +@variable(par_model, p ∈ MOI.Parameter(1.0)) +@variable(par_model, p2 ∈ MOI.Parameter(2.0)) # Variables -@variable(model, x) -@variable(model, y) +@variable(par_model, x) +@variable(par_model, y) # Constraints -@constraint(model, con1, y >= p*sin(x)) # NLP Constraint -@constraint(model, con2, x + y == p) -@constraint(model, con3, p2 * x >= 0.1) -@objective(model, Min, (1 - x)^2 + 100 * (y - x^2)^2) # NLP Objective -optimize!(model) +@constraint(par_model, con1, y >= p*sin(x)) # NLP Constraint +@constraint(par_model, con2, x + y == p) +@constraint(par_model, con3, p2 * x >= 0.1) +@objective(par_model, Min, (1 - x)^2 + 100 * (y - x^2)^2) # NLP Objective +optimize!(par_model) # Check local optimality -termination_status(model) +termination_status(par_model) ############ # Retrieve important quantities @@ -61,12 +63,15 @@ dual_values = dual.([con1; con2; con3]) num_vars = length(primal_values) num_cons = length(dual_values) +# create copy with no parameters +model, var_src_to_dest, cons_to_cons = copy_jump_no_parameters(par_model) + # `Evaluator`: Object that helps evaluating functions and calculating related values (Hessian, Jacobian, ...) -evaluator = JuMP.MOI.Nonlinear.Evaluator(model.moi_backend.optimizer.model.nlp_model, JuMP.MOI.Nonlinear.SparseReverseMode(), - [ model.moi_backend.model_to_optimizer_map[index(x)], - model.moi_backend.model_to_optimizer_map[index(y)], - model.moi_backend.model_to_optimizer_map[index(p)], - model.moi_backend.model_to_optimizer_map[index(p2)], +evaluator = JuMP.MOI.Nonlinear.Evaluator(nonlinear_model(model; force=true), JuMP.MOI.Nonlinear.SparseReverseMode(), + [ index(var_src_to_dest[x]), + index(var_src_to_dest[y]), + index(var_src_to_dest[p]), + index(var_src_to_dest[p2]), ] ) @@ -77,7 +82,7 @@ MOI.initialize(evaluator, [:Grad, :Jac, :Hess, :JacVec]) hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) W = fill(NaN, length(hessian_sparsity)) -# Modify H with the values for the hessian of the lagrangian +# Modify W with the values for the hessian of the lagrangian MOI.eval_hessian_lagrangian(evaluator, W, primal_values, 1.0, dual_values) W = _dense_hessian(hessian_sparsity, W, num_vars) From a9450fab8ac70554b087e6b435d34e98fcfc082e Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 12:16:28 -0600 Subject: [PATCH 008/108] add full hessian and jacobian --- jump_copy.jl | 133 --------------------------------------------- nlp_utilities.jl | 84 ++++++++++++++++++++++++++++ testing_barrier.jl | 97 +++++++++++---------------------- 3 files changed, 116 insertions(+), 198 deletions(-) delete mode 100644 jump_copy.jl create mode 100644 nlp_utilities.jl diff --git a/jump_copy.jl b/jump_copy.jl deleted file mode 100644 index 6b5379db..00000000 --- a/jump_copy.jl +++ /dev/null @@ -1,133 +0,0 @@ -using JuMP.MOI -function add_par_model_vars!(model::JuMP.Model, par_model::JuMP.Model, var_src_to_dest::Dict{VariableRef, VariableRef}) - allvars = all_variables(par_model) - x = @variable(model, [1:length(allvars)]) - for (src, dest) in zip(allvars, x) - var_src_to_dest[src] = dest - JuMP.set_name(dest, JuMP.name(src)) - end - return var_src_to_dest -end - - -function copy_and_replace_variables( - src::Vector, - map::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) - return copy_and_replace_variables.(src, Ref(map)) -end - -function copy_and_replace_variables( - src::Real, - ::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) - return src -end - -function copy_and_replace_variables( - src::JuMP.VariableRef, - src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) - return src_to_dest_variable[src] -end - -function copy_and_replace_variables( - src::JuMP.GenericAffExpr, - src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) - return JuMP.GenericAffExpr( - src.constant, - Pair{VariableRef,Float64}[ - src_to_dest_variable[key] => val for (key, val) in src.terms - ], - ) -end - -function copy_and_replace_variables( - src::JuMP.GenericQuadExpr, - src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) - return JuMP.GenericQuadExpr( - copy_and_replace_variables(src.aff, src_to_dest_variable), - Pair{UnorderedPair{VariableRef},Float64}[ - UnorderedPair{VariableRef}( - src_to_dest_variable[pair.a], - src_to_dest_variable[pair.b], - ) => coef for (pair, coef) in src.terms - ], - ) -end - -function copy_and_replace_variables( - src::JuMP.GenericNonlinearExpr{V}, - src_to_dest_variable::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) where {V} - num_args = length(src.args) - args = Vector{Any}(undef, num_args) - for i = 1:num_args - args[i] = copy_and_replace_variables(src.args[i], src_to_dest_variable) - end - - return @expression(owner_model(first(src_to_dest_variable)[2]), eval(src.head)(args...)) -end - -function copy_and_replace_variables( - src::Any, - ::Dict{JuMP.VariableRef,JuMP.VariableRef}, -) - return error( - "`copy_and_replace_variables` is not implemented for functions like `$(src)`.", - ) -end - -function create_constraint(model, obj, var_src_to_dest) - new_func = copy_and_replace_variables(obj.func, var_src_to_dest) - return @constraint(model, new_func in obj.set) -end - -function create_constraint(model, obj::ScalarConstraint{NonlinearExpr, MOI.EqualTo{Float64}}, var_src_to_dest) - new_func = copy_and_replace_variables(obj.func, var_src_to_dest) - return @constraint(model, new_func == obj.set.value) -end - -function create_constraint(model, obj::ScalarConstraint{NonlinearExpr, MOI.LessThan{Float64}}, var_src_to_dest) - new_func = copy_and_replace_variables(obj.func, var_src_to_dest) - return @constraint(model, new_func <= obj.set.upper) -end - -function create_constraint(model, obj::ScalarConstraint{NonlinearExpr, MOI.GreaterThan{Float64}}, var_src_to_dest) - new_func = copy_and_replace_variables(obj.func, var_src_to_dest) - return @constraint(model, new_func >= obj.set.lower) -end - -function add_child_model_exps!(model::JuMP.Model, par_model::JuMP.Model, var_src_to_dest::Dict{VariableRef, VariableRef}) - # Add constraints: - cons_to_cons = Dict() - for (F, S) in JuMP.list_of_constraint_types(par_model) - S <: MathOptInterface.Parameter && continue - for con in JuMP.all_constraints(par_model, F, S) - obj = JuMP.constraint_object(con) - cons_to_cons[con] = create_constraint(model, obj, var_src_to_dest) - end - end - # Add objective: - current = JuMP.objective_function(model) - par_model_objective = - copy_and_replace_variables(JuMP.objective_function(par_model), var_src_to_dest) - JuMP.set_objective_function( - model, - current + par_model_objective, - ) - return cons_to_cons -end - - -function copy_jump_no_parameters(par_model::JuMP.Model, model = JuMP.Model()) - set_objective_sense(model, objective_sense(par_model)) - var_src_to_dest = Dict{VariableRef, VariableRef}() - add_par_model_vars!(model, par_model, var_src_to_dest) - - cons_to_cons = add_child_model_exps!(model, par_model, var_src_to_dest) - - return model, var_src_to_dest, cons_to_cons -end \ No newline at end of file diff --git a/nlp_utilities.jl b/nlp_utilities.jl new file mode 100644 index 00000000..6bf42a00 --- /dev/null +++ b/nlp_utilities.jl @@ -0,0 +1,84 @@ +using JuMP.MOI +using SparseArrays + +function create_nlp_model(model::JuMP.Model) + rows = Any[] + nlp = MOI.Nonlinear.Model() + for (F, S) in list_of_constraint_types(model) + if F <: VariableRef + continue # Skip variable bounds + end + for ci in all_constraints(model, F, S) + push!(rows, ci) + object = constraint_object(ci) + MOI.Nonlinear.add_constraint(nlp, object.func, object.set) + end + end + MOI.Nonlinear.set_objective(nlp, objective_function(model)) + return nlp, rows +end + +function fill_off_diagonal(H) + ret = H + H' + row_vals = SparseArrays.rowvals(ret) + non_zeros = SparseArrays.nonzeros(ret) + for col in 1:size(ret, 2) + for i in SparseArrays.nzrange(ret, col) + if col == row_vals[i] + non_zeros[i] /= 2 + end + end + end + return ret +end + +function compute_optimal_hessian(model::Model; nlp=nothing, rows=nothing, x=nothing, evaluator=nothing, backend=nothing +) + if isnothing(nlp) + nlp, rows = create_nlp_model(model) + x=all_variables(model), + backend=MOI.Nonlinear.SparseReverseMode() + evaluator = MOI.Nonlinear.Evaluator(nlp, backend, index.(x)) + MOI.initialize(evaluator, [:Hess]) + end + + hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) + I = [i for (i, _) in hessian_sparsity] + J = [j for (_, j) in hessian_sparsity] + V = zeros(length(hessian_sparsity)) + MOI.eval_hessian_lagrangian(evaluator, V, value.(x), 1.0, dual.(rows)) + H = SparseArrays.sparse(I, J, V, length(x), length(x)) + return Matrix(fill_off_diagonal(H)) +end + +function compute_optimal_jacobian(model::Model; nlp=nothing, rows=nothing, x=nothing, evaluator=nothing, backend=nothing +) + if isnothing(nlp) + nlp, rows = create_nlp_model(model) + x=all_variables(model), + backend=MOI.Nonlinear.SparseReverseMode() + evaluator = MOI.Nonlinear.Evaluator(nlp, backend, index.(x)) + MOI.initialize(evaluator, [:Jac]) + end + jacobian_sparsity = MOI.jacobian_structure(evaluator) + I = [i for (i, _) in jacobian_sparsity] + J = [j for (_, j) in jacobian_sparsity] + V = zeros(length(jacobian_sparsity)) + MOI.eval_constraint_jacobian(evaluator, V, value.(x)) + A = SparseArrays.sparse(I, J, V, length(rows), length(x)) + return Matrix(A) +end + +function compute_optimal_hess_jac(model::Model; nlp_rows=create_nlp_model(model), x=all_variables(model), + backend=MOI.Nonlinear.SparseReverseMode(), + evaluator = MOI.Nonlinear.Evaluator(nlp_rows[1], backend, index.(x)) +) + MOI.initialize(evaluator, [:Hess, :Jac]) + hessian = compute_optimal_hessian(model, nlp=nlp_rows[1], rows=nlp_rows[2], x=x, backend=backend, evaluator=evaluator) + jacobian = compute_optimal_jacobian(model, nlp=nlp_rows[1], rows=nlp_rows[2], x=x, backend=backend, evaluator=evaluator) + + return hessian, jacobian, nlp_rows[1], nlp_rows[2] +end + + + diff --git a/testing_barrier.jl b/testing_barrier.jl index ed2db531..2e5b00d7 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -9,7 +9,7 @@ using Ipopt # using MadNLP # using KNITRO -include("jump_copy.jl") +include("nlp_utilities.jl") ############ # Test Case @@ -40,76 +40,43 @@ termination_status(par_model) # Retrieve important quantities ############ -function _dense_hessian(hessian_sparsity, V, n) - I = [i for (i, _) in hessian_sparsity] - J = [j for (_, j) in hessian_sparsity] - raw = SparseArrays.sparse(I, J, V, n, n) - return Matrix( - raw + raw' - - SparseArrays.sparse(LinearAlgebra.diagm(0 => LinearAlgebra.diag(raw))), - ) -end - -function _dense_jacobian(jacobian_sparsity, V, m, n) - I = [i for (i, j) in jacobian_sparsity] - J = [j for (i, j) in jacobian_sparsity] - raw = SparseArrays.sparse(I, J, V, m, n) - return Matrix(raw) -end - -# Primal Solution -primal_values = value.([x, y, p, p2]) -dual_values = dual.([con1; con2; con3]) -num_vars = length(primal_values) -num_cons = length(dual_values) - -# create copy with no parameters -model, var_src_to_dest, cons_to_cons = copy_jump_no_parameters(par_model) - -# `Evaluator`: Object that helps evaluating functions and calculating related values (Hessian, Jacobian, ...) -evaluator = JuMP.MOI.Nonlinear.Evaluator(nonlinear_model(model; force=true), JuMP.MOI.Nonlinear.SparseReverseMode(), - [ index(var_src_to_dest[x]), - index(var_src_to_dest[y]), - index(var_src_to_dest[p]), - index(var_src_to_dest[p2]), - ] -) - -# Define what we will need to evaluate -MOI.initialize(evaluator, [:Grad, :Jac, :Hess, :JacVec]) - -# Hessian "Symetric" structure values - Placeholder that will be modified to during the evaluation of the hessian -hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) -W = fill(NaN, length(hessian_sparsity)) - -# Modify W with the values for the hessian of the lagrangian -MOI.eval_hessian_lagrangian(evaluator, W, primal_values, 1.0, dual_values) -W = _dense_hessian(hessian_sparsity, W, num_vars) - -# Jacobian of the constraints Placeholder -jacobian_sparsity = MOI.jacobian_structure(evaluator) -A = zeros(length(jacobian_sparsity)) - -# Evaluate Jacobian -MOI.eval_constraint_jacobian(evaluator, A, primal_values) -A = _dense_jacobian(jacobian_sparsity, A, num_cons, num_vars) - -# TODO: ∇ₓₚL (Partial second derivative of the lagrangian wrt primal solution and parameters) ; -# TODO: ∇ₚC (partial derivative of the equality constraintswith wrt parameters). +# Primal variables +primal_vars = [x; y] +num_vars = length(primal_vars) +params = [p; p2] +all_vars = [primal_vars; params] + +hessian, jacobian, nlp, cons = compute_optimal_hess_jac(par_model; x=all_vars) + +W = hessian[1:num_vars, 1:num_vars] +A = jacobian[:, 1:num_vars] + +# ∇ₓₚL (Partial second derivative of the lagrangian wrt primal solution and parameters) +∇ₓₚL = hessian[num_vars+1:end, 1:num_vars] +# ∇ₚC (partial derivative of the equality constraintswith wrt parameters). +∇ₚC = jacobian[:, num_vars+1:end] ############ # (WORK IN PROGRESS) - Non working code # Calculate Sensitivity ############ -V = diagm(dual_values) -X = diagm(primal_values) - -M = [ - [W A -I]; - [A' 0 0]; - [V 0 X] -] +num_cons = length(cons) +V = diagm(dual.(LowerBoundRef.(primal_vars))) # dual of the bounds +X = diagm(value.(primal_vars)) + +M = zeros(num_vars * 2 + num_cons, num_vars * 2 + num_cons) + +# M = [ +# [W A' -I]; +# [A 0 0]; +# [V 0 X] +# ] + +M[1:num_vars, 1:num_vars] = W +M[1:num_vars, num_vars+1:num_vars+num_cons] = A' +M[num_vars+1:num_vars+num_cons, 1:num_vars] = A +M[num_vars+num_cons+1:end, 1:num_vars] = V N = [∇ₓₚL ; ∇ₚC] From 71879ba825f0150a16def437033461e6999e3239 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 14:33:08 -0600 Subject: [PATCH 009/108] first try working code --- testing_barrier.jl | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index 2e5b00d7..42db4f44 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -44,6 +44,7 @@ termination_status(par_model) primal_vars = [x; y] num_vars = length(primal_vars) params = [p; p2] +num_parms = length(params) all_vars = [primal_vars; params] hessian, jacobian, nlp, cons = compute_optimal_hess_jac(par_model; x=all_vars) @@ -57,14 +58,24 @@ A = jacobian[:, 1:num_vars] ∇ₚC = jacobian[:, num_vars+1:end] ############ -# (WORK IN PROGRESS) - Non working code - # Calculate Sensitivity ############ num_cons = length(cons) -V = diagm(dual.(LowerBoundRef.(primal_vars))) # dual of the bounds X = diagm(value.(primal_vars)) +# dual of the bounds +bound_duals = zeros(length(primal_vars)) +for i in 1:length(primal_vars) + if has_lower_bound(primal_vars[i]) + bound_duals[i] = dual.(LowerBoundRef(primal_vars[i])) + end + if has_upper_bound(primal_vars[i]) + bound_duals[i] -= dual.(UpperBoundRef(primal_vars[i])) + end +end +V = diagm(bound_duals) + +# M matrix M = zeros(num_vars * 2 + num_cons, num_vars * 2 + num_cons) # M = [ @@ -76,9 +87,11 @@ M = zeros(num_vars * 2 + num_cons, num_vars * 2 + num_cons) M[1:num_vars, 1:num_vars] = W M[1:num_vars, num_vars+1:num_vars+num_cons] = A' M[num_vars+1:num_vars+num_cons, 1:num_vars] = A +M[1:num_vars, num_vars+num_cons+1:end] = -I(num_vars) M[num_vars+num_cons+1:end, 1:num_vars] = V +M[num_vars+num_cons+1:end, num_vars+num_cons+1:end] = X -N = [∇ₓₚL ; ∇ₚC] +N = [∇ₓₚL ; ∇ₚC; zeros(num_vars, num_parms)] # sesitivity of the solution (primal-dual) with respect to the parameter -∂s = inv(M) * N \ No newline at end of file +∂s = pinv(M) * N \ No newline at end of file From c175d52bf511d3719053b26c8064136f31dc5f87 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 14:52:28 -0600 Subject: [PATCH 010/108] comment --- testing_barrier.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index 42db4f44..263e207c 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -93,5 +93,5 @@ M[num_vars+num_cons+1:end, num_vars+num_cons+1:end] = X N = [∇ₓₚL ; ∇ₚC; zeros(num_vars, num_parms)] -# sesitivity of the solution (primal-dual) with respect to the parameter +# sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters ∂s = pinv(M) * N \ No newline at end of file From c026c99a235f3f116496095c0c5fd1361dbfc7d5 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 15:33:05 -0600 Subject: [PATCH 011/108] update code --- nlp_utilities.jl | 104 +++++++++++++++++++++++++++++++++------------ testing_barrier.jl | 58 +------------------------ 2 files changed, 79 insertions(+), 83 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 6bf42a00..a6d361bf 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -32,16 +32,7 @@ function fill_off_diagonal(H) return ret end -function compute_optimal_hessian(model::Model; nlp=nothing, rows=nothing, x=nothing, evaluator=nothing, backend=nothing -) - if isnothing(nlp) - nlp, rows = create_nlp_model(model) - x=all_variables(model), - backend=MOI.Nonlinear.SparseReverseMode() - evaluator = MOI.Nonlinear.Evaluator(nlp, backend, index.(x)) - MOI.initialize(evaluator, [:Hess]) - end - +function compute_optimal_hessian(evaluator, rows, x) hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) I = [i for (i, _) in hessian_sparsity] J = [j for (_, j) in hessian_sparsity] @@ -51,15 +42,7 @@ function compute_optimal_hessian(model::Model; nlp=nothing, rows=nothing, x=noth return Matrix(fill_off_diagonal(H)) end -function compute_optimal_jacobian(model::Model; nlp=nothing, rows=nothing, x=nothing, evaluator=nothing, backend=nothing -) - if isnothing(nlp) - nlp, rows = create_nlp_model(model) - x=all_variables(model), - backend=MOI.Nonlinear.SparseReverseMode() - evaluator = MOI.Nonlinear.Evaluator(nlp, backend, index.(x)) - MOI.initialize(evaluator, [:Jac]) - end +function compute_optimal_jacobian(evaluator, rows, x) jacobian_sparsity = MOI.jacobian_structure(evaluator) I = [i for (i, _) in jacobian_sparsity] J = [j for (_, j) in jacobian_sparsity] @@ -69,16 +52,83 @@ function compute_optimal_jacobian(model::Model; nlp=nothing, rows=nothing, x=not return Matrix(A) end -function compute_optimal_hess_jac(model::Model; nlp_rows=create_nlp_model(model), x=all_variables(model), - backend=MOI.Nonlinear.SparseReverseMode(), - evaluator = MOI.Nonlinear.Evaluator(nlp_rows[1], backend, index.(x)) -) - MOI.initialize(evaluator, [:Hess, :Jac]) - hessian = compute_optimal_hessian(model, nlp=nlp_rows[1], rows=nlp_rows[2], x=x, backend=backend, evaluator=evaluator) - jacobian = compute_optimal_jacobian(model, nlp=nlp_rows[1], rows=nlp_rows[2], x=x, backend=backend, evaluator=evaluator) +function compute_optimal_hess_jac(evaluator, rows, x) + hessian = compute_optimal_hessian(evaluator, rows, x) + jacobian = compute_optimal_jacobian(evaluator, rows, x) - return hessian, jacobian, nlp_rows[1], nlp_rows[2] + return hessian, jacobian end +all_primal_vars(model::Model) = filter(x -> !is_parameter(x), all_variables(model)) +all_params(model::Model) = filter(x -> is_parameter(x), all_variables(model)) +function create_evaluator(model::Model; x=all_variables(model)) + nlp, rows = create_nlp_model(model) + backend = MOI.Nonlinear.SparseReverseMode() + evaluator = MOI.Nonlinear.Evaluator(nlp, backend, index.(x)) + MOI.initialize(evaluator, [:Hess, :Jac]) + return evaluator, rows +end + +function compute_derivatives(evaluator, cons; primal_vars=all_primal_vars(model), params=all_params(model) +) + # Setting + num_vars = length(primal_vars) + num_parms = length(params) + num_cons = length(cons) + all_vars = [primal_vars; params] + + # Primal solution + X = diagm(value.(primal_vars)) + # Dual of the bounds + bound_duals = zeros(length(primal_vars)) + for i in 1:length(primal_vars) + if has_lower_bound(primal_vars[i]) + bound_duals[i] = dual.(LowerBoundRef(primal_vars[i])) + end + if has_upper_bound(primal_vars[i]) + bound_duals[i] -= dual.(UpperBoundRef(primal_vars[i])) + end + end + V = diagm(bound_duals) + + # Function Derivatives + hessian, jacobian = compute_optimal_hess_jac(evaluator, cons, all_vars) + + # Hessian of the lagrangian wrt the primal variables + W = hessian[1:num_vars, 1:num_vars] + # Jacobian of the constraints wrt the primal variables + A = jacobian[:, 1:num_vars] + # Partial second derivative of the lagrangian wrt primal solution and parameters + ∇ₓₚL = hessian[num_vars+1:end, 1:num_vars] + # Partial derivative of the equality constraintswith wrt parameters + ∇ₚC = jacobian[:, num_vars+1:end] + + # M matrix + M = zeros(num_vars * 2 + num_cons, num_vars * 2 + num_cons) + + # M = [ + # [W A' -I]; + # [A 0 0]; + # [V 0 X] + # ] + + M[1:num_vars, 1:num_vars] = W + M[1:num_vars, num_vars+1:num_vars+num_cons] = A' + M[num_vars+1:num_vars+num_cons, 1:num_vars] = A + M[1:num_vars, num_vars+num_cons+1:end] = -I(num_vars) + M[num_vars+num_cons+1:end, 1:num_vars] = V + M[num_vars+num_cons+1:end, num_vars+num_cons+1:end] = X + + # N matrix + N = [∇ₓₚL ; ∇ₚC; zeros(num_vars, num_parms)] + + # sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters + return pinv(M) * N +end + +function compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) + evaluator, rows = create_evaluator(model) + return compute_derivatives(evaluator, rows; primal_vars=primal_vars, params=params), evaluator, rows +end diff --git a/testing_barrier.jl b/testing_barrier.jl index 263e207c..aef01ebb 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -37,61 +37,7 @@ optimize!(par_model) termination_status(par_model) ############ -# Retrieve important quantities +# Sensitivity Analysis ############ -# Primal variables -primal_vars = [x; y] -num_vars = length(primal_vars) -params = [p; p2] -num_parms = length(params) -all_vars = [primal_vars; params] - -hessian, jacobian, nlp, cons = compute_optimal_hess_jac(par_model; x=all_vars) - -W = hessian[1:num_vars, 1:num_vars] -A = jacobian[:, 1:num_vars] - -# ∇ₓₚL (Partial second derivative of the lagrangian wrt primal solution and parameters) -∇ₓₚL = hessian[num_vars+1:end, 1:num_vars] -# ∇ₚC (partial derivative of the equality constraintswith wrt parameters). -∇ₚC = jacobian[:, num_vars+1:end] - -############ -# Calculate Sensitivity -############ -num_cons = length(cons) -X = diagm(value.(primal_vars)) - -# dual of the bounds -bound_duals = zeros(length(primal_vars)) -for i in 1:length(primal_vars) - if has_lower_bound(primal_vars[i]) - bound_duals[i] = dual.(LowerBoundRef(primal_vars[i])) - end - if has_upper_bound(primal_vars[i]) - bound_duals[i] -= dual.(UpperBoundRef(primal_vars[i])) - end -end -V = diagm(bound_duals) - -# M matrix -M = zeros(num_vars * 2 + num_cons, num_vars * 2 + num_cons) - -# M = [ -# [W A' -I]; -# [A 0 0]; -# [V 0 X] -# ] - -M[1:num_vars, 1:num_vars] = W -M[1:num_vars, num_vars+1:num_vars+num_cons] = A' -M[num_vars+1:num_vars+num_cons, 1:num_vars] = A -M[1:num_vars, num_vars+num_cons+1:end] = -I(num_vars) -M[num_vars+num_cons+1:end, 1:num_vars] = V -M[num_vars+num_cons+1:end, num_vars+num_cons+1:end] = X - -N = [∇ₓₚL ; ∇ₚC; zeros(num_vars, num_parms)] - -# sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters -∂s = pinv(M) * N \ No newline at end of file +∂s, evaluator, rows = compute_derivatives(par_model) From d163dfa4792e2b8482bce6c8690429bb2fdd2f9b Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 15:43:56 -0600 Subject: [PATCH 012/108] add comments --- nlp_utilities.jl | 62 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index a6d361bf..1c536cdd 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -1,6 +1,11 @@ using JuMP.MOI using SparseArrays +""" + create_nlp_model(model::JuMP.Model) + +Create a Nonlinear Programming (NLP) model from a JuMP model. +""" function create_nlp_model(model::JuMP.Model) rows = Any[] nlp = MOI.Nonlinear.Model() @@ -18,6 +23,11 @@ function create_nlp_model(model::JuMP.Model) return nlp, rows end +""" + fill_off_diagonal(H) + +Filling the off-diagonal elements of a sparse matrix to make it symmetric. +""" function fill_off_diagonal(H) ret = H + H' row_vals = SparseArrays.rowvals(ret) @@ -32,7 +42,12 @@ function fill_off_diagonal(H) return ret end -function compute_optimal_hessian(evaluator, rows, x) +""" + compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) + +Compute the optimal Hessian of the Lagrangian. +""" +function compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) I = [i for (i, _) in hessian_sparsity] J = [j for (_, j) in hessian_sparsity] @@ -42,7 +57,12 @@ function compute_optimal_hessian(evaluator, rows, x) return Matrix(fill_off_diagonal(H)) end -function compute_optimal_jacobian(evaluator, rows, x) +""" + compute_optimal_jacobian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) + +Compute the optimal Jacobian of the constraints. +""" +function compute_optimal_jacobian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) jacobian_sparsity = MOI.jacobian_structure(evaluator) I = [i for (i, _) in jacobian_sparsity] J = [j for (_, j) in jacobian_sparsity] @@ -52,16 +72,37 @@ function compute_optimal_jacobian(evaluator, rows, x) return Matrix(A) end -function compute_optimal_hess_jac(evaluator, rows, x) +""" + compute_optimal_hess_jac(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) + +Compute the optimal Hessian of the Lagrangian and Jacobian of the constraints. +""" +function compute_optimal_hess_jac(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) hessian = compute_optimal_hessian(evaluator, rows, x) jacobian = compute_optimal_jacobian(evaluator, rows, x) return hessian, jacobian end +""" + all_primal_vars(model::Model) + +Get all the primal variables in the model. +""" all_primal_vars(model::Model) = filter(x -> !is_parameter(x), all_variables(model)) + +""" + all_params(model::Model) + +Get all the parameters in the model. +""" all_params(model::Model) = filter(x -> is_parameter(x), all_variables(model)) +""" + create_evaluator(model::Model; x=all_variables(model)) + +Create an evaluator for the model. +""" function create_evaluator(model::Model; x=all_variables(model)) nlp, rows = create_nlp_model(model) backend = MOI.Nonlinear.SparseReverseMode() @@ -70,8 +111,16 @@ function create_evaluator(model::Model; x=all_variables(model)) return evaluator, rows end -function compute_derivatives(evaluator, cons; primal_vars=all_primal_vars(model), params=all_params(model) +""" + compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model)) + +Compute the derivatives of the solution with respect to the parameters. +""" +function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; + primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model) ) + @assert all(x -> is_parameter(x), params) "All parameters must be parameters" + # Setting num_vars = length(primal_vars) num_parms = length(params) @@ -128,6 +177,11 @@ function compute_derivatives(evaluator, cons; primal_vars=all_primal_vars(model) return pinv(M) * N end +""" + compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) + +Compute the derivatives of the solution with respect to the parameters. +""" function compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) evaluator, rows = create_evaluator(model) return compute_derivatives(evaluator, rows; primal_vars=primal_vars, params=params), evaluator, rows From 223b4b6e06a3071a5ee96f069864aa86a35313e3 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 15:49:08 -0600 Subject: [PATCH 013/108] add TODO tests --- nlp.utilities_test.jl | 5 +++++ testing_barrier.jl | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 nlp.utilities_test.jl diff --git a/nlp.utilities_test.jl b/nlp.utilities_test.jl new file mode 100644 index 00000000..0aa8985b --- /dev/null +++ b/nlp.utilities_test.jl @@ -0,0 +1,5 @@ +# TODO: Test Hessian + +# TODO: Test Jacobian + +# TODO: Test derivatives \ No newline at end of file diff --git a/testing_barrier.jl b/testing_barrier.jl index aef01ebb..e700029d 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -12,7 +12,7 @@ using Ipopt include("nlp_utilities.jl") ############ -# Test Case +# Test Case 1 ############ # Define the problem From 7f057950d2e6f5f48a163976f6ab418b67370b68 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 1 Jul 2024 16:21:28 -0600 Subject: [PATCH 014/108] add todo slack variables --- nlp_utilities.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 1c536cdd..d2609646 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -145,6 +145,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Function Derivatives hessian, jacobian = compute_optimal_hess_jac(evaluator, cons, all_vars) + # TODO: Add appropriate entries for the slack variables: Zeros for the hessian and Identity for the jacobian # Hessian of the lagrangian wrt the primal variables W = hessian[1:num_vars, 1:num_vars] # Jacobian of the constraints wrt the primal variables From 7caec85102c0fccf119196c3f1f25e8eca6409c7 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 2 Jul 2024 09:43:24 -0600 Subject: [PATCH 015/108] add slack variables --- nlp_utilities.jl | 62 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index d2609646..1d82049c 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -111,13 +111,33 @@ function create_evaluator(model::Model; x=all_variables(model)) return evaluator, rows end +function is_inequality(con::ConstraintRef) + set_type = MOI.get(owner_model(con), MOI.ConstraintSet(), con) + return set_type <: MOI.LessThan || set_type <: MOI.GreaterThan +end + +function find_inequealities(cons::Vector{ConstraintRef}) + ineq_locations = zeros(length(cons)) + for i in 1:length(cons) + if is_inequality(cons[i]) + ineq_locations[i] = 1 + end + end + return findall(ineq_locations) +end + +function get_slack_inequality(con::ConstraintRef) + slack = MOI.get(owner_model(con), CanonicalConstraintFunction(), con) + return slack +end + """ compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model)) Compute the derivatives of the solution with respect to the parameters. """ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; - primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model) + primal_vars::Vector{VariableRef}, params::Vector{VariableRef} ) @assert all(x -> is_parameter(x), params) "All parameters must be parameters" @@ -125,14 +145,17 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co num_vars = length(primal_vars) num_parms = length(params) num_cons = length(cons) + ineq_locations = find_inequealities(cons) + num_ineq = length(ineq_locations) + slack_vars = [get_slack_inequality(cons[i]) for i in ineq_locations] all_vars = [primal_vars; params] # Primal solution - X = diagm(value.(primal_vars)) + X = diagm(value.([primal_vars; slack_vars])) # Dual of the bounds - bound_duals = zeros(length(primal_vars)) - for i in 1:length(primal_vars) + bound_duals = zeros(num_vars+num_ineq) + for i in 1:num_vars if has_lower_bound(primal_vars[i]) bound_duals[i] = dual.(LowerBoundRef(primal_vars[i])) end @@ -140,6 +163,9 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co bound_duals[i] -= dual.(UpperBoundRef(primal_vars[i])) end end + for (i, con) in enumerate(cons[ineq_locations]) + bound_duals[num_vars+i] = dual.(con) + end V = diagm(bound_duals) # Function Derivatives @@ -147,16 +173,22 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # TODO: Add appropriate entries for the slack variables: Zeros for the hessian and Identity for the jacobian # Hessian of the lagrangian wrt the primal variables - W = hessian[1:num_vars, 1:num_vars] + W = zeros(num_vars + num_ineq, num_vars + num_ineq) + W[1:num_vars, 1:num_vars] = hessian[1:num_vars, 1:num_vars] # Jacobian of the constraints wrt the primal variables - A = jacobian[:, 1:num_vars] + A = zeros(num_cons, num_vars + num_ineq) + A[:, 1:num_vars] = jacobian[:, 1:num_vars] + for (i,j) in enumerate(ineq_locations) + A[j, num_vars+i] = 1 + end # Partial second derivative of the lagrangian wrt primal solution and parameters - ∇ₓₚL = hessian[num_vars+1:end, 1:num_vars] + ∇ₓₚL = zeros(num_params, num_vars + num_ineq) + ∇ₓₚL[:, 1:num_vars] = hessian[num_vars+1:end, 1:num_vars] # Partial derivative of the equality constraintswith wrt parameters ∇ₚC = jacobian[:, num_vars+1:end] # M matrix - M = zeros(num_vars * 2 + num_cons, num_vars * 2 + num_cons) + M = zeros(2 * (num_vars, num_ineq) + num_cons, 2 * (num_vars, num_ineq) + num_cons) # M = [ # [W A' -I]; @@ -164,15 +196,15 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # [V 0 X] # ] - M[1:num_vars, 1:num_vars] = W - M[1:num_vars, num_vars+1:num_vars+num_cons] = A' - M[num_vars+1:num_vars+num_cons, 1:num_vars] = A - M[1:num_vars, num_vars+num_cons+1:end] = -I(num_vars) - M[num_vars+num_cons+1:end, 1:num_vars] = V - M[num_vars+num_cons+1:end, num_vars+num_cons+1:end] = X + M[1:num_vars + num_ineq, 1:num_vars + num_ineq] = W + M[1:num_vars + num_ineq, num_vars + num_ineq + 1 : num_vars + num_ineq + num_cons] = A' + M[num_vars + num_ineq+1:num_vars + num_ineq+num_cons, 1:num_vars + num_ineq] = A + M[1:num_vars + num_ineq, num_vars + num_ineq+num_cons+1:end] = -I(num_vars + num_ineq) + M[num_vars + num_ineq+num_cons+1:end, 1:num_vars + num_ineq] = V + M[num_vars + num_ineq+num_cons+1:end, num_vars + num_ineq+num_cons+1:end] = X # N matrix - N = [∇ₓₚL ; ∇ₚC; zeros(num_vars, num_parms)] + N = [∇ₓₚL ; ∇ₚC; zeros(num_vars + num_ineq, num_parms)] # sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters return pinv(M) * N From 983c1822641d5cf9fca43fcce670a535f438fe44 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 2 Jul 2024 09:45:43 -0600 Subject: [PATCH 016/108] add comments --- nlp_utilities.jl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 1d82049c..46aabc0d 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -111,11 +111,21 @@ function create_evaluator(model::Model; x=all_variables(model)) return evaluator, rows end +""" + is_inequality(con::ConstraintRef) + +Check if the constraint is an inequality. +""" function is_inequality(con::ConstraintRef) set_type = MOI.get(owner_model(con), MOI.ConstraintSet(), con) return set_type <: MOI.LessThan || set_type <: MOI.GreaterThan end +""" + find_inequealities(cons::Vector{ConstraintRef}) + +Find the indices of the inequality constraints. +""" function find_inequealities(cons::Vector{ConstraintRef}) ineq_locations = zeros(length(cons)) for i in 1:length(cons) @@ -126,6 +136,11 @@ function find_inequealities(cons::Vector{ConstraintRef}) return findall(ineq_locations) end +""" + get_slack_inequality(con::ConstraintRef) + +Get the reference to the canonical function that is equivalent to the slack variable of the inequality constraint. +""" function get_slack_inequality(con::ConstraintRef) slack = MOI.get(owner_model(con), CanonicalConstraintFunction(), con) return slack @@ -171,7 +186,6 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Function Derivatives hessian, jacobian = compute_optimal_hess_jac(evaluator, cons, all_vars) - # TODO: Add appropriate entries for the slack variables: Zeros for the hessian and Identity for the jacobian # Hessian of the lagrangian wrt the primal variables W = zeros(num_vars + num_ineq, num_vars + num_ineq) W[1:num_vars, 1:num_vars] = hessian[1:num_vars, 1:num_vars] @@ -206,7 +220,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # N matrix N = [∇ₓₚL ; ∇ₚC; zeros(num_vars + num_ineq, num_parms)] - # sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters + # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters return pinv(M) * N end From cd6d5182baaeda79dcef6f5a958534cff10276fe Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 2 Jul 2024 10:54:13 -0600 Subject: [PATCH 017/108] adding tests --- nlp.utilities_test.jl | 5 ---- nlp_utilities.jl | 4 +-- nlp_utilities_test.jl | 68 +++++++++++++++++++++++++++++++++++++++++++ testing_barrier.jl | 4 +++ 4 files changed, 74 insertions(+), 7 deletions(-) delete mode 100644 nlp.utilities_test.jl create mode 100644 nlp_utilities_test.jl diff --git a/nlp.utilities_test.jl b/nlp.utilities_test.jl deleted file mode 100644 index 0aa8985b..00000000 --- a/nlp.utilities_test.jl +++ /dev/null @@ -1,5 +0,0 @@ -# TODO: Test Hessian - -# TODO: Test Jacobian - -# TODO: Test derivatives \ No newline at end of file diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 46aabc0d..9fd2c48e 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -7,7 +7,7 @@ using SparseArrays Create a Nonlinear Programming (NLP) model from a JuMP model. """ function create_nlp_model(model::JuMP.Model) - rows = Any[] + rows = Vector{ConstraintRef}(undef, 0) nlp = MOI.Nonlinear.Model() for (F, S) in list_of_constraint_types(model) if F <: VariableRef @@ -230,6 +230,6 @@ end Compute the derivatives of the solution with respect to the parameters. """ function compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) - evaluator, rows = create_evaluator(model) + evaluator, rows = create_evaluator(model; x=[primal_vars; params]) return compute_derivatives(evaluator, rows; primal_vars=primal_vars, params=params), evaluator, rows end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl new file mode 100644 index 00000000..8e89f7a5 --- /dev/null +++ b/nlp_utilities_test.jl @@ -0,0 +1,68 @@ +using JuMP +using Ipopt +using Test + +function create_nonlinear_jump_model() + model = Model(Ipopt.Optimizer) + set_silent(model) + # Parameters + @variable(model, p ∈ MOI.Parameter(1.0)) + @variable(model, p2 ∈ MOI.Parameter(2.0)) + @variable(model, p3 ∈ MOI.Parameter(100.0)) + @variable(model, x[i = 1:2], start = -i) + @constraint(model, g_1, x[1]^2 <= p) + @constraint(model, g_2, p * (x[1] + x[2])^2 <= p2) + @objective(model, Min, (1 - x[1])^2 + p3 * (x[2] - x[1]^2)^2) + return model, x, [g_1; g_2], [p; p2; p3] +end + +function analytic_hessian(x, σ, μ) + g_1_H = [2.0 0.0; 0.0 0.0] + g_2_H = [2.0 2.0; 2.0 2.0] + f_H = zeros(2, 2) + f_H[1, 1] = 2.0 + 1200.0 * x[1]^2 - 400.0 * x[2] + f_H[1, 2] = f_H[2, 1] = -400.0 * x[1] + f_H[2, 2] = 200.0 + return σ * f_H + μ' * [g_1_H, g_2_H] +end + +function test_create_evaluator(model, x) + @testset "Create NLP model" begin + nlp, rows = create_nlp_model(model) + @test nlp isa MOI.Nonlinear.Model + @test rows isa Vector{ConstraintRef} + end + @testset "Create Evaluator" begin + evaluator, rows = create_evaluator(model; x = x) + @test evaluator isa MOI.Nonlinear.Evaluator + @test rows isa Vector{ConstraintRef} + end +end + +function test_compute_optimal_hess_jacobian() + @testset "Compute Optimal Hessian and Jacobian" begin + # Model + model, x, cons, params = create_nonlinear_jump_model() + num_var = length(x) + test_create_evaluator(model, x) + evaluator, rows = create_evaluator(model; x = [x; params]) + # Optimize + optimize!(model) + @assert is_solved_and_feasible(model) + # Compute Hessian and Jacobian + full_hessian, full_jacobian = compute_optimal_hess_jac(evaluator, rows, [x; params]) + hessian = full_hessian[1:num_var, 1:num_var] + # Check Hessian + @test hessian .≈ analytic_hessian(value.(x), 1.0, dual.(cons)) + # TODO: Check Jacobian + end +end + +# TODO: Test derivatives + +function test_compute_derivatives() + model, x = create_nonlinear_jump_model() + optimize!(model) + @assert is_solved_and_feasible(model) + +end \ No newline at end of file diff --git a/testing_barrier.jl b/testing_barrier.jl index e700029d..d8d87d76 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -10,6 +10,7 @@ using Ipopt # using KNITRO include("nlp_utilities.jl") +include("nlp_utilities_test.jl") ############ # Test Case 1 @@ -41,3 +42,6 @@ termination_status(par_model) ############ ∂s, evaluator, rows = compute_derivatives(par_model) + + +test_compute_optimal_hess_jacobian() \ No newline at end of file From 97e5379ea2ed4d8583fb0b81f215afc31742dfd1 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 2 Jul 2024 12:35:02 -0600 Subject: [PATCH 018/108] add hessian and jacobian test --- nlp_utilities_test.jl | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 8e89f7a5..f949dfbe 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -16,16 +16,34 @@ function create_nonlinear_jump_model() return model, x, [g_1; g_2], [p; p2; p3] end -function analytic_hessian(x, σ, μ) +function analytic_hessian(x, σ, μ, p) g_1_H = [2.0 0.0; 0.0 0.0] - g_2_H = [2.0 2.0; 2.0 2.0] + g_2_H = p[1] * [2.0 2.0; 2.0 2.0] f_H = zeros(2, 2) - f_H[1, 1] = 2.0 + 1200.0 * x[1]^2 - 400.0 * x[2] - f_H[1, 2] = f_H[2, 1] = -400.0 * x[1] - f_H[2, 2] = 200.0 + f_H[1, 1] = 2.0 + p[3] * 12.0 * x[1]^2 - p[3] * 4.0 * x[2] + f_H[1, 2] = f_H[2, 1] = -p[3] * 4.0 * x[1] + f_H[2, 2] = p[3] * 2.0 return σ * f_H + μ' * [g_1_H, g_2_H] end +function analytic_jacobian(x, p) + g_1_J = [ + 2.0 * x[1], # ∂g_1/∂x_1 + 0.0, # ∂g_1/∂x_2 + -1.0, # ∂g_1/∂p_1 + 0.0, # ∂g_1/∂p_2 + 0.0 # ∂g_1/∂p_3 + ] + g_2_J = [ + p[1] * 2.0 * (x[1] + x[2]), # ∂g_2/∂x_1 + 2.0 * (x[1] + x[2]), # ∂g_2/∂x_2 + (x[1] + x[2])^2, # ∂g_2/∂p_1 + -1.0, # ∂g_2/∂p_2 + 0.0 # ∂g_2/∂p_3 + ] + return hcat(g_2_J, g_1_J)'[:,:] +end + function test_create_evaluator(model, x) @testset "Create NLP model" begin nlp, rows = create_nlp_model(model) @@ -43,18 +61,21 @@ function test_compute_optimal_hess_jacobian() @testset "Compute Optimal Hessian and Jacobian" begin # Model model, x, cons, params = create_nonlinear_jump_model() - num_var = length(x) - test_create_evaluator(model, x) - evaluator, rows = create_evaluator(model; x = [x; params]) # Optimize optimize!(model) @assert is_solved_and_feasible(model) + # Create evaluator + test_create_evaluator(model, [x; params]) + evaluator, rows = create_evaluator(model; x = [x; params]) # Compute Hessian and Jacobian + num_var = length(x) full_hessian, full_jacobian = compute_optimal_hess_jac(evaluator, rows, [x; params]) hessian = full_hessian[1:num_var, 1:num_var] # Check Hessian - @test hessian .≈ analytic_hessian(value.(x), 1.0, dual.(cons)) - # TODO: Check Jacobian + @test all(hessian .≈ analytic_hessian(value.(x), 1.0, dual.(cons), value.(params))) + # TODO: Test hessial of parameters + # Check Jacobian + @test all(full_jacobian .≈ analytic_jacobian(value.(x), value.(params))) end end From 41672ce604126a7b190ccf6b5b480e9e0e2aa492 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 2 Jul 2024 13:39:55 -0600 Subject: [PATCH 019/108] start adding tests derivatives --- nlp_utilities.jl | 11 ++++++----- nlp_utilities_test.jl | 44 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 9fd2c48e..9a305d03 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -117,7 +117,7 @@ end Check if the constraint is an inequality. """ function is_inequality(con::ConstraintRef) - set_type = MOI.get(owner_model(con), MOI.ConstraintSet(), con) + set_type = typeof(MOI.get(owner_model(con), MOI.ConstraintSet(), con)) return set_type <: MOI.LessThan || set_type <: MOI.GreaterThan end @@ -130,10 +130,10 @@ function find_inequealities(cons::Vector{ConstraintRef}) ineq_locations = zeros(length(cons)) for i in 1:length(cons) if is_inequality(cons[i]) - ineq_locations[i] = 1 + ineq_locations[i] = true end end - return findall(ineq_locations) + return findall(x -> x ==1, ineq_locations) end """ @@ -196,13 +196,14 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co A[j, num_vars+i] = 1 end # Partial second derivative of the lagrangian wrt primal solution and parameters - ∇ₓₚL = zeros(num_params, num_vars + num_ineq) + # TODO Fix dimensions + ∇ₓₚL = zeros(num_parms, num_vars + num_ineq) ∇ₓₚL[:, 1:num_vars] = hessian[num_vars+1:end, 1:num_vars] # Partial derivative of the equality constraintswith wrt parameters ∇ₚC = jacobian[:, num_vars+1:end] # M matrix - M = zeros(2 * (num_vars, num_ineq) + num_cons, 2 * (num_vars, num_ineq) + num_cons) + M = zeros(2 * (num_vars + num_ineq) + num_cons, 2 * (num_vars + num_ineq) + num_cons) # M = [ # [W A' -I]; diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index f949dfbe..b6d70bb7 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -1,11 +1,16 @@ using JuMP using Ipopt using Test +""" + create_nonlinear_jump_model() +Create a nonlinear jump model from the example in: +JuMP Tutorial for Querying Hessians: +https://github.com/jump-dev/JuMP.jl/blob/301d46e81cb66c74c6e22cd89fb89ced740f157b/docs/src/tutorials/nonlinear/querying_hessians.jl#L67-L72 +""" function create_nonlinear_jump_model() model = Model(Ipopt.Optimizer) set_silent(model) - # Parameters @variable(model, p ∈ MOI.Parameter(1.0)) @variable(model, p2 ∈ MOI.Parameter(2.0)) @variable(model, p3 ∈ MOI.Parameter(100.0)) @@ -79,11 +84,38 @@ function test_compute_optimal_hess_jacobian() end end -# TODO: Test derivatives +################################################ -function test_compute_derivatives() - model, x = create_nonlinear_jump_model() - optimize!(model) - @assert is_solved_and_feasible(model) +function create_nonlinear_jump_model_sipopt() + model = Model(Ipopt.Optimizer) + set_silent(model) + @variable(model, p1 ∈ MOI.Parameter(4.5)) + @variable(model, p2 ∈ MOI.Parameter(1.0)) + @variable(model, x[i = 1:3] >= 0, start = -i) + @constraint(model, g_1, 6 * x[1] + 3 * x[2] + 2 * x[3] - p1 == 0) + @constraint(model, g_2, p2 * x[1] + x[2] - x[3] - 1 == 0) + @objective(model, Min, x[1]^2 + x[2]^2 + x[3]^2) + return model, x, [g_1; g_2], [p1; p2] +end +function test_compute_derivatives() + @testset "Compute Derivatives" begin + # Model + model, x, cons, params = create_nonlinear_jump_model_sipopt() + optimize!(model) + @assert is_solved_and_feasible(model) + # Analytical solutions case b + pb = [4.5, 1.0] + s_pb = [0.5, 0.5, 0.0] + @assert all(isapprox(value.(x), s_pb; atol = 1e-6)) + # Analytical solutions case a + pa = [5.0, 1.0] + s_pa = [0.6327, 0.3878, 0.0204] + set_parameter_value.(params, pa) + optimize!(model) + @assert is_solved_and_feasible(model) + @assert all(isapprox(value.(x), s_pa; atol = 1e-4)) + # Compute derivatives + ∂s, evaluator, rows = compute_derivatives(model) + end end \ No newline at end of file From a8bae3ad6a391bf0636345df3f4d5b23eb2c3695 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 4 Jul 2024 11:08:38 -0600 Subject: [PATCH 020/108] working derivatives --- nlp_utilities.jl | 10 +++++----- nlp_utilities_test.jl | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 9a305d03..0b1a6e01 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -151,7 +151,7 @@ end Compute the derivatives of the solution with respect to the parameters. """ -function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; +function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}, params::Vector{VariableRef} ) @assert all(x -> is_parameter(x), params) "All parameters must be parameters" @@ -197,8 +197,8 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co end # Partial second derivative of the lagrangian wrt primal solution and parameters # TODO Fix dimensions - ∇ₓₚL = zeros(num_parms, num_vars + num_ineq) - ∇ₓₚL[:, 1:num_vars] = hessian[num_vars+1:end, 1:num_vars] + ∇ₓₚL = zeros(num_vars + num_ineq, num_parms) + ∇ₓₚL[1:num_vars, :] = hessian[1:num_vars, num_vars+1:end] # Partial derivative of the equality constraintswith wrt parameters ∇ₚC = jacobian[:, num_vars+1:end] @@ -222,7 +222,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co N = [∇ₓₚL ; ∇ₚC; zeros(num_vars + num_ineq, num_parms)] # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters - return pinv(M) * N + return - M \ N end """ @@ -232,5 +232,5 @@ Compute the derivatives of the solution with respect to the parameters. """ function compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) evaluator, rows = create_evaluator(model; x=[primal_vars; params]) - return compute_derivatives(evaluator, rows; primal_vars=primal_vars, params=params), evaluator, rows + return compute_derivatives_no_relax(evaluator, rows; primal_vars=primal_vars, params=params), evaluator, rows end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index b6d70bb7..4f87c52d 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -116,6 +116,8 @@ function test_compute_derivatives() @assert is_solved_and_feasible(model) @assert all(isapprox(value.(x), s_pa; atol = 1e-4)) # Compute derivatives - ∂s, evaluator, rows = compute_derivatives(model) + ∂s, evaluator, rows = compute_derivatives(model; primal_vars = x, params = params) + # Check linear approx s_pb + s_pb_approx = s_pa + ∂s[1:3, :] * (pb - pa) end end \ No newline at end of file From 17462cc9df26819251458cab6e8ef9761eeb6542 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 4 Jul 2024 11:11:13 -0600 Subject: [PATCH 021/108] add nominal derivatives test --- nlp_utilities_test.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 4f87c52d..fa763a2a 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -99,7 +99,7 @@ function create_nonlinear_jump_model_sipopt() end function test_compute_derivatives() - @testset "Compute Derivatives" begin + @testset "Compute Derivatives No Inequalities" begin # Model model, x, cons, params = create_nonlinear_jump_model_sipopt() optimize!(model) @@ -119,5 +119,6 @@ function test_compute_derivatives() ∂s, evaluator, rows = compute_derivatives(model; primal_vars = x, params = params) # Check linear approx s_pb s_pb_approx = s_pa + ∂s[1:3, :] * (pb - pa) + @test all(isapprox([0.5765; 0.3775; -0.0459], s_pb_approx; atol = 1e-2)) end end \ No newline at end of file From 0aadfe1f80a817e5a9898671f5f1920109ba4b82 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 5 Jul 2024 11:04:08 -0600 Subject: [PATCH 022/108] intermdetate solution --- nlp_utilities.jl | 139 ++++++++++++++++++++++++++++++------------ nlp_utilities_test.jl | 17 ++++-- 2 files changed, 112 insertions(+), 44 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 0b1a6e01..0e1512e1 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -146,42 +146,67 @@ function get_slack_inequality(con::ConstraintRef) return slack end +function compute_solution_and_bounds(primal_vars, cons) + num_vars = length(primal_vars) + ineq_locations = find_inequealities(cons) + num_ineq = length(ineq_locations) + slack_vars = [get_slack_inequality(cons[i]) for i in ineq_locations] + + # Primal solution + X = value.([primal_vars; slack_vars]) + + # value and dual of the lower bounds + V_L = zeros(num_vars+num_ineq) + X_L = zeros(num_vars+num_ineq) + for i in 1:num_vars + if has_lower_bound(primal_vars[i]) + V_L[i] = dual.(LowerBoundRef(primal_vars[i])) + X_L[i] = JuMP.lower_bound(primal_vars[i]) + end + end + for (i, con) in enumerate(cons[ineq_locations]) + V_L[num_vars+i] = dual.(con) + end + # value and dual of the upper bounds + V_U = zeros(num_vars+num_ineq) + X_U = zeros(num_vars+num_ineq) + for i in 1:num_vars + if has_upper_bound(primal_vars[i]) + V_U[i] = dual.(UpperBoundRef(primal_vars[i])) + X_U[i] = JuMP.upper_bound(primal_vars[i]) + end + end + + return X, V_L, X_L, V_U, X_U, ineq_locations +end + """ compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model)) -Compute the derivatives of the solution with respect to the parameters. +Compute the derivatives of the solution with respect to the parameters without accounting for active set changes. """ -function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; - primal_vars::Vector{VariableRef}, params::Vector{VariableRef} -) +function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, + primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, + _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z} +) where {T<:Real, Z<:Integer} @assert all(x -> is_parameter(x), params) "All parameters must be parameters" # Setting num_vars = length(primal_vars) num_parms = length(params) num_cons = length(cons) - ineq_locations = find_inequealities(cons) num_ineq = length(ineq_locations) - slack_vars = [get_slack_inequality(cons[i]) for i in ineq_locations] all_vars = [primal_vars; params] # Primal solution - X = diagm(value.([primal_vars; slack_vars])) + X = diagm(_X) - # Dual of the bounds - bound_duals = zeros(num_vars+num_ineq) - for i in 1:num_vars - if has_lower_bound(primal_vars[i]) - bound_duals[i] = dual.(LowerBoundRef(primal_vars[i])) - end - if has_upper_bound(primal_vars[i]) - bound_duals[i] -= dual.(UpperBoundRef(primal_vars[i])) - end - end - for (i, con) in enumerate(cons[ineq_locations]) - bound_duals[num_vars+i] = dual.(con) - end - V = diagm(bound_duals) + # value and dual of the lower bounds + V_L = diagm(_V_L) + X_L = diagm(_X_L) + # value and dual of the upper bounds + V_U = diagm(_V_U) + X_U = diagm(_X_U) # Function Derivatives hessian, jacobian = compute_optimal_hess_jac(evaluator, cons, all_vars) @@ -196,33 +221,47 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: A[j, num_vars+i] = 1 end # Partial second derivative of the lagrangian wrt primal solution and parameters - # TODO Fix dimensions ∇ₓₚL = zeros(num_vars + num_ineq, num_parms) ∇ₓₚL[1:num_vars, :] = hessian[1:num_vars, num_vars+1:end] # Partial derivative of the equality constraintswith wrt parameters ∇ₚC = jacobian[:, num_vars+1:end] # M matrix - M = zeros(2 * (num_vars + num_ineq) + num_cons, 2 * (num_vars + num_ineq) + num_cons) - # M = [ - # [W A' -I]; - # [A 0 0]; - # [V 0 X] + # [W A' -I I]; + # [A 0 0 0]; + # [V_L 0 (X - X_L) 0] + # [V_U 0 0 0 (X - X_U)] # ] - - M[1:num_vars + num_ineq, 1:num_vars + num_ineq] = W - M[1:num_vars + num_ineq, num_vars + num_ineq + 1 : num_vars + num_ineq + num_cons] = A' - M[num_vars + num_ineq+1:num_vars + num_ineq+num_cons, 1:num_vars + num_ineq] = A - M[1:num_vars + num_ineq, num_vars + num_ineq+num_cons+1:end] = -I(num_vars + num_ineq) - M[num_vars + num_ineq+num_cons+1:end, 1:num_vars + num_ineq] = V - M[num_vars + num_ineq+num_cons+1:end, num_vars + num_ineq+num_cons+1:end] = X + len_w = num_vars + num_ineq + M = zeros(3 * len_w + num_cons, 3 * len_w + num_cons) + + M[1:len_w, 1:len_w] = W + M[1:len_w, len_w + 1 : len_w + num_cons] = A' + M[len_w+1:len_w+num_cons, 1:len_w] = A + M[1:len_w, len_w+num_cons+1:2 * len_w+num_cons] = -I(len_w) + M[len_w+num_cons+1:2 * len_w+num_cons, 1:len_w] = V_L + M[len_w+num_cons+1:2 * len_w+num_cons, len_w+num_cons+1:2 * len_w+num_cons] = X - X_L + M[2 * len_w+num_cons+1:3 * len_w+num_cons, 1:len_w] = V_U + M[2 * len_w+num_cons+1:3 * len_w+num_cons, 2 * len_w+num_cons+1:3 * len_w+num_cons] = X - X_U + M[1:len_w, 2 * len_w+num_cons+1:end] = I(len_w) # N matrix - N = [∇ₓₚL ; ∇ₚC; zeros(num_vars + num_ineq, num_parms)] + N = [∇ₓₚL ; ∇ₚC; zeros(2 * len_w, num_parms)] # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters - return - M \ N + K = qr(M) # Factorization + return - (K \ N), K, N +end + +function fix_and_relax(E, K, N, r1, ∂p) + rs = N * ∂p + # C = −E' inv(K) E + C = - E' * (K \ E) + # C ∆ν¯ = E' inv(K) rs − r1 + ∆ν¯ = C \ (E' * (K \ rs) - r1) + # K ∆s = − (rs + E∆ν¯) + return K \ (- (rs + E * ∆ν¯)) end """ @@ -230,7 +269,29 @@ end Compute the derivatives of the solution with respect to the parameters. """ -function compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) - evaluator, rows = create_evaluator(model; x=[primal_vars; params]) - return compute_derivatives_no_relax(evaluator, rows; primal_vars=primal_vars, params=params), evaluator, rows +function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, + ∂p::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 +) where {T<:Real} + num_cons = length(cons) + # Solution and bounds + X, V_L, X_L, V_U, X_U, ineq_locations = compute_solution_and_bounds(primal_vars, cons) + num_w = length(ineq_locations) + length(primal_vars) + # Compute derivatives + ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations) + # Linearly appoximated solution + sp = [X; dual.(cons); V_L; V_U] .+ ∂s * ∂p + # One-hot vector that signals the bounds that are violated + # [X_L<= X <= X_U, dual ∈ R, 0 <= V] + E = [1.0 * (sp[1:num_w] .> X_U .+ tol) + 1.0 * (sp[1:num_w] .< X_L .+ tol); zeros(num_cons); sp[num_w+num_cons+1:end] .> 0.0 .+ tol] + # optimal solution at the violated bounds + r1 = E .* [X; zeros(num_cons); V_L; V_U] + if sum(E) > 0 + return fix_and_relax(E, K, N, r1, ∂p), evaluator, cons + end + return ∂s end + +function compute_derivatives(model::Model, ∂p::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} + evaluator, cons = create_evaluator(model; x=[primal_vars; params]) + return compute_derivatives(evaluator, cons, ∂p; primal_vars=primal_vars, params=params), evaluator, cons +end \ No newline at end of file diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index fa763a2a..f545fea2 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -101,7 +101,7 @@ end function test_compute_derivatives() @testset "Compute Derivatives No Inequalities" begin # Model - model, x, cons, params = create_nonlinear_jump_model_sipopt() + model, primal_vars, cons, params = create_nonlinear_jump_model_sipopt() optimize!(model) @assert is_solved_and_feasible(model) # Analytical solutions case b @@ -115,10 +115,17 @@ function test_compute_derivatives() optimize!(model) @assert is_solved_and_feasible(model) @assert all(isapprox(value.(x), s_pa; atol = 1e-4)) - # Compute derivatives - ∂s, evaluator, rows = compute_derivatives(model; primal_vars = x, params = params) + # Compute derivatives without accounting for active set changes + evaluator, rows = create_evaluator(model; x=[primal_vars; params]) + X, V_L, X_L, V_U, X_U, ineq_locations = compute_solution_and_bounds(primal_vars, rows) + ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations) # Check linear approx s_pb - s_pb_approx = s_pa + ∂s[1:3, :] * (pb - pa) - @test all(isapprox([0.5765; 0.3775; -0.0459], s_pb_approx; atol = 1e-2)) + ∂p = pb - pa + s_pb_approx_violated = s_pa + ∂s[1:3, :] * ∂p + @test all(isapprox([0.5765; 0.3775; -0.0459], s_pb_approx_violated; atol = 1e-2)) + # Account for active set changes + ∂s = compute_derivatives(evaluator, rows, ∂p; primal_vars, params) + s_pb_approx = s_pa + ∂s[1:3, :] * ∂p + @test all(isapprox(s_pb, s_pb_approx; atol = 1e-2)) end end \ No newline at end of file From 25dc2223ecd9714e388b339b3e998d33693255e7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 6 Jul 2024 14:22:10 -0600 Subject: [PATCH 023/108] add upperbound --- nlp_utilities.jl | 73 +++++++++++++++++++++++++------------------ nlp_utilities_test.jl | 8 ++--- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 0e1512e1..836f653b 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -151,18 +151,19 @@ function compute_solution_and_bounds(primal_vars, cons) ineq_locations = find_inequealities(cons) num_ineq = length(ineq_locations) slack_vars = [get_slack_inequality(cons[i]) for i in ineq_locations] + has_up = findall(x -> has_upper_bound(x), primal_vars) + has_low = findall(x -> has_lower_bound(x), primal_vars) # Primal solution X = value.([primal_vars; slack_vars]) # value and dual of the lower bounds - V_L = zeros(num_vars+num_ineq) - X_L = zeros(num_vars+num_ineq) - for i in 1:num_vars - if has_lower_bound(primal_vars[i]) - V_L[i] = dual.(LowerBoundRef(primal_vars[i])) - X_L[i] = JuMP.lower_bound(primal_vars[i]) - end + num_low = length(has_low) + V_L = zeros(num_low+num_ineq) + X_L = zeros(num_low+num_ineq) + for (i, j) in enumerate(has_low) + V_L[i] = dual.(LowerBoundRef(primal_vars[j])) + X_L[i] = JuMP.lower_bound(primal_vars[j]) end for (i, con) in enumerate(cons[ineq_locations]) V_L[num_vars+i] = dual.(con) @@ -170,14 +171,12 @@ function compute_solution_and_bounds(primal_vars, cons) # value and dual of the upper bounds V_U = zeros(num_vars+num_ineq) X_U = zeros(num_vars+num_ineq) - for i in 1:num_vars - if has_upper_bound(primal_vars[i]) - V_U[i] = dual.(UpperBoundRef(primal_vars[i])) - X_U[i] = JuMP.upper_bound(primal_vars[i]) - end + for (i, j) in enumerate(has_up) + V_U[i] = dual.(UpperBoundRef(primal_vars[j])) + X_U[i] = JuMP.upper_bound(primal_vars[j]) end - return X, V_L, X_L, V_U, X_U, ineq_locations + return X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low end """ @@ -187,7 +186,8 @@ Compute the derivatives of the solution with respect to the parameters without a """ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z} + _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + has_up::Vector{Z}, has_low::Vector{Z} ) where {T<:Real, Z<:Integer} @assert all(x -> is_parameter(x), params) "All parameters must be parameters" @@ -197,16 +197,29 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: num_cons = length(cons) num_ineq = length(ineq_locations) all_vars = [primal_vars; params] + num_low = length(has_low) + num_up = length(has_up) # Primal solution - X = diagm(_X) + X_lb = zeros(num_low, num_vars + num_ineq) + X_ub = zeros(num_up, num_vars + num_ineq) + V_L = zeros(num_low, num_vars + num_ineq) + V_U = zeros(num_up, num_vars + num_ineq) + I_L = zeros(num_vars + num_ineq, num_low) + I_U = zeros(num_vars + num_ineq, num_up) # value and dual of the lower bounds - V_L = diagm(_V_L) - X_L = diagm(_X_L) + for (i, j) in enumerate(has_low) + V_L[i, j] = _V_L[j] + X_lb[i, j] = _X[j] - _X_L[j] + I_L[j, i] = -1 + end # value and dual of the upper bounds - V_U = diagm(_V_U) - X_U = diagm(_X_U) + for (i, j) in enumerate(has_up) + V_U[i, j] = _V_U[j] + X_ub[i, j] = _X_U[j] - _X[j] + I_U[j, i] = 1 + end # Function Derivatives hessian, jacobian = compute_optimal_hess_jac(evaluator, cons, all_vars) @@ -231,23 +244,23 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # [W A' -I I]; # [A 0 0 0]; # [V_L 0 (X - X_L) 0] - # [V_U 0 0 0 (X - X_U)] + # [V_U 0 0 0 (X_U - X)] # ] len_w = num_vars + num_ineq - M = zeros(3 * len_w + num_cons, 3 * len_w + num_cons) + M = zeros(len_w + num_cons + num_low + num_up, len_w + num_cons + num_low + num_up) M[1:len_w, 1:len_w] = W M[1:len_w, len_w + 1 : len_w + num_cons] = A' M[len_w+1:len_w+num_cons, 1:len_w] = A - M[1:len_w, len_w+num_cons+1:2 * len_w+num_cons] = -I(len_w) - M[len_w+num_cons+1:2 * len_w+num_cons, 1:len_w] = V_L - M[len_w+num_cons+1:2 * len_w+num_cons, len_w+num_cons+1:2 * len_w+num_cons] = X - X_L - M[2 * len_w+num_cons+1:3 * len_w+num_cons, 1:len_w] = V_U - M[2 * len_w+num_cons+1:3 * len_w+num_cons, 2 * len_w+num_cons+1:3 * len_w+num_cons] = X - X_U - M[1:len_w, 2 * len_w+num_cons+1:end] = I(len_w) + M[1:len_w, len_w+num_cons+1:len_w+num_cons+num_low] = I_L + M[len_w+num_cons+1:len_w+num_cons+num_low, 1:len_w] = V_L + M[len_w+num_cons+1:len_w+num_cons+num_low, len_w+num_cons+1:len_w+num_cons+num_low] = X_lb + M[len_w+num_cons+num_low+1:len_w+num_cons+num_low+num_up, 1:len_w] = V_U + M[len_w+num_cons+num_low+1:len_w+num_cons+num_low+num_up, len_w+num_cons+num_low+1:len_w+num_cons+num_low+num_up] = X_ub + M[1:len_w, len_w+num_cons+num_low+1:end] = I_U # N matrix - N = [∇ₓₚL ; ∇ₚC; zeros(2 * len_w, num_parms)] + N = [∇ₓₚL ; ∇ₚC; zeros(num_low + num_up, num_parms)] # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters K = qr(M) # Factorization @@ -274,10 +287,10 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co ) where {T<:Real} num_cons = length(cons) # Solution and bounds - X, V_L, X_L, V_U, X_U, ineq_locations = compute_solution_and_bounds(primal_vars, cons) + X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) num_w = length(ineq_locations) + length(primal_vars) # Compute derivatives - ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations) + ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) # Linearly appoximated solution sp = [X; dual.(cons); V_L; V_U] .+ ∂s * ∂p # One-hot vector that signals the bounds that are violated diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index f545fea2..dba7dfc1 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -107,18 +107,18 @@ function test_compute_derivatives() # Analytical solutions case b pb = [4.5, 1.0] s_pb = [0.5, 0.5, 0.0] - @assert all(isapprox(value.(x), s_pb; atol = 1e-6)) + @assert all(isapprox(value.(primal_vars), s_pb; atol = 1e-6)) # Analytical solutions case a pa = [5.0, 1.0] s_pa = [0.6327, 0.3878, 0.0204] set_parameter_value.(params, pa) optimize!(model) @assert is_solved_and_feasible(model) - @assert all(isapprox(value.(x), s_pa; atol = 1e-4)) + @assert all(isapprox(value.(primal_vars), s_pa; atol = 1e-4)) # Compute derivatives without accounting for active set changes evaluator, rows = create_evaluator(model; x=[primal_vars; params]) - X, V_L, X_L, V_U, X_U, ineq_locations = compute_solution_and_bounds(primal_vars, rows) - ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations) + X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) + ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) # Check linear approx s_pb ∂p = pb - pa s_pb_approx_violated = s_pa + ∂s[1:3, :] * ∂p From 0ef56d851e3af39c47bb17fba0c27e02915c453c Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 8 Jul 2024 09:34:27 -0600 Subject: [PATCH 024/108] all tests passing --- nlp_utilities.jl | 50 +++++++++++++++++++++++++++++++++++-------- nlp_utilities_test.jl | 4 ++-- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 836f653b..c730c702 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -176,7 +176,7 @@ function compute_solution_and_bounds(primal_vars, cons) X_U[i] = JuMP.upper_bound(primal_vars[j]) end - return X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low + return X, V_L, X_L, V_U, X_U, ineq_locations, has_up, vcat(has_low, collect(num_vars+1:num_vars+num_ineq)) end """ @@ -277,6 +277,44 @@ function fix_and_relax(E, K, N, r1, ∂p) return K \ (- (rs + E * ∆ν¯)) end +function find_violations(X, ∂p, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + num_w = length(X) + num_low = length(has_low) + num_up = length(has_up) + # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] + sp = [X; zeros(num_cons); V_L[has_low]; V_U[has_up]] .+ ∂s * ∂p + + _E = [] + r1 = [] + for i in has_low + if sp[i] < X_L[i] - tol + push!(_E, i) + push!(r1, X[i] - X_L[i]) + end + if sp[num_w+num_cons+i] < -tol + push!(_E, num_w+num_cons+i) + push!(r1, V_L[i]) + end + end + for i in has_up + if sp[i] > X_U[i] + tol + push!(_E, i) + push!(r1, X_U[i] - X[i]) + end + if sp[num_w+num_cons+num_low+i] < -tol + push!(_E, num_w+num_cons+num_low+i) + push!(r1, V_U[i]) + end + end + + E = zeros(num_w + num_cons + num_low + num_up, length(_E)) + for (i, j) in enumerate(_E) + E[j, i] = 1 + end + + return E, r1 +end + """ compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) @@ -288,17 +326,11 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co num_cons = length(cons) # Solution and bounds X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) - num_w = length(ineq_locations) + length(primal_vars) # Compute derivatives ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) # Linearly appoximated solution - sp = [X; dual.(cons); V_L; V_U] .+ ∂s * ∂p - # One-hot vector that signals the bounds that are violated - # [X_L<= X <= X_U, dual ∈ R, 0 <= V] - E = [1.0 * (sp[1:num_w] .> X_U .+ tol) + 1.0 * (sp[1:num_w] .< X_L .+ tol); zeros(num_cons); sp[num_w+num_cons+1:end] .> 0.0 .+ tol] - # optimal solution at the violated bounds - r1 = E .* [X; zeros(num_cons); V_L; V_U] - if sum(E) > 0 + E, r1 = find_violations(X, ∂p, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + if !isempty(r1) return fix_and_relax(E, K, N, r1, ∂p), evaluator, cons end return ∂s diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index dba7dfc1..de981e80 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -124,8 +124,8 @@ function test_compute_derivatives() s_pb_approx_violated = s_pa + ∂s[1:3, :] * ∂p @test all(isapprox([0.5765; 0.3775; -0.0459], s_pb_approx_violated; atol = 1e-2)) # Account for active set changes - ∂s = compute_derivatives(evaluator, rows, ∂p; primal_vars, params) - s_pb_approx = s_pa + ∂s[1:3, :] * ∂p + ∂s, evaluator, cons = compute_derivatives(evaluator, rows, ∂p; primal_vars, params) + s_pb_approx = s_pa + ∂s[1:3, :] @test all(isapprox(s_pb, s_pb_approx; atol = 1e-2)) end end \ No newline at end of file From f00931fd44b30338a33ec302dab3bd8d68023d5b Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 8 Jul 2024 09:35:06 -0600 Subject: [PATCH 025/108] fix type --- nlp_utilities.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index c730c702..07723dde 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -333,7 +333,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co if !isempty(r1) return fix_and_relax(E, K, N, r1, ∂p), evaluator, cons end - return ∂s + return ∂s * ∂p, evaluator, cons end function compute_derivatives(model::Model, ∂p::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} From f593afab06a40c84578ec9f219f21cc8ec0a2a5c Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 9 Jul 2024 15:15:05 -0600 Subject: [PATCH 026/108] start adding approximation tests --- nlp_utilities.jl | 64 ++++++++++++++++++++---------------- nlp_utilities_test.jl | 76 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 37 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 07723dde..d5cd09fe 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -1,4 +1,6 @@ -using JuMP.MOI +using JuMP +using MathOptInterface +import MathOptInterface: ConstraintSet, CanonicalConstraintFunction using SparseArrays """ @@ -142,8 +144,8 @@ end Get the reference to the canonical function that is equivalent to the slack variable of the inequality constraint. """ function get_slack_inequality(con::ConstraintRef) - slack = MOI.get(owner_model(con), CanonicalConstraintFunction(), con) - return slack + obj = constraint_object(con) + return obj.func end function compute_solution_and_bounds(primal_vars, cons) @@ -158,9 +160,8 @@ function compute_solution_and_bounds(primal_vars, cons) X = value.([primal_vars; slack_vars]) # value and dual of the lower bounds - num_low = length(has_low) - V_L = zeros(num_low+num_ineq) - X_L = zeros(num_low+num_ineq) + V_L = zeros(num_vars+num_ineq) + X_L = zeros(num_vars+num_ineq) for (i, j) in enumerate(has_low) V_L[i] = dual.(LowerBoundRef(primal_vars[j])) X_L[i] = JuMP.lower_bound(primal_vars[j]) @@ -201,8 +202,8 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: num_up = length(has_up) # Primal solution - X_lb = zeros(num_low, num_vars + num_ineq) - X_ub = zeros(num_up, num_vars + num_ineq) + X_lb = zeros(num_low, num_low) + X_ub = zeros(num_up, num_low) V_L = zeros(num_low, num_vars + num_ineq) V_U = zeros(num_up, num_vars + num_ineq) I_L = zeros(num_vars + num_ineq, num_low) @@ -211,13 +212,13 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # value and dual of the lower bounds for (i, j) in enumerate(has_low) V_L[i, j] = _V_L[j] - X_lb[i, j] = _X[j] - _X_L[j] + X_lb[i, i] = _X[j] - _X_L[j] I_L[j, i] = -1 end # value and dual of the upper bounds for (i, j) in enumerate(has_up) V_U[i, j] = _V_U[j] - X_ub[i, j] = _X_U[j] - _X[j] + X_ub[i, i] = _X_U[j] - _X[j] I_U[j, i] = 1 end @@ -267,8 +268,8 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: return - (K \ N), K, N end -function fix_and_relax(E, K, N, r1, ∂p) - rs = N * ∂p +function fix_and_relax(E, K, N, r1, Δp) + rs = N * Δp # C = −E' inv(K) E C = - E' * (K \ E) # C ∆ν¯ = E' inv(K) rs − r1 @@ -277,32 +278,34 @@ function fix_and_relax(E, K, N, r1, ∂p) return K \ (- (rs + E * ∆ν¯)) end -function find_violations(X, ∂p, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) +function approximate_solution(X, Λ, V_L, V_U, Δs) + return [X; Λ; V_L; V_U] .+ Δs +end + +function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) num_w = length(X) num_low = length(has_low) num_up = length(has_up) - # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] - sp = [X; zeros(num_cons); V_L[has_low]; V_U[has_up]] .+ ∂s * ∂p _E = [] r1 = [] - for i in has_low + for (j, i) in enumerate(has_low) if sp[i] < X_L[i] - tol push!(_E, i) push!(r1, X[i] - X_L[i]) end - if sp[num_w+num_cons+i] < -tol - push!(_E, num_w+num_cons+i) + if sp[num_w+num_cons+j] < -tol + push!(_E, num_w+num_cons+j) push!(r1, V_L[i]) end end - for i in has_up + for (j, i) in enumerate(has_up) if sp[i] > X_U[i] + tol push!(_E, i) push!(r1, X_U[i] - X[i]) end - if sp[num_w+num_cons+num_low+i] < -tol - push!(_E, num_w+num_cons+num_low+i) + if sp[num_w+num_cons+num_low+j] < -tol + push!(_E, num_w+num_cons+num_low+j) push!(r1, V_U[i]) end end @@ -321,22 +324,27 @@ end Compute the derivatives of the solution with respect to the parameters. """ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, - ∂p::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 + Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 ) where {T<:Real} num_cons = length(cons) # Solution and bounds X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) # Compute derivatives + # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) + Δs = ∂s * Δp + Λ = dual.(cons) + sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution - E, r1 = find_violations(X, ∂p, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) if !isempty(r1) - return fix_and_relax(E, K, N, r1, ∂p), evaluator, cons + Δs = fix_and_relax(E, K, N, r1, Δp) + sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end - return ∂s * ∂p, evaluator, cons + return Δs, sp end -function compute_derivatives(model::Model, ∂p::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} +function compute_derivatives(model::Model, Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} evaluator, cons = create_evaluator(model; x=[primal_vars; params]) - return compute_derivatives(evaluator, cons, ∂p; primal_vars=primal_vars, params=params), evaluator, cons -end \ No newline at end of file + return compute_derivatives(evaluator, cons, Δp; primal_vars=primal_vars, params=params), evaluator, cons +end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index de981e80..1003393f 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -1,13 +1,14 @@ using JuMP using Ipopt using Test -""" - create_nonlinear_jump_model() +using FiniteDiff -Create a nonlinear jump model from the example in: -JuMP Tutorial for Querying Hessians: +#= +# Test JuMP Hessian and Jacobian + +From JuMP Tutorial for Querying Hessians: https://github.com/jump-dev/JuMP.jl/blob/301d46e81cb66c74c6e22cd89fb89ced740f157b/docs/src/tutorials/nonlinear/querying_hessians.jl#L67-L72 -""" +=# function create_nonlinear_jump_model() model = Model(Ipopt.Optimizer) set_silent(model) @@ -85,6 +86,11 @@ function test_compute_optimal_hess_jacobian() end ################################################ +#= +# Test Derivatives and Sensitivity: QP problem + +From sIpopt paper: https://optimization-online.org/2011/04/3008/ +=# function create_nonlinear_jump_model_sipopt() model = Model(Ipopt.Optimizer) @@ -120,12 +126,64 @@ function test_compute_derivatives() X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) # Check linear approx s_pb - ∂p = pb - pa - s_pb_approx_violated = s_pa + ∂s[1:3, :] * ∂p + Δp = pb - pa + s_pb_approx_violated = s_pa + ∂s[1:3, :] * Δp @test all(isapprox([0.5765; 0.3775; -0.0459], s_pb_approx_violated; atol = 1e-2)) # Account for active set changes - ∂s, evaluator, cons = compute_derivatives(evaluator, rows, ∂p; primal_vars, params) - s_pb_approx = s_pa + ∂s[1:3, :] + Δs = compute_derivatives(evaluator, rows, Δp; primal_vars, params) + s_pb_approx = s_pa + Δs[1:3, :] @test all(isapprox(s_pb, s_pb_approx; atol = 1e-2)) end +end + +################################################ +#= +# Test Sensitivity through finite differences +=# + +function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + + return model, [x; y], [con1; con2; con3], p +end + +function eval_model_jump(model, primal_vars, cons, params, p_val) + set_parameter_value.(params, p_val) + optimize!(model) + return value.(primal_vars), dual.(cons), [dual.(LowerBoundRef(v)) for v in primal_vars if has_lower_bound(v)], [dual.(UpperBoundRef(v)) for v in primal_vars if has_upper_bound(v)] +end + +function test_compute_derivatives_1() + @testset "Compute Derivatives" begin + # OPT Problem + p_a = [1.0; 2.0; 100] + model, primal_vars, cons, params = create_nonlinear_jump_model_1(p_a) + optimize!(model) + @assert is_solved_and_feasible(model) + # Compute derivatives + p_b = [1.001; 2.001; 100.001] + Δp = p_b - p_a + (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) + # Check solution + x_b, λ_b, ν_Lb, ν_Ub = eval_model_jump(model, primal_vars, cons, params, p_b) + sp = [x_b; λ_b; ν_Lb; ν_Ub] # TODO: repeat ineq on bounds + @test all(isapprox(sp, sp_approx; atol = 1e-3)) + # Check derivatives using finite differences + ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> eval_model_jump(model, primal_vars, cons, params, p), p_a) + Δs_fd = ∂s_fd * Δp + end end \ No newline at end of file From 2a977994b4cba2fdf4073352bc3e500577786167 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 9 Jul 2024 15:49:37 -0600 Subject: [PATCH 027/108] fix sign --- nlp_utilities.jl | 2 +- nlp_utilities_test.jl | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index d5cd09fe..04255799 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -232,7 +232,7 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: A = zeros(num_cons, num_vars + num_ineq) A[:, 1:num_vars] = jacobian[:, 1:num_vars] for (i,j) in enumerate(ineq_locations) - A[j, num_vars+i] = 1 + A[j, num_vars+i] = -1 end # Partial second derivative of the lagrangian wrt primal solution and parameters ∇ₓₚL = zeros(num_vars + num_ineq, num_parms) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 1003393f..c48508ed 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -180,10 +180,11 @@ function test_compute_derivatives_1() (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) # Check solution x_b, λ_b, ν_Lb, ν_Ub = eval_model_jump(model, primal_vars, cons, params, p_b) - sp = [x_b; λ_b; ν_Lb; ν_Ub] # TODO: repeat ineq on bounds - @test all(isapprox(sp, sp_approx; atol = 1e-3)) + ineq_locations = find_inequealities(cons) + sp = [x_b; value.(get_slack_inequality.(cons[ineq_locations])); λ_b; ν_Lb; λ_b[ineq_locations]; ν_Ub] + @test all(isapprox.(sp, sp_approx; atol = 1e-2)) # Check derivatives using finite differences - ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> eval_model_jump(model, primal_vars, cons, params, p), p_a) - Δs_fd = ∂s_fd * Δp + # ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> eval_model_jump(model, primal_vars, cons, params, p), p_a) + # Δs_fd = ∂s_fd * Δp end end \ No newline at end of file From 52f075be2b0edbaea9655679dabff61de8dec8ea Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 9 Jul 2024 15:56:07 -0600 Subject: [PATCH 028/108] update signs --- nlp_utilities.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 04255799..f16f6d4b 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -334,6 +334,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) Δs = ∂s * Δp Λ = dual.(cons) + Λ[ineq_locations] = Λ[ineq_locations] .* -1.0 # Correcting the sign of the duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) From 6b567b5281cca227870348eeb1fda25a7ecd0a45 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Tue, 9 Jul 2024 16:09:21 -0600 Subject: [PATCH 029/108] update solution structure for test --- nlp_utilities.jl | 2 +- nlp_utilities_test.jl | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index f16f6d4b..ad724982 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -128,7 +128,7 @@ end Find the indices of the inequality constraints. """ -function find_inequealities(cons::Vector{ConstraintRef}) +function find_inequealities(cons::Vector{C}) where C<:ConstraintRef ineq_locations = zeros(length(cons)) for i in 1:length(cons) if is_inequality(cons[i]) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index c48508ed..34f4d0ff 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -164,6 +164,7 @@ end function eval_model_jump(model, primal_vars, cons, params, p_val) set_parameter_value.(params, p_val) optimize!(model) + @assert is_solved_and_feasible(model) return value.(primal_vars), dual.(cons), [dual.(LowerBoundRef(v)) for v in primal_vars if has_lower_bound(v)], [dual.(UpperBoundRef(v)) for v in primal_vars if has_upper_bound(v)] end @@ -172,16 +173,22 @@ function test_compute_derivatives_1() # OPT Problem p_a = [1.0; 2.0; 100] model, primal_vars, cons, params = create_nonlinear_jump_model_1(p_a) - optimize!(model) - @assert is_solved_and_feasible(model) + # Debugging + x_a, _λ_a, ν_La, ν_Ua = eval_model_jump(model, primal_vars, cons, params, p_a) + ineq_locations = find_inequealities(cons) + λ_a = deepcopy(_λ_a) + λ_a[ineq_locations] = _λ_a[ineq_locations] .* -1 + s_a = [x_a; value.(get_slack_inequality.(cons[ineq_locations])); λ_a; ν_La; _λ_a[ineq_locations]; ν_Ua] # Compute derivatives - p_b = [1.001; 2.001; 100.001] + p_b = [1.5; 2.00; 100.0] Δp = p_b - p_a (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) # Check solution - x_b, λ_b, ν_Lb, ν_Ub = eval_model_jump(model, primal_vars, cons, params, p_b) + x_b, _λ_b, ν_Lb, ν_Ub = eval_model_jump(model, primal_vars, cons, params, p_b) ineq_locations = find_inequealities(cons) - sp = [x_b; value.(get_slack_inequality.(cons[ineq_locations])); λ_b; ν_Lb; λ_b[ineq_locations]; ν_Ub] + λ_b = deepcopy(_λ_b) + λ_b[ineq_locations] = _λ_b[ineq_locations] .* -1 + sp = [x_b; value.(get_slack_inequality.(cons[ineq_locations])); λ_b; ν_Lb; _λ_b[ineq_locations]; ν_Ub] @test all(isapprox.(sp, sp_approx; atol = 1e-2)) # Check derivatives using finite differences # ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> eval_model_jump(model, primal_vars, cons, params, p), p_a) From 55f9b643d2163d416d9c93bb397223d4ecb311b9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 11 Jul 2024 15:59:48 -0600 Subject: [PATCH 030/108] debug tests --- nlp_utilities.jl | 4 +- nlp_utilities_test.jl | 87 +++++++++++++++++++++++++++++++++---------- 2 files changed, 71 insertions(+), 20 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index ad724982..c3021ea1 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -2,6 +2,7 @@ using JuMP using MathOptInterface import MathOptInterface: ConstraintSet, CanonicalConstraintFunction using SparseArrays +using LinearAlgebra """ create_nlp_model(model::JuMP.Model) @@ -324,7 +325,7 @@ end Compute the derivatives of the solution with respect to the parameters. """ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, - Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 + Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-8 ) where {T<:Real} num_cons = length(cons) # Solution and bounds @@ -339,6 +340,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) if !isempty(r1) + @warn "Relaxation needed" Δs = fix_and_relax(E, K, N, r1, Δp) sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 34f4d0ff..8a9afdee 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -3,6 +3,8 @@ using Ipopt using Test using FiniteDiff +include("nlp_utilities.jl") + #= # Test JuMP Hessian and Jacobian @@ -168,30 +170,77 @@ function eval_model_jump(model, primal_vars, cons, params, p_val) return value.(primal_vars), dual.(cons), [dual.(LowerBoundRef(v)) for v in primal_vars if has_lower_bound(v)], [dual.(UpperBoundRef(v)) for v in primal_vars if has_upper_bound(v)] end -function test_compute_derivatives_1() - @testset "Compute Derivatives" begin +function stack_solution(ineq_locations, x, _λ, ν_L, ν_U) + λ = deepcopy(_λ) + λ[ineq_locations] = _λ[ineq_locations] .* -1 + return Float64[x; value.(get_slack_inequality.(cons[ineq_locations])); λ; ν_L; _λ[ineq_locations]; ν_U] +end + +function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) + # primal vars + num_primal_vars = length(primal_vars) + for (i, v) in enumerate(primal_vars) + if !isapprox(Δs[i], Δs_fd[i]; atol = 1e-2) + println("Primal var: ", v, " | Δs: ", Δs[i], " | Δs_fd: ", Δs_fd[i]) + end + end + # slack vars + num_slack_vars = length(ineq_locations) + num_w = num_slack_vars + num_primal_vars + for (i, c) in enumerate(cons[ineq_locations]) + if !isapprox(Δs[i + num_primal_vars], Δs_fd[i + num_primal_vars] ; atol = 1e-2) + println("Slack var: ", c, " | Δs: ", Δs[i + num_primal_vars], " | Δs_fd: ", Δs_fd[i + num_primal_vars]) + end + end + # dual vars + num_cons = length(cons) + for (i, c) in enumerate(cons) + if !isapprox(Δs[i + num_w], Δs_fd[i + num_w] ; atol = 1e-2) + println("Dual var: ", c, " | Δs: ", Δs[i + num_w], " | Δs_fd: ", Δs_fd[i + num_w]) + end + end + # dual lower bound primal vars + num_lower_bounds = length([v for v in primal_vars if has_lower_bound(v)]) + for (i, v) in enumerate(primal_vars) + if has_lower_bound(v) && !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-2) + lower_bound_ref = LowerBoundRef(v) + println("lower bound dual: ", lower_bound_ref, " | Δs: ", Δs[i + num_w + num_cons], " | Δs_fd: ", Δs_fd[i + num_w + num_cons]) + end + end + # dual upper bound primal vars + for (i, v) in enumerate(primal_vars) + if has_upper_bound(v) && !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-2) + upper_bound_ref = UpperBoundRef(v) + println("upper bound dual: ", upper_bound_ref, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars]) + end + end +end + +DICT_PROBLEMS = Dict( + "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), + "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), + "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1) +) + +function test_compute_derivatives_Finite_Diff() + @testset "Compute Derivatives: $problem_name" for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS # OPT Problem - p_a = [1.0; 2.0; 100] - model, primal_vars, cons, params = create_nonlinear_jump_model_1(p_a) - # Debugging - x_a, _λ_a, ν_La, ν_Ua = eval_model_jump(model, primal_vars, cons, params, p_a) + # p_a = [3.0; 2.0; 200] + model, primal_vars, cons, params = model_generator() ineq_locations = find_inequealities(cons) - λ_a = deepcopy(_λ_a) - λ_a[ineq_locations] = _λ_a[ineq_locations] .* -1 - s_a = [x_a; value.(get_slack_inequality.(cons[ineq_locations])); λ_a; ν_La; _λ_a[ineq_locations]; ν_Ua] + # Debugging + s_a = stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) # Compute derivatives - p_b = [1.5; 2.00; 100.0] - Δp = p_b - p_a + # Δp = [0.001; 0.0; 0.0] + # p_b = p_a + Δp (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) # Check solution - x_b, _λ_b, ν_Lb, ν_Ub = eval_model_jump(model, primal_vars, cons, params, p_b) - ineq_locations = find_inequealities(cons) - λ_b = deepcopy(_λ_b) - λ_b[ineq_locations] = _λ_b[ineq_locations] .* -1 - sp = [x_b; value.(get_slack_inequality.(cons[ineq_locations])); λ_b; ν_Lb; _λ_b[ineq_locations]; ν_Ub] - @test all(isapprox.(sp, sp_approx; atol = 1e-2)) + # sp = stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) + # @test all(isapprox.(sp, sp_approx; atol = 1e-2)) # Check derivatives using finite differences - # ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> eval_model_jump(model, primal_vars, cons, params, p), p_a) - # Δs_fd = ∂s_fd * Δp + ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) + Δs_fd = ∂s_fd * Δp + # @test all(isapprox.(Δs, Δs_fd; atol = 1e-2)) + print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end end \ No newline at end of file From 9b00e399f9cebc6372e0ce5a089ead913971927f Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 11 Jul 2024 16:09:02 -0600 Subject: [PATCH 031/108] update debug --- nlp_utilities_test.jl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 8a9afdee..9a93a7cd 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -177,6 +177,7 @@ function stack_solution(ineq_locations, x, _λ, ν_L, ν_U) end function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) + println("Some sensitivities are not correct: \n") # primal vars num_primal_vars = length(primal_vars) for (i, v) in enumerate(primal_vars) @@ -223,7 +224,8 @@ DICT_PROBLEMS = Dict( ) function test_compute_derivatives_Finite_Diff() - @testset "Compute Derivatives: $problem_name" for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS + # @testset "Compute Derivatives: $problem_name" + for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS # OPT Problem # p_a = [3.0; 2.0; 200] model, primal_vars, cons, params = model_generator() @@ -240,7 +242,13 @@ function test_compute_derivatives_Finite_Diff() # Check derivatives using finite differences ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) Δs_fd = ∂s_fd * Δp - # @test all(isapprox.(Δs, Δs_fd; atol = 1e-2)) - print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) + + println("$problem_name: ", model) + if all(isapprox.(Δs, Δs_fd; atol = 1e-2)) + println("All sensitivities are correct") + else + print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) + end + println("--------------------") end end \ No newline at end of file From c695c52545c261e9c76d3adbf9acecf843207b25 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 12 Jul 2024 10:54:54 -0600 Subject: [PATCH 032/108] fix signs --- nlp_utilities.jl | 31 +++++++++++++++++++++++-------- nlp_utilities_test.jl | 30 +++++++++++++++--------------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index c3021ea1..2ebf9ba5 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -55,7 +55,7 @@ function compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vecto I = [i for (i, _) in hessian_sparsity] J = [j for (_, j) in hessian_sparsity] V = zeros(length(hessian_sparsity)) - MOI.eval_hessian_lagrangian(evaluator, V, value.(x), 1.0, dual.(rows)) + MOI.eval_hessian_lagrangian(evaluator, V, value.(x), -1.0, dual.(rows)) H = SparseArrays.sparse(I, J, V, length(x), length(x)) return Matrix(fill_off_diagonal(H)) end @@ -145,8 +145,13 @@ end Get the reference to the canonical function that is equivalent to the slack variable of the inequality constraint. """ function get_slack_inequality(con::ConstraintRef) + set_type = typeof(MOI.get(owner_model(con), MOI.ConstraintSet(), con)) obj = constraint_object(con) - return obj.func + if set_type <: MOI.LessThan + # c(x) <= b --> slack = -c(x) + b | slack >= 0 + return - obj.func + obj.set.upper + end + return obj.func - obj.set.lower end function compute_solution_and_bounds(primal_vars, cons) @@ -181,12 +186,7 @@ function compute_solution_and_bounds(primal_vars, cons) return X, V_L, X_L, V_U, X_U, ineq_locations, has_up, vcat(has_low, collect(num_vars+1:num_vars+num_ineq)) end -""" - compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model)) - -Compute the derivatives of the solution with respect to the parameters without accounting for active set changes. -""" -function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, +function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} @@ -264,6 +264,21 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # N matrix N = [∇ₓₚL ; ∇ₚC; zeros(num_low + num_up, num_parms)] + return M, N +end + +""" + compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model)) + +Compute the derivatives of the solution with respect to the parameters without accounting for active set changes. +""" +function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, + primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, + _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + has_up::Vector{Z}, has_low::Vector{Z} +) where {T<:Real, Z<:Integer} + M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, ineq_locations, has_up, has_low) + # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters K = qr(M) # Factorization return - (K \ N), K, N diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 9a93a7cd..95fcd3b4 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -114,27 +114,27 @@ function test_compute_derivatives() @assert is_solved_and_feasible(model) # Analytical solutions case b pb = [4.5, 1.0] - s_pb = [0.5, 0.5, 0.0] + s_pb = [0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] @assert all(isapprox(value.(primal_vars), s_pb; atol = 1e-6)) # Analytical solutions case a pa = [5.0, 1.0] - s_pa = [0.6327, 0.3878, 0.0204] + s_pa = [0.6327, 0.3878, 0.0204, 0.1633, 0.2857, 0, 0, 0] set_parameter_value.(params, pa) optimize!(model) @assert is_solved_and_feasible(model) - @assert all(isapprox(value.(primal_vars), s_pa; atol = 1e-4)) + @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) # Compute derivatives without accounting for active set changes evaluator, rows = create_evaluator(model; x=[primal_vars; params]) X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) # Check linear approx s_pb Δp = pb - pa - s_pb_approx_violated = s_pa + ∂s[1:3, :] * Δp - @test all(isapprox([0.5765; 0.3775; -0.0459], s_pb_approx_violated; atol = 1e-2)) + s_pb_approx_violated = s_pa + ∂s * Δp + @test all(isapprox.([0.5765; 0.3775; -0.0459; 0.1327; 0.3571; 0.0; 0.0; 0.0], s_pb_approx_violated; atol = 1e-4)) # Account for active set changes - Δs = compute_derivatives(evaluator, rows, Δp; primal_vars, params) - s_pb_approx = s_pa + Δs[1:3, :] - @test all(isapprox(s_pb, s_pb_approx; atol = 1e-2)) + Δs, s_pb_approx = compute_derivatives(evaluator, rows, Δp; primal_vars, params) + # s_pb_approx = s_pa .+ Δs + @test all(isapprox.(s_pb, s_pb_approx; atol = 1e-2)) end end @@ -170,10 +170,10 @@ function eval_model_jump(model, primal_vars, cons, params, p_val) return value.(primal_vars), dual.(cons), [dual.(LowerBoundRef(v)) for v in primal_vars if has_lower_bound(v)], [dual.(UpperBoundRef(v)) for v in primal_vars if has_upper_bound(v)] end -function stack_solution(ineq_locations, x, _λ, ν_L, ν_U) - λ = deepcopy(_λ) - λ[ineq_locations] = _λ[ineq_locations] .* -1 - return Float64[x; value.(get_slack_inequality.(cons[ineq_locations])); λ; ν_L; _λ[ineq_locations]; ν_U] +function stack_solution(cons, ineq_locations, x, _λ, ν_L, ν_U) + # λ = deepcopy(_λ) + # λ[ineq_locations] = _λ[ineq_locations] .* -1 + return Float64[x; value.(get_slack_inequality.(cons[ineq_locations])); _λ; ν_L; _λ[ineq_locations]; ν_U] end function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) @@ -231,16 +231,16 @@ function test_compute_derivatives_Finite_Diff() model, primal_vars, cons, params = model_generator() ineq_locations = find_inequealities(cons) # Debugging - s_a = stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) + s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) # Compute derivatives # Δp = [0.001; 0.0; 0.0] # p_b = p_a + Δp (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) # Check solution - # sp = stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) + # sp = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) # @test all(isapprox.(sp, sp_approx; atol = 1e-2)) # Check derivatives using finite differences - ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) + ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) Δs_fd = ∂s_fd * Δp println("$problem_name: ", model) From 9b0f72f992a8e422640c3649092a8414a6133408 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 12 Jul 2024 13:12:21 -0600 Subject: [PATCH 033/108] fix signs --- nlp_utilities.jl | 14 +++++++++----- nlp_utilities_test.jl | 23 +++++++++++++---------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 2ebf9ba5..ed7c237d 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -277,21 +277,26 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} ) where {T<:Real, Z<:Integer} + num_bounds = length(has_up) + length(has_low) M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, ineq_locations, has_up, has_low) # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters K = qr(M) # Factorization - return - (K \ N), K, N + ∂s = - (K \ N) # Sensitivity + ∂s[end-num_bounds+1:end,:] = ∂s[end-num_bounds+1:end,:] .* -1.0 # Correcting the sign of the bounds duals for the standard form + return ∂s, K, N end -function fix_and_relax(E, K, N, r1, Δp) +function fix_and_relax(E, K, N, r1, Δp, num_bounds) rs = N * Δp # C = −E' inv(K) E C = - E' * (K \ E) # C ∆ν¯ = E' inv(K) rs − r1 ∆ν¯ = C \ (E' * (K \ rs) - r1) # K ∆s = − (rs + E∆ν¯) - return K \ (- (rs + E * ∆ν¯)) + ∆s = K \ (- (rs + E * ∆ν¯)) + ∆s[end-num_bounds+1:end] = ∆s[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form + return ∆s end function approximate_solution(X, Λ, V_L, V_U, Δs) @@ -349,8 +354,7 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) Δs = ∂s * Δp - Λ = dual.(cons) - Λ[ineq_locations] = Λ[ineq_locations] .* -1.0 # Correcting the sign of the duals for the standard form + Λ = -dual.(cons) sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 95fcd3b4..9361d65c 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -134,7 +134,7 @@ function test_compute_derivatives() # Account for active set changes Δs, s_pb_approx = compute_derivatives(evaluator, rows, Δp; primal_vars, params) # s_pb_approx = s_pa .+ Δs - @test all(isapprox.(s_pb, s_pb_approx; atol = 1e-2)) + @test all(isapprox.(s_pb, s_pb_approx; atol = 1e-4)) end end @@ -181,7 +181,7 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) # primal vars num_primal_vars = length(primal_vars) for (i, v) in enumerate(primal_vars) - if !isapprox(Δs[i], Δs_fd[i]; atol = 1e-2) + if !isapprox(Δs[i], Δs_fd[i]; atol = 1e-4) println("Primal var: ", v, " | Δs: ", Δs[i], " | Δs_fd: ", Δs_fd[i]) end end @@ -189,28 +189,28 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) num_slack_vars = length(ineq_locations) num_w = num_slack_vars + num_primal_vars for (i, c) in enumerate(cons[ineq_locations]) - if !isapprox(Δs[i + num_primal_vars], Δs_fd[i + num_primal_vars] ; atol = 1e-2) + if !isapprox(Δs[i + num_primal_vars], Δs_fd[i + num_primal_vars] ; atol = 1e-4) println("Slack var: ", c, " | Δs: ", Δs[i + num_primal_vars], " | Δs_fd: ", Δs_fd[i + num_primal_vars]) end end # dual vars num_cons = length(cons) for (i, c) in enumerate(cons) - if !isapprox(Δs[i + num_w], Δs_fd[i + num_w] ; atol = 1e-2) + if !isapprox(Δs[i + num_w], Δs_fd[i + num_w] ; atol = 1e-4) println("Dual var: ", c, " | Δs: ", Δs[i + num_w], " | Δs_fd: ", Δs_fd[i + num_w]) end end # dual lower bound primal vars num_lower_bounds = length([v for v in primal_vars if has_lower_bound(v)]) for (i, v) in enumerate(primal_vars) - if has_lower_bound(v) && !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-2) + if has_lower_bound(v) && !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-4) lower_bound_ref = LowerBoundRef(v) println("lower bound dual: ", lower_bound_ref, " | Δs: ", Δs[i + num_w + num_cons], " | Δs_fd: ", Δs_fd[i + num_w + num_cons]) end end # dual upper bound primal vars for (i, v) in enumerate(primal_vars) - if has_upper_bound(v) && !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-2) + if has_upper_bound(v) && !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-4) upper_bound_ref = UpperBoundRef(v) println("upper bound dual: ", upper_bound_ref, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars]) end @@ -220,7 +220,10 @@ end DICT_PROBLEMS = Dict( "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), - "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1) + "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), + "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), + "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), + "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), ) function test_compute_derivatives_Finite_Diff() @@ -234,17 +237,17 @@ function test_compute_derivatives_Finite_Diff() s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) # Compute derivatives # Δp = [0.001; 0.0; 0.0] - # p_b = p_a + Δp + p_b = p_a + Δp (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) # Check solution # sp = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) - # @test all(isapprox.(sp, sp_approx; atol = 1e-2)) + # @test all(isapprox.(sp, sp_approx; atol = 1e-4)) # Check derivatives using finite differences ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) Δs_fd = ∂s_fd * Δp println("$problem_name: ", model) - if all(isapprox.(Δs, Δs_fd; atol = 1e-2)) + if all(isapprox.(Δs, Δs_fd; atol = 1e-4)) println("All sensitivities are correct") else print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) From 2f9bc7f2a219ccb3731ba2036e04cec10af8c435 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 12 Jul 2024 14:03:08 -0600 Subject: [PATCH 034/108] fix typos --- nlp_utilities.jl | 5 +++-- nlp_utilities_test.jl | 33 +++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index ed7c237d..f9119b6c 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -204,7 +204,7 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe # Primal solution X_lb = zeros(num_low, num_low) - X_ub = zeros(num_up, num_low) + X_ub = zeros(num_up, num_up) V_L = zeros(num_low, num_vars + num_ineq) V_U = zeros(num_up, num_vars + num_ineq) I_L = zeros(num_vars + num_ineq, num_low) @@ -360,7 +360,8 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) if !isempty(r1) @warn "Relaxation needed" - Δs = fix_and_relax(E, K, N, r1, Δp) + num_bounds = length(has_up) + length(has_low) + Δs = fix_and_relax(E, K, N, r1, Δp, num_bounds) sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end return Δs, sp diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 9361d65c..04add88b 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -201,22 +201,38 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end end # dual lower bound primal vars - num_lower_bounds = length([v for v in primal_vars if has_lower_bound(v)]) - for (i, v) in enumerate(primal_vars) - if has_lower_bound(v) && !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-4) + var_lower = [v for v in primal_vars if has_lower_bound(v)] + num_lower_bounds = length(var_lower) + for (i, v) in enumerate(var_lower) + if !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-4) lower_bound_ref = LowerBoundRef(v) println("lower bound dual: ", lower_bound_ref, " | Δs: ", Δs[i + num_w + num_cons], " | Δs_fd: ", Δs_fd[i + num_w + num_cons]) end end # dual upper bound primal vars - for (i, v) in enumerate(primal_vars) - if has_upper_bound(v) && !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-4) + var_upper = [v for v in primal_vars if has_upper_bound(v)] + for (i, v) in enumerate(var_upper) + if !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-4) upper_bound_ref = UpperBoundRef(v) println("upper bound dual: ", upper_bound_ref, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars]) end end end +function create_nonlinear_jump_model_2() + model = Model(Ipopt.Optimizer) + set_silent(model) + @variable(model, p[i=1:3] ∈ MOI.Parameter.([3.0; 2.0; 10.0])) + @variable(model, x_1) + @variable(model, x_2 >= 0.5) + @variable(model, x_3 <= 10) + @constraint(model, g_1, x_1 + x_2 == p[1]) + @constraint(model, g_2, cos(x_1^2) + p[2] * x_2^2 <= 100.0) + @constraint(model, g_3, 1/(x_1 + 1) + 1/(x_2 + 2) + 1/(x_3 + 3) >= 1.0) + @objective(model, Min, x_1^2 + x_2^2 - p[3]*x_3) + return model, [x_1; x_2; x_3], [g_1; g_2; g_3], p +end + DICT_PROBLEMS = Dict( "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), @@ -224,6 +240,7 @@ DICT_PROBLEMS = Dict( "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), + "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2) ) function test_compute_derivatives_Finite_Diff() @@ -232,14 +249,14 @@ function test_compute_derivatives_Finite_Diff() # OPT Problem # p_a = [3.0; 2.0; 200] model, primal_vars, cons, params = model_generator() - ineq_locations = find_inequealities(cons) - # Debugging - s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) + eval_model_jump(model, primal_vars, cons, params, p_a) # Compute derivatives # Δp = [0.001; 0.0; 0.0] p_b = p_a + Δp (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) + ineq_locations = find_inequealities(cons) # Check solution + # s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) # sp = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) # @test all(isapprox.(sp, sp_approx; atol = 1e-4)) # Check derivatives using finite differences From ebe789cdcb6e53ba0a26ca9a80fc8aa02c759dbd Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 12 Jul 2024 14:27:40 -0600 Subject: [PATCH 035/108] adapt signs to obj sense --- nlp_utilities.jl | 3 ++- nlp_utilities_test.jl | 60 ++++++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index f9119b6c..5b9141e5 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -51,11 +51,12 @@ end Compute the optimal Hessian of the Lagrangian. """ function compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) + sense_multiplier = objective_sense(owner_model(x[1])) == MOI.MIN_SENSE ? -1.0 : 1.0 hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) I = [i for (i, _) in hessian_sparsity] J = [j for (_, j) in hessian_sparsity] V = zeros(length(hessian_sparsity)) - MOI.eval_hessian_lagrangian(evaluator, V, value.(x), -1.0, dual.(rows)) + MOI.eval_hessian_lagrangian(evaluator, V, value.(x), sense_multiplier, dual.(rows)) H = SparseArrays.sparse(I, J, V, length(x), length(x)) return Matrix(fill_off_diagonal(H)) end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 04add88b..db467776 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -219,35 +219,59 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end end -function create_nonlinear_jump_model_2() +function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]) model = Model(Ipopt.Optimizer) set_silent(model) - @variable(model, p[i=1:3] ∈ MOI.Parameter.([3.0; 2.0; 10.0])) - @variable(model, x_1) - @variable(model, x_2 >= 0.5) - @variable(model, x_3 <= 10) - @constraint(model, g_1, x_1 + x_2 == p[1]) - @constraint(model, g_2, cos(x_1^2) + p[2] * x_2^2 <= 100.0) - @constraint(model, g_3, 1/(x_1 + 1) + 1/(x_2 + 2) + 1/(x_3 + 3) >= 1.0) - @objective(model, Min, x_1^2 + x_2^2 - p[3]*x_3) - return model, [x_1; x_2; x_3], [g_1; g_2; g_3], p + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x <= 10) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x <= 10) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + return model, [x; y], [con1; con2; con3], p end DICT_PROBLEMS = Dict( - "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), - "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), - "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), - "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), - "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), - "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), - "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2) + # "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), + # "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), + # "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), + # "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), + # "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), + # "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), + "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), + "NLP_3" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_3), ) function test_compute_derivatives_Finite_Diff() # @testset "Compute Derivatives: $problem_name" for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS # OPT Problem - # p_a = [3.0; 2.0; 200] model, primal_vars, cons, params = model_generator() eval_model_jump(model, primal_vars, cons, params, p_a) # Compute derivatives From 4762fb04985b1fc662a244ea88b5814d243caf77 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 12 Jul 2024 15:15:20 -0600 Subject: [PATCH 036/108] add comments --- nlp_utilities.jl | 56 +++++++++++++++++++----- nlp_utilities_test.jl | 99 ++++++++++++++++++++++--------------------- 2 files changed, 96 insertions(+), 59 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 5b9141e5..649a5fb3 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -155,7 +155,12 @@ function get_slack_inequality(con::ConstraintRef) return obj.func - obj.set.lower end -function compute_solution_and_bounds(primal_vars, cons) +""" + compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vector{C}) where C<:ConstraintRef + +Compute the solution and bounds of the primal variables. +""" +function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vector{C}) where {C<:ConstraintRef} num_vars = length(primal_vars) ineq_locations = find_inequealities(cons) num_ineq = length(ineq_locations) @@ -187,6 +192,15 @@ function compute_solution_and_bounds(primal_vars, cons) return X, V_L, X_L, V_U, X_U, ineq_locations, has_up, vcat(has_low, collect(num_vars+1:num_vars+num_ineq)) end +""" + build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, + primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, + _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + has_up::Vector{Z}, has_low::Vector{Z} +) where {T<:Real, Z<:Integer} + +Build the M (KKT Jacobian w.r.t. solution) and N (KKT Jacobian w.r.t. parameters) matrices for the sensitivity analysis. +""" function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, @@ -269,9 +283,13 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe end """ - compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}; primal_vars::Vector{VariableRef}=all_primal_vars(model), params::Vector{VariableRef}=all_params(model)) + compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, + primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, + _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + has_up::Vector{Z}, has_low::Vector{Z} + ) where {T<:Real, Z<:Integer} -Compute the derivatives of the solution with respect to the parameters without accounting for active set changes. +Compute the derivatives of the solution w.r.t. the parameters without accounting for active set changes. """ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, @@ -281,13 +299,18 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: num_bounds = length(has_up) + length(has_low) M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, ineq_locations, has_up, has_low) - # Sesitivity of the solution (primal-dual_constraints-dual_bounds) with respect to the parameters + # Sesitivity of the solution (primal-dual_constraints-dual_bounds) w.r.t. the parameters K = qr(M) # Factorization ∂s = - (K \ N) # Sensitivity ∂s[end-num_bounds+1:end,:] = ∂s[end-num_bounds+1:end,:] .* -1.0 # Correcting the sign of the bounds duals for the standard form return ∂s, K, N end +""" + fix_and_relax(E, K, N, r1, Δp, num_bounds) + +Fix the violations and relax complementary slackness. +""" function fix_and_relax(E, K, N, r1, Δp, num_bounds) rs = N * Δp # C = −E' inv(K) E @@ -300,10 +323,20 @@ function fix_and_relax(E, K, N, r1, Δp, num_bounds) return ∆s end +""" + approximate_solution(X, Λ, V_L, V_U, Δs) + +First order approximation the primal-dual solution. +""" function approximate_solution(X, Λ, V_L, V_U, Δs) return [X; Λ; V_L; V_U] .+ Δs end +""" + find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + +Find the bound violations of the primal-dual solution first order approximation based on the (unrelaxed) sensitivity estimation. +""" function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) num_w = length(X) num_low = length(has_low) @@ -340,12 +373,8 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t return E, r1 end -""" - compute_derivatives(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) -Compute the derivatives of the solution with respect to the parameters. -""" -function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, +function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-8 ) where {T<:Real} num_cons = length(cons) @@ -368,7 +397,12 @@ function compute_derivatives(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co return Δs, sp end -function compute_derivatives(model::Model, Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} +""" + compute_sensitivity(model::Model; primal_vars=all_primal_vars(model), params=all_params(model)) + +Compute the sensitivity of the solution given sensitivity of the parameters (Δp). +""" +function compute_sensitivity(model::Model, Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} evaluator, cons = create_evaluator(model; x=[primal_vars; params]) - return compute_derivatives(evaluator, cons, Δp; primal_vars=primal_vars, params=params), evaluator, cons + return compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=params), evaluator, cons end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index db467776..744c1aaa 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -5,12 +5,15 @@ using FiniteDiff include("nlp_utilities.jl") +################################################ #= # Test JuMP Hessian and Jacobian From JuMP Tutorial for Querying Hessians: https://github.com/jump-dev/JuMP.jl/blob/301d46e81cb66c74c6e22cd89fb89ced740f157b/docs/src/tutorials/nonlinear/querying_hessians.jl#L67-L72 =# +################################################ + function create_nonlinear_jump_model() model = Model(Ipopt.Optimizer) set_silent(model) @@ -93,6 +96,7 @@ end From sIpopt paper: https://optimization-online.org/2011/04/3008/ =# +################################################ function create_nonlinear_jump_model_sipopt() model = Model(Ipopt.Optimizer) @@ -132,7 +136,7 @@ function test_compute_derivatives() s_pb_approx_violated = s_pa + ∂s * Δp @test all(isapprox.([0.5765; 0.3775; -0.0459; 0.1327; 0.3571; 0.0; 0.0; 0.0], s_pb_approx_violated; atol = 1e-4)) # Account for active set changes - Δs, s_pb_approx = compute_derivatives(evaluator, rows, Δp; primal_vars, params) + Δs, s_pb_approx = compute_sensitivity(evaluator, rows, Δp; primal_vars, params) # s_pb_approx = s_pa .+ Δs @test all(isapprox.(s_pb, s_pb_approx; atol = 1e-4)) end @@ -142,6 +146,7 @@ end #= # Test Sensitivity through finite differences =# +################################################ function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]) model = Model(Ipopt.Optimizer) @@ -163,6 +168,44 @@ function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]) return model, [x; y], [con1; con2; con3], p end +function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x <= 10) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x <= 10) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + return model, [x; y], [con1; con2; con3], p +end + function eval_model_jump(model, primal_vars, cons, params, p_val) set_parameter_value.(params, p_val) optimize!(model) @@ -171,8 +214,6 @@ function eval_model_jump(model, primal_vars, cons, params, p_val) end function stack_solution(cons, ineq_locations, x, _λ, ν_L, ν_U) - # λ = deepcopy(_λ) - # λ[ineq_locations] = _λ[ineq_locations] .* -1 return Float64[x; value.(get_slack_inequality.(cons[ineq_locations])); _λ; ν_L; _λ[ineq_locations]; ν_U] end @@ -219,51 +260,13 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end end -function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x <= 10) - @variable(model, y) - - # Constraints - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective - return model, [x; y], [con1; con2; con3], p -end - -function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x <= 10) - @variable(model, y) - - # Constraints - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective - return model, [x; y], [con1; con2; con3], p -end - DICT_PROBLEMS = Dict( - # "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), - # "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), - # "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), - # "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), - # "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), - # "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), + "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), + "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), + "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), + "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), + "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), + "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), "NLP_3" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_3), ) @@ -277,7 +280,7 @@ function test_compute_derivatives_Finite_Diff() # Compute derivatives # Δp = [0.001; 0.0; 0.0] p_b = p_a + Δp - (Δs, sp_approx), evaluator, cons = compute_derivatives(model, Δp; primal_vars, params) + (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) ineq_locations = find_inequealities(cons) # Check solution # s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) From 18e8bf84c30d3b4233a7729f8881aeebf0ebcbdc Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Fri, 12 Jul 2024 16:52:35 -0600 Subject: [PATCH 037/108] debug fix and relax --- nlp_utilities_test.jl | 2 +- testing_barrier.jl | 33 +++------------------------------ 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 744c1aaa..f40d6aea 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -119,7 +119,7 @@ function test_compute_derivatives() # Analytical solutions case b pb = [4.5, 1.0] s_pb = [0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] - @assert all(isapprox(value.(primal_vars), s_pb; atol = 1e-6)) + @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pb; atol = 1e-6)) # Analytical solutions case a pa = [5.0, 1.0] s_pa = [0.6327, 0.3878, 0.0204, 0.1633, 0.2857, 0, 0, 0] diff --git a/testing_barrier.jl b/testing_barrier.jl index d8d87d76..f69a58a2 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -12,36 +12,9 @@ using Ipopt include("nlp_utilities.jl") include("nlp_utilities_test.jl") -############ -# Test Case 1 -############ -# Define the problem -par_model = Model(Ipopt.Optimizer) +test_compute_optimal_hess_jacobian() -# Parameters -@variable(par_model, p ∈ MOI.Parameter(1.0)) -@variable(par_model, p2 ∈ MOI.Parameter(2.0)) +test_compute_derivatives() -# Variables -@variable(par_model, x) -@variable(par_model, y) - -# Constraints -@constraint(par_model, con1, y >= p*sin(x)) # NLP Constraint -@constraint(par_model, con2, x + y == p) -@constraint(par_model, con3, p2 * x >= 0.1) -@objective(par_model, Min, (1 - x)^2 + 100 * (y - x^2)^2) # NLP Objective -optimize!(par_model) - -# Check local optimality -termination_status(par_model) - -############ -# Sensitivity Analysis -############ - -∂s, evaluator, rows = compute_derivatives(par_model) - - -test_compute_optimal_hess_jacobian() \ No newline at end of file +test_compute_derivatives_Finite_Diff() \ No newline at end of file From 02592a95d9248239127667416d91eaea4ac0fec4 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 15 Jul 2024 13:13:31 -0600 Subject: [PATCH 038/108] update tests --- nlp_utilities.jl | 2 +- nlp_utilities_test.jl | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 649a5fb3..95012b0e 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -384,7 +384,7 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) Δs = ∂s * Δp - Λ = -dual.(cons) + Λ = dual.(cons) sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index f40d6aea..9162aa3b 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -83,7 +83,7 @@ function test_compute_optimal_hess_jacobian() full_hessian, full_jacobian = compute_optimal_hess_jac(evaluator, rows, [x; params]) hessian = full_hessian[1:num_var, 1:num_var] # Check Hessian - @test all(hessian .≈ analytic_hessian(value.(x), 1.0, dual.(cons), value.(params))) + @test all(hessian .≈ analytic_hessian(value.(x), -1.0, dual.(cons), value.(params))) # TODO: Test hessial of parameters # Check Jacobian @test all(full_jacobian .≈ analytic_jacobian(value.(x), value.(params))) @@ -138,7 +138,7 @@ function test_compute_derivatives() # Account for active set changes Δs, s_pb_approx = compute_sensitivity(evaluator, rows, Δp; primal_vars, params) # s_pb_approx = s_pa .+ Δs - @test all(isapprox.(s_pb, s_pb_approx; atol = 1e-4)) + @test all(isapprox.([0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], s_pb_approx; atol = 1e-6)) end end @@ -222,7 +222,7 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) # primal vars num_primal_vars = length(primal_vars) for (i, v) in enumerate(primal_vars) - if !isapprox(Δs[i], Δs_fd[i]; atol = 1e-4) + if !isapprox(Δs[i], Δs_fd[i]; atol = 1e-6) println("Primal var: ", v, " | Δs: ", Δs[i], " | Δs_fd: ", Δs_fd[i]) end end @@ -230,14 +230,14 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) num_slack_vars = length(ineq_locations) num_w = num_slack_vars + num_primal_vars for (i, c) in enumerate(cons[ineq_locations]) - if !isapprox(Δs[i + num_primal_vars], Δs_fd[i + num_primal_vars] ; atol = 1e-4) + if !isapprox(Δs[i + num_primal_vars], Δs_fd[i + num_primal_vars] ; atol = 1e-6) println("Slack var: ", c, " | Δs: ", Δs[i + num_primal_vars], " | Δs_fd: ", Δs_fd[i + num_primal_vars]) end end # dual vars num_cons = length(cons) for (i, c) in enumerate(cons) - if !isapprox(Δs[i + num_w], Δs_fd[i + num_w] ; atol = 1e-4) + if !isapprox(Δs[i + num_w], Δs_fd[i + num_w] ; atol = 1e-6) println("Dual var: ", c, " | Δs: ", Δs[i + num_w], " | Δs_fd: ", Δs_fd[i + num_w]) end end @@ -245,15 +245,21 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) var_lower = [v for v in primal_vars if has_lower_bound(v)] num_lower_bounds = length(var_lower) for (i, v) in enumerate(var_lower) - if !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-4) + if !isapprox(Δs[i + num_w + num_cons], Δs_fd[i + num_w + num_cons] ; atol = 1e-6) lower_bound_ref = LowerBoundRef(v) println("lower bound dual: ", lower_bound_ref, " | Δs: ", Δs[i + num_w + num_cons], " | Δs_fd: ", Δs_fd[i + num_w + num_cons]) end end + # dual lower bound slack vars + for (i, c) in enumerate(cons[ineq_locations]) + if !isapprox(Δs[i + num_w + num_cons + num_lower_bounds], Δs_fd[i + num_w + num_cons + num_lower_bounds] ; atol = 1e-6) + println("lower bound slack dual: ", c, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds]) + end + end # dual upper bound primal vars var_upper = [v for v in primal_vars if has_upper_bound(v)] for (i, v) in enumerate(var_upper) - if !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-4) + if !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars] ; atol = 1e-6) upper_bound_ref = UpperBoundRef(v) println("upper bound dual: ", upper_bound_ref, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds + num_slack_vars], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds + num_slack_vars]) end @@ -269,6 +275,10 @@ DICT_PROBLEMS = Dict( "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), "NLP_3" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_3), + "NLP_3_2" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_3), + "NLP_3_3" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_3), + "NLP_3_4" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_3), + "NLP_3_5" => (p_a=[3.0; 2.0; 10], Δp=[0.01; 0.03; 0.1], model_generator=create_nonlinear_jump_model_3), ) function test_compute_derivatives_Finite_Diff() @@ -285,13 +295,13 @@ function test_compute_derivatives_Finite_Diff() # Check solution # s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) # sp = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) - # @test all(isapprox.(sp, sp_approx; atol = 1e-4)) + # @test all(isapprox.(sp, sp_approx; atol = 1e-6)) # Check derivatives using finite differences ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) Δs_fd = ∂s_fd * Δp println("$problem_name: ", model) - if all(isapprox.(Δs, Δs_fd; atol = 1e-4)) + if all(isapprox.(Δs, Δs_fd; atol = 1e-6)) println("All sensitivities are correct") else print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) From 763d0c047c6f7a593b88a640dcdcb7e41acec74a Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 15 Jul 2024 13:26:31 -0600 Subject: [PATCH 039/108] use sparse matrix --- nlp_utilities.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 95012b0e..828fd824 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -58,7 +58,7 @@ function compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vecto V = zeros(length(hessian_sparsity)) MOI.eval_hessian_lagrangian(evaluator, V, value.(x), sense_multiplier, dual.(rows)) H = SparseArrays.sparse(I, J, V, length(x), length(x)) - return Matrix(fill_off_diagonal(H)) + return fill_off_diagonal(H) end """ @@ -73,7 +73,7 @@ function compute_optimal_jacobian(evaluator::MOI.Nonlinear.Evaluator, rows::Vect V = zeros(length(jacobian_sparsity)) MOI.eval_constraint_jacobian(evaluator, V, value.(x)) A = SparseArrays.sparse(I, J, V, length(rows), length(x)) - return Matrix(A) + return A # Matrix(A) end """ From 41b88d97fc11921441ac7080809aaf0989a22bd1 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 15 Jul 2024 15:23:19 -0600 Subject: [PATCH 040/108] add tests with variable constrained by equalto --- nlp_utilities_test.jl | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 9162aa3b..bca7aab1 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -206,6 +206,48 @@ function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]) return model, [x; y], [con1; con2; con3], p end +function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x) + @variable(model, y) + + # Constraints + @constraint(model, con0, x == p[1] - 0.5) + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_4_2(p_val = [1.0; 2.0; 100]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x) + @variable(model, y) + + # Constraints + @constraint(model, con0, x == 0.5) + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + + return model, [x; y], [con0; con1; con2; con3], p +end + function eval_model_jump(model, primal_vars, cons, params, p_val) set_parameter_value.(params, p_val) optimize!(model) @@ -279,6 +321,8 @@ DICT_PROBLEMS = Dict( "NLP_3_3" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_3), "NLP_3_4" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_3), "NLP_3_5" => (p_a=[3.0; 2.0; 10], Δp=[0.01; 0.03; 0.1], model_generator=create_nonlinear_jump_model_3), + "NLP_4" => (p_a=[1.0; 2.0; 100], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_4), + "NLP_4_2" => (p_a=[1.0; 2.0; 100], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_4), ) function test_compute_derivatives_Finite_Diff() From 7d91082aad17515de51dd437b9e72a737c3613aa Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 15 Jul 2024 15:33:35 -0600 Subject: [PATCH 041/108] allow variable fix --- nlp_utilities.jl | 2 +- nlp_utilities_test.jl | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 828fd824..4799b583 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -13,7 +13,7 @@ function create_nlp_model(model::JuMP.Model) rows = Vector{ConstraintRef}(undef, 0) nlp = MOI.Nonlinear.Model() for (F, S) in list_of_constraint_types(model) - if F <: VariableRef + if F <: VariableRef && !(S <: MathOptInterface.EqualTo{Float64}) continue # Skip variable bounds end for ci in all_constraints(model, F, S) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index bca7aab1..3ab9738e 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -227,7 +227,7 @@ function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]) return model, [x; y], [con1; con2; con3], p end -function create_nonlinear_jump_model_4_2(p_val = [1.0; 2.0; 100]) +function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]) model = Model(Ipopt.Optimizer) set_silent(model) @@ -239,7 +239,8 @@ function create_nonlinear_jump_model_4_2(p_val = [1.0; 2.0; 100]) @variable(model, y) # Constraints - @constraint(model, con0, x == 0.5) + fix(x, 0.5) + con0 = JuMP.FixRef(x) @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint @constraint(model, con2, x + y == p[1]) @constraint(model, con3, p[2] * x >= 0.1) @@ -322,7 +323,7 @@ DICT_PROBLEMS = Dict( "NLP_3_4" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_3), "NLP_3_5" => (p_a=[3.0; 2.0; 10], Δp=[0.01; 0.03; 0.1], model_generator=create_nonlinear_jump_model_3), "NLP_4" => (p_a=[1.0; 2.0; 100], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_4), - "NLP_4_2" => (p_a=[1.0; 2.0; 100], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_4), + "NLP_5" => (p_a=[1.0; 2.0; 100], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_5), ) function test_compute_derivatives_Finite_Diff() From 2538e9c1411a9970c179011b43e898f389357251 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 15 Jul 2024 16:02:11 -0600 Subject: [PATCH 042/108] add more tests --- nlp_utilities_test.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 3ab9738e..faed4743 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -310,18 +310,21 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end DICT_PROBLEMS = Dict( - "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model), + "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[-0.5; 0.5; 0.1], model_generator=create_nonlinear_jump_model), "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), + "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; -0.01; 0.001], model_generator=create_nonlinear_jump_model_1), "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), + "NLP_2_2" => (p_a=[3.0; 2.0; 10], Δp=[-0.01; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), "NLP_3" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_3), "NLP_3_2" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_3), "NLP_3_3" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_3), "NLP_3_4" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_3), "NLP_3_5" => (p_a=[3.0; 2.0; 10], Δp=[0.01; 0.03; 0.1], model_generator=create_nonlinear_jump_model_3), + "NLP_3_6" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.000; -0.01], model_generator=create_nonlinear_jump_model_3), "NLP_4" => (p_a=[1.0; 2.0; 100], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_4), "NLP_5" => (p_a=[1.0; 2.0; 100], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_5), ) @@ -349,6 +352,7 @@ function test_compute_derivatives_Finite_Diff() if all(isapprox.(Δs, Δs_fd; atol = 1e-6)) println("All sensitivities are correct") else + @show Δp print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end println("--------------------") From a3f8e1be15990ab8ce6e58cde702114697509f65 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Mon, 15 Jul 2024 16:49:28 -0600 Subject: [PATCH 043/108] start test bilevel --- testing_barrier.jl | 76 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index f69a58a2..fc2591d6 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -17,4 +17,78 @@ test_compute_optimal_hess_jacobian() test_compute_derivatives() -test_compute_derivatives_Finite_Diff() \ No newline at end of file +test_compute_derivatives_Finite_Diff() + + +####################### +# Test Bilevel (WIP) +####################### + +using BilevelJuMP, Ipopt + +function constraint_upper!(model, x, y) + @constraints(model, begin + x <= 5 + y <= 8 + y >= 0 + end) +end + +function obj_upper!(model, x, y) + @objective(model, Min, 3x + y) +end + +function build_upper!(model, x, y) + obj_upper!(model, x, y) + constraint_upper!(model, x, y) +end + +function constraint_lower!(model, x, y) + @constraints(model, begin + x + y <= 8 + 4x + y >= 8 + 2x + y <= 13 + 2x - 7y <= 0 + end) +end + +function obj_lower!(model, x, y) + @objective(model, Min, -x) +end + +function build_lower!(model, x, y) + obj_lower!(model, x, y) + constraint_lower!(model, x, y) +end + +### Test BilevelJuMP ### + +model = BilevelModel(Ipopt.Optimizer, mode = BilevelJuMP.ProductMode(1e-5)) + +@variable(Lower(model), x) +@variable(Upper(model), y) + +build_upper!(Upper(model), x, y) +build_lower!(Lower(model), x, y) +optimize!(model) + +objective_value(model) # = 3 * (3.5 * 8/15) + 8/15 # = 6.13... +value(x) # = 3.5 * 8/15 # = 1.86... +value(y) # = 8/15 # = 0.53... + +### Test DiffOpt Non-Convex ### + +model_upper = Model(Ipopt.Optimizer) +model_lower = Model(Ipopt.Optimizer) + +@variable(model_upper, y) +@variable(model_upper, x_star) +@variable(model_upper, x_aux) +@variable(model_lower, x) +@variable(model_lower, y_p ∈ MOI.Parameter(1.0)) + +build_upper!(model_upper, x_star, y) +constraint_lower!(model_upper, x_aux, y) +build_lower!(model_lower, x, y_p) + +# Define `f(y) = x_star` & `f'(y)` \ No newline at end of file From eabce334eb0b7b97aae6dac2fa6914aa4f948635 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 17 Jul 2024 11:49:00 -0600 Subject: [PATCH 044/108] pass all to sparse --- nlp_utilities.jl | 75 +++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 4799b583..b145e659 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -172,8 +172,8 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec X = value.([primal_vars; slack_vars]) # value and dual of the lower bounds - V_L = zeros(num_vars+num_ineq) - X_L = zeros(num_vars+num_ineq) + V_L = spzeros(num_vars+num_ineq) + X_L = spzeros(num_vars+num_ineq) for (i, j) in enumerate(has_low) V_L[i] = dual.(LowerBoundRef(primal_vars[j])) X_L[i] = JuMP.lower_bound(primal_vars[j]) @@ -182,8 +182,8 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec V_L[num_vars+i] = dual.(con) end # value and dual of the upper bounds - V_U = zeros(num_vars+num_ineq) - X_U = zeros(num_vars+num_ineq) + V_U = spzeros(num_vars+num_ineq) + X_U = spzeros(num_vars+num_ineq) for (i, j) in enumerate(has_up) V_U[i] = dual.(UpperBoundRef(primal_vars[j])) X_U[i] = JuMP.upper_bound(primal_vars[j]) @@ -195,17 +195,17 @@ end """ build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + _X::Vector, _V_L::Vector, _X_L::Vector, _V_U::Vector, _X_U::Vector, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} -) where {T<:Real, Z<:Integer} +) where {Z<:Integer} Build the M (KKT Jacobian w.r.t. solution) and N (KKT Jacobian w.r.t. parameters) matrices for the sensitivity analysis. """ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + _X::AbstractVector, _V_L::AbstractVector, _X_L::AbstractVector, _V_U::AbstractVector, _X_U::AbstractVector, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} -) where {T<:Real, Z<:Integer} +) where {Z<:Integer} @assert all(x -> is_parameter(x), params) "All parameters must be parameters" # Setting @@ -218,12 +218,12 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe num_up = length(has_up) # Primal solution - X_lb = zeros(num_low, num_low) - X_ub = zeros(num_up, num_up) - V_L = zeros(num_low, num_vars + num_ineq) - V_U = zeros(num_up, num_vars + num_ineq) - I_L = zeros(num_vars + num_ineq, num_low) - I_U = zeros(num_vars + num_ineq, num_up) + X_lb = spzeros(num_low, num_low) + X_ub = spzeros(num_up, num_up) + V_L = spzeros(num_low, num_vars + num_ineq) + V_U = spzeros(num_up, num_vars + num_ineq) + I_L = spzeros(num_vars + num_ineq, num_low) + I_U = spzeros(num_vars + num_ineq, num_up) # value and dual of the lower bounds for (i, j) in enumerate(has_low) @@ -242,16 +242,16 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe hessian, jacobian = compute_optimal_hess_jac(evaluator, cons, all_vars) # Hessian of the lagrangian wrt the primal variables - W = zeros(num_vars + num_ineq, num_vars + num_ineq) + W = spzeros(num_vars + num_ineq, num_vars + num_ineq) W[1:num_vars, 1:num_vars] = hessian[1:num_vars, 1:num_vars] # Jacobian of the constraints wrt the primal variables - A = zeros(num_cons, num_vars + num_ineq) + A = spzeros(num_cons, num_vars + num_ineq) A[:, 1:num_vars] = jacobian[:, 1:num_vars] for (i,j) in enumerate(ineq_locations) A[j, num_vars+i] = -1 end # Partial second derivative of the lagrangian wrt primal solution and parameters - ∇ₓₚL = zeros(num_vars + num_ineq, num_parms) + ∇ₓₚL = spzeros(num_vars + num_ineq, num_parms) ∇ₓₚL[1:num_vars, :] = hessian[1:num_vars, num_vars+1:end] # Partial derivative of the equality constraintswith wrt parameters ∇ₚC = jacobian[:, num_vars+1:end] @@ -264,7 +264,7 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe # [V_U 0 0 0 (X_U - X)] # ] len_w = num_vars + num_ineq - M = zeros(len_w + num_cons + num_low + num_up, len_w + num_cons + num_low + num_up) + M = spzeros(len_w + num_cons + num_low + num_up, len_w + num_cons + num_low + num_up) M[1:len_w, 1:len_w] = W M[1:len_w, len_w + 1 : len_w + num_cons] = A' @@ -277,7 +277,10 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe M[1:len_w, len_w+num_cons+num_low+1:end] = I_U # N matrix - N = [∇ₓₚL ; ∇ₚC; zeros(num_low + num_up, num_parms)] + # N = [∇ₓₚL ; ∇ₚC; zeros(num_low + num_up, num_parms)] + N = spzeros(len_w + num_cons + num_low + num_up, num_parms) + N[1:len_w, :] = ∇ₓₚL + N[len_w+1:len_w+num_cons, :] = ∇ₚC return M, N end @@ -285,23 +288,26 @@ end """ compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + _X::Vector, _V_L::Vector, _X_L::Vector, _V_U::Vector, _X_U::Vector, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} - ) where {T<:Real, Z<:Integer} + ) where {Z<:Integer} Compute the derivatives of the solution w.r.t. the parameters without accounting for active set changes. """ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::Vector{T}, _V_L::Vector{T}, _X_L::Vector{T}, _V_U::Vector{T}, _X_U::Vector{T}, ineq_locations::Vector{Z}, + _X::AbstractVector, _V_L::AbstractVector, _X_L::AbstractVector, _V_U::AbstractVector, _X_U::AbstractVector, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} -) where {T<:Real, Z<:Integer} +) where {Z<:Integer} num_bounds = length(has_up) + length(has_low) M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, ineq_locations, has_up, has_low) # Sesitivity of the solution (primal-dual_constraints-dual_bounds) w.r.t. the parameters - K = qr(M) # Factorization - ∂s = - (K \ N) # Sensitivity + K = lu(M) # Factorization + ∂s = zeros(size(M, 1), size(N, 2)) + # ∂s = - (K \ N) # Sensitivity + ldiv!(∂s, K, N) + ∂s = - ∂s ∂s[end-num_bounds+1:end,:] = ∂s[end-num_bounds+1:end,:] .* -1.0 # Correcting the sign of the bounds duals for the standard form return ∂s, K, N end @@ -312,13 +318,22 @@ end Fix the violations and relax complementary slackness. """ function fix_and_relax(E, K, N, r1, Δp, num_bounds) - rs = N * Δp + rs = (N * Δp)[:,:] # C = −E' inv(K) E - C = - E' * (K \ E) + # C = - E' * (K \ E) + C = zeros(size(K, 1), size(E, 2)) + ldiv!(C, K, E) + C = - E' * C # C ∆ν¯ = E' inv(K) rs − r1 - ∆ν¯ = C \ (E' * (K \ rs) - r1) + # ∆ν¯ = C \ (E' * (K \ rs) - r1) + aux = zeros(size(K, 1), size(rs, 2)) + ldiv!(aux, K, rs) + ∆ν¯ = C \ (E' * aux - r1) # K ∆s = − (rs + E∆ν¯) - ∆s = K \ (- (rs + E * ∆ν¯)) + # ∆s = K \ (- (rs + E * ∆ν¯)) + aux = - (rs + E * ∆ν¯)[:,:] + ∆s = zeros(size(K, 1), size(aux, 2)) + ldiv!(∆s, K, aux) ∆s[end-num_bounds+1:end] = ∆s[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form return ∆s end @@ -365,7 +380,7 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t end end - E = zeros(num_w + num_cons + num_low + num_up, length(_E)) + E = spzeros(num_w + num_cons + num_low + num_up, length(_E)) for (i, j) in enumerate(_E) E[j, i] = 1 end From c2d73cd527dda334928a215ee8bb74aaa7a1620d Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Wed, 17 Jul 2024 16:38:01 -0600 Subject: [PATCH 045/108] add bilevel test --- testing_barrier.jl | 83 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index fc2591d6..25b2ff1f 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -43,38 +43,39 @@ function build_upper!(model, x, y) constraint_upper!(model, x, y) end -function constraint_lower!(model, x, y) +function constraint_lower!(model, x, y; slack = zeros(4)) @constraints(model, begin - x + y <= 8 - 4x + y >= 8 - 2x + y <= 13 - 2x - 7y <= 0 + x + y + slack[1] <= 8 + 4x + y + slack[2] >= 8 + 2x + y + slack[3] <= 13 + 2x - 7y + slack[4] <= 0 end) end -function obj_lower!(model, x, y) - @objective(model, Min, -x) +function obj_lower!(model, x, y, slack) + @objective(model, Min, -x + 10 * sum(s^2 for s in slack)) end -function build_lower!(model, x, y) - obj_lower!(model, x, y) - constraint_lower!(model, x, y) +function build_lower!(model, x, y, slack) + obj_lower!(model, x, y, slack) + constraint_lower!(model, x, y, slack = slack) end ### Test BilevelJuMP ### +using BilevelJuMP model = BilevelModel(Ipopt.Optimizer, mode = BilevelJuMP.ProductMode(1e-5)) -@variable(Lower(model), x) -@variable(Upper(model), y) +@variable(Lower(model), x_b) +@variable(Upper(model), y_b) -build_upper!(Upper(model), x, y) -build_lower!(Lower(model), x, y) +build_upper!(Upper(model), x_b, y_b) +build_lower!(Lower(model), x_b, y_b, zeros(4)) optimize!(model) objective_value(model) # = 3 * (3.5 * 8/15) + 8/15 # = 6.13... -value(x) # = 3.5 * 8/15 # = 1.86... -value(y) # = 8/15 # = 0.53... +value(x_b) # = 3.5 * 8/15 # = 1.86... +value(y_b) # = 8/15 # = 0.53... ### Test DiffOpt Non-Convex ### @@ -86,9 +87,55 @@ model_lower = Model(Ipopt.Optimizer) @variable(model_upper, x_aux) @variable(model_lower, x) @variable(model_lower, y_p ∈ MOI.Parameter(1.0)) +@variable(model_lower, slack[1:4]) build_upper!(model_upper, x_star, y) constraint_lower!(model_upper, x_aux, y) -build_lower!(model_lower, x, y_p) +build_lower!(model_lower, x, y_p, slack) +primal_vars = [x; slack] +params = [y_p] +evaluator, cons = create_evaluator(model_lower; x=[primal_vars; params]) + +# Define `f(y) = x_star` & `f'(y)` +function f(y_val) + set_parameter_value(y_p, y_val) + optimize!(model_lower) + @assert is_solved_and_feasible(model_lower) + return value(x) +end + +function ∇f(y_val) + @assert value(y_p) == y_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) + return Δs[1] +end + +function memoize(foo::Function) + last_x, last_f = nothing, nothing + last_dx, last_dfdx = nothing, nothing + function foo_i(x::T...) where {T<:Real} + if T == Float64 + if x !== last_x + last_x, last_f = x, foo(x...) + end + return last_f::T + else + if x !== last_dx + last_dx, last_dfdx = x, foo(x...) + end + return last_dfdx::T + end + end + return (x...) -> foo_i(x...) +end + +memoized_f = memoize(f) + +@operator(model_upper, op_f, 1, memoized_f, ∇f) +@constraint(model_upper, x_star == op_f(y)) + +optimize!(model_upper) -# Define `f(y) = x_star` & `f'(y)` \ No newline at end of file +@test objective_value(model_upper) ≈ objective_value(model) atol=0.05 +@test value(x_star) ≈ value(x_b) atol=0.05 +@test value(y) ≈ value(y_b) atol=0.05 \ No newline at end of file From dda646933c460a841d24989075198f880014d35f Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 24 Jul 2024 17:48:00 -0600 Subject: [PATCH 046/108] update fix tolerance test --- nlp_utilities.jl | 76 +++++++++++++++++++++++++++++------------ nlp_utilities_test.jl | 79 ++++++++++++++++++++++++++++++------------- 2 files changed, 110 insertions(+), 45 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index b145e659..0ff37ecc 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -120,9 +120,23 @@ end Check if the constraint is an inequality. """ -function is_inequality(con::ConstraintRef) +# function is_inequality(con::ConstraintRef) +# set_type = typeof(MOI.get(owner_model(con), MOI.ConstraintSet(), con)) +# return set_type <: MOI.LessThan || set_type <: MOI.GreaterThan +# end + +function is_less_inequality(con::ConstraintRef) set_type = typeof(MOI.get(owner_model(con), MOI.ConstraintSet(), con)) - return set_type <: MOI.LessThan || set_type <: MOI.GreaterThan + return set_type <: MOI.LessThan +end + +function is_greater_inequality(con::ConstraintRef) + set_type = typeof(MOI.get(owner_model(con), MOI.ConstraintSet(), con)) + return set_type <: MOI.GreaterThan +end + +function is_inequality(con::ConstraintRef) + return is_less_inequality(con) || is_greater_inequality(con) end """ @@ -131,13 +145,17 @@ end Find the indices of the inequality constraints. """ function find_inequealities(cons::Vector{C}) where C<:ConstraintRef - ineq_locations = zeros(length(cons)) + leq_locations = zeros(length(cons)) + geq_locations = zeros(length(cons)) for i in 1:length(cons) - if is_inequality(cons[i]) - ineq_locations[i] = true + if is_less_inequality(cons[i]) + leq_locations[i] = true + end + if is_greater_inequality(cons[i]) + geq_locations[i] = true end end - return findall(x -> x ==1, ineq_locations) + return findall(x -> x ==1, leq_locations), findall(x -> x ==1, geq_locations) end """ @@ -150,7 +168,7 @@ function get_slack_inequality(con::ConstraintRef) obj = constraint_object(con) if set_type <: MOI.LessThan # c(x) <= b --> slack = -c(x) + b | slack >= 0 - return - obj.func + obj.set.upper + return obj.func - obj.set.upper end return obj.func - obj.set.lower end @@ -162,8 +180,11 @@ Compute the solution and bounds of the primal variables. """ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vector{C}) where {C<:ConstraintRef} num_vars = length(primal_vars) - ineq_locations = find_inequealities(cons) - num_ineq = length(ineq_locations) + leq_locations, geq_locations = find_inequealities(cons) + ineq_locations = vcat(geq_locations, leq_locations) + num_leq = length(leq_locations) + num_geq = length(geq_locations) + num_ineq = num_leq + num_geq slack_vars = [get_slack_inequality(cons[i]) for i in ineq_locations] has_up = findall(x -> has_upper_bound(x), primal_vars) has_low = findall(x -> has_lower_bound(x), primal_vars) @@ -178,7 +199,7 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec V_L[i] = dual.(LowerBoundRef(primal_vars[j])) X_L[i] = JuMP.lower_bound(primal_vars[j]) end - for (i, con) in enumerate(cons[ineq_locations]) + for (i, con) in enumerate(cons[geq_locations]) V_L[num_vars+i] = dual.(con) end # value and dual of the upper bounds @@ -188,8 +209,11 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec V_U[i] = dual.(UpperBoundRef(primal_vars[j])) X_U[i] = JuMP.upper_bound(primal_vars[j]) end + for (i, con) in enumerate(cons[leq_locations]) + V_U[num_vars+i] = dual.(con) + end - return X, V_L, X_L, V_U, X_U, ineq_locations, has_up, vcat(has_low, collect(num_vars+1:num_vars+num_ineq)) + return X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, vcat(has_up, collect(num_vars+num_geq+1:num_vars+num_geq+num_leq)), vcat(has_low, collect(num_vars+1:num_vars+num_geq)) end """ @@ -203,7 +227,7 @@ Build the M (KKT Jacobian w.r.t. solution) and N (KKT Jacobian w.r.t. parameters """ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::AbstractVector, _V_L::AbstractVector, _X_L::AbstractVector, _V_U::AbstractVector, _X_U::AbstractVector, ineq_locations::Vector{Z}, + _X::AbstractVector, _V_L::AbstractVector, _X_L::AbstractVector, _V_U::AbstractVector, _X_U::AbstractVector, leq_locations::Vector{Z}, geq_locations::Vector{Z}, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} ) where {Z<:Integer} @assert all(x -> is_parameter(x), params) "All parameters must be parameters" @@ -247,9 +271,12 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe # Jacobian of the constraints wrt the primal variables A = spzeros(num_cons, num_vars + num_ineq) A[:, 1:num_vars] = jacobian[:, 1:num_vars] - for (i,j) in enumerate(ineq_locations) + for (i,j) in enumerate(geq_locations) A[j, num_vars+i] = -1 end + for (i,j) in enumerate(leq_locations) + A[j, num_vars+i] = 1 + end # Partial second derivative of the lagrangian wrt primal solution and parameters ∇ₓₚL = spzeros(num_vars + num_ineq, num_parms) ∇ₓₚL[1:num_vars, :] = hessian[1:num_vars, num_vars+1:end] @@ -296,11 +323,11 @@ Compute the derivatives of the solution w.r.t. the parameters without accounting """ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, - _X::AbstractVector, _V_L::AbstractVector, _X_L::AbstractVector, _V_U::AbstractVector, _X_U::AbstractVector, ineq_locations::Vector{Z}, + _X::AbstractVector, _V_L::AbstractVector, _X_L::AbstractVector, _V_U::AbstractVector, _X_U::AbstractVector, leq_locations::Vector{Z}, geq_locations::Vector{Z}, ineq_locations::Vector{Z}, has_up::Vector{Z}, has_low::Vector{Z} ) where {Z<:Integer} num_bounds = length(has_up) + length(has_low) - M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, ineq_locations, has_up, has_low) + M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) # Sesitivity of the solution (primal-dual_constraints-dual_bounds) w.r.t. the parameters K = lu(M) # Factorization @@ -308,7 +335,7 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # ∂s = - (K \ N) # Sensitivity ldiv!(∂s, K, N) ∂s = - ∂s - ∂s[end-num_bounds+1:end,:] = ∂s[end-num_bounds+1:end,:] .* -1.0 # Correcting the sign of the bounds duals for the standard form + # ∂s[end-num_bounds+1:end,:] = ∂s[end-num_bounds+1:end,:] .* -1.0 # Correcting the sign of the bounds duals for the standard form return ∂s, K, N end @@ -334,7 +361,7 @@ function fix_and_relax(E, K, N, r1, Δp, num_bounds) aux = - (rs + E * ∆ν¯)[:,:] ∆s = zeros(size(K, 1), size(aux, 2)) ldiv!(∆s, K, aux) - ∆s[end-num_bounds+1:end] = ∆s[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form + # ∆s[end-num_bounds+1:end] = ∆s[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form return ∆s end @@ -361,20 +388,24 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t r1 = [] for (j, i) in enumerate(has_low) if sp[i] < X_L[i] - tol + println("Violation LB Var: ", i, " ", sp[i], " ", X_L[i]) push!(_E, i) push!(r1, X[i] - X_L[i]) end if sp[num_w+num_cons+j] < -tol + println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) push!(_E, num_w+num_cons+j) push!(r1, V_L[i]) end end for (j, i) in enumerate(has_up) if sp[i] > X_U[i] + tol + println("Violation UB Var: ", i, " ", sp[i], " ", X_U[i]) push!(_E, i) push!(r1, X_U[i] - X[i]) end - if sp[num_w+num_cons+num_low+j] < -tol + if sp[num_w+num_cons+num_low+j] > tol + println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) push!(_E, num_w+num_cons+num_low+j) push!(r1, V_U[i]) end @@ -390,25 +421,26 @@ end function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, - Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-8 + Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 ) where {T<:Real} num_cons = length(cons) # Solution and bounds - X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) + X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) # Compute derivatives # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] - ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) + ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) Δs = ∂s * Δp Λ = dual.(cons) sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + num_bounds = length(has_up) + length(has_low) if !isempty(r1) @warn "Relaxation needed" - num_bounds = length(has_up) + length(has_low) Δs = fix_and_relax(E, K, N, r1, Δp, num_bounds) sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end + Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form return Δs, sp end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index faed4743..06c5a9ad 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -129,8 +129,8 @@ function test_compute_derivatives() @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) # Compute derivatives without accounting for active set changes evaluator, rows = create_evaluator(model; x=[primal_vars; params]) - X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) - ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, ineq_locations, has_up, has_low) + X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) + ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) # Check linear approx s_pb Δp = pb - pa s_pb_approx_violated = s_pa + ∂s * Δp @@ -249,6 +249,28 @@ function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]) return model, [x; y], [con0; con1; con2; con3], p end +function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:2] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x[i=1:2]) + @variable(model, z) # >= 2.0) + @variable(model, w) # <= 3.0) + # @variable(model, f[1:2]) + + # Constraints + # @constraint(model, con1, x[2] - 0.0001 * x[1]^2 - 0.2 * z^2 - 0.3 * w^2 >= p[1] + 1) + @constraint(model, con2, x[1] + 0.001 * x[2]^2 + 0.5 * w^2 + 0.4 * z^2 <= 10 * p[1] + 2) + @constraint(model, con3, z^2 + w^2 == 13) + @objective(model, Min, x[2] - x[1] + z - w) + + return model, [x; z; w], [con2; con3], p +end + function eval_model_jump(model, primal_vars, cons, params, p_val) set_parameter_value.(params, p_val) optimize!(model) @@ -256,11 +278,13 @@ function eval_model_jump(model, primal_vars, cons, params, p_val) return value.(primal_vars), dual.(cons), [dual.(LowerBoundRef(v)) for v in primal_vars if has_lower_bound(v)], [dual.(UpperBoundRef(v)) for v in primal_vars if has_upper_bound(v)] end -function stack_solution(cons, ineq_locations, x, _λ, ν_L, ν_U) - return Float64[x; value.(get_slack_inequality.(cons[ineq_locations])); _λ; ν_L; _λ[ineq_locations]; ν_U] +function stack_solution(cons, leq_locations, geq_locations, x, _λ, ν_L, ν_U) + ineq_locations = vcat(geq_locations, leq_locations) + return Float64[x; value.(get_slack_inequality.(cons[ineq_locations])); _λ; ν_L; _λ[geq_locations]; ν_U; _λ[leq_locations]] end -function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) +function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, leq_locations, geq_locations) + ineq_locations = vcat(geq_locations, leq_locations) println("Some sensitivities are not correct: \n") # primal vars num_primal_vars = length(primal_vars) @@ -294,11 +318,16 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) end end # dual lower bound slack vars - for (i, c) in enumerate(cons[ineq_locations]) + for (i, c) in enumerate(cons[geq_locations]) if !isapprox(Δs[i + num_w + num_cons + num_lower_bounds], Δs_fd[i + num_w + num_cons + num_lower_bounds] ; atol = 1e-6) println("lower bound slack dual: ", c, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds]) end end + for (i, c) in enumerate(cons[leq_locations]) + if !isapprox(Δs[i + num_w + num_cons + num_lower_bounds + length(geq_locations)], Δs_fd[i + num_w + num_cons + num_lower_bounds + length(geq_locations)] ; atol = 1e-6) + println("upper bound slack dual: ", c, " | Δs: ", Δs[i + num_w + num_cons + num_lower_bounds + length(geq_locations)], " | Δs_fd: ", Δs_fd[i + num_w + num_cons + num_lower_bounds + length(geq_locations)]) + end + end # dual upper bound primal vars var_upper = [v for v in primal_vars if has_upper_bound(v)] for (i, v) in enumerate(var_upper) @@ -312,21 +341,23 @@ end DICT_PROBLEMS = Dict( "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[-0.5; 0.5; 0.1], model_generator=create_nonlinear_jump_model), "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), + "QP_sIpopt" => (p_a=[5.0; 1.0], Δp=[-0.5; 0.0], model_generator=create_nonlinear_jump_model_sipopt), "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), - "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_1), - "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.001; -0.01; 0.001], model_generator=create_nonlinear_jump_model_1), - "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), - "NLP_2_2" => (p_a=[3.0; 2.0; 10], Δp=[-0.01; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), + "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.1; 0.5; 0.5], model_generator=create_nonlinear_jump_model_1), + "NLP_1_4" => (p_a=[3.0; 2.0; 200], Δp=[0.5; -0.5; 0.1], model_generator=create_nonlinear_jump_model_1), + "NLP_2" => (p_a=[3.0; 2.0; 10], Δp=[0.01; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), + "NLP_2_2" => (p_a=[3.0; 2.0; 10], Δp=[-0.1; 0.0; 0.0], model_generator=create_nonlinear_jump_model_2), "NLP_3" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_3), "NLP_3_2" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_3), "NLP_3_3" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_3), - "NLP_3_4" => (p_a=[3.0; 2.0; 10], Δp=[0.001; 0.001; 0.001], model_generator=create_nonlinear_jump_model_3), - "NLP_3_5" => (p_a=[3.0; 2.0; 10], Δp=[0.01; 0.03; 0.1], model_generator=create_nonlinear_jump_model_3), - "NLP_3_6" => (p_a=[3.0; 2.0; 10], Δp=[0.0; 0.000; -0.01], model_generator=create_nonlinear_jump_model_3), + "NLP_3_4" => (p_a=[3.0; 2.0; 10], Δp=[0.5; 0.001; 0.5], model_generator=create_nonlinear_jump_model_3), + "NLP_3_5" => (p_a=[3.0; 2.0; 10], Δp=[0.1; 0.3; 0.1], model_generator=create_nonlinear_jump_model_3), + "NLP_3_6" => (p_a=[3.0; 2.0; 10], Δp=[0.1; 0.2; -0.5], model_generator=create_nonlinear_jump_model_3), "NLP_4" => (p_a=[1.0; 2.0; 100], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_4), "NLP_5" => (p_a=[1.0; 2.0; 100], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_5), + "NLP_6" => (p_a=[100.0; 200.0], Δp=[0.2; 0.5], model_generator=create_nonlinear_jump_model_6), ) function test_compute_derivatives_Finite_Diff() @@ -335,25 +366,27 @@ function test_compute_derivatives_Finite_Diff() # OPT Problem model, primal_vars, cons, params = model_generator() eval_model_jump(model, primal_vars, cons, params, p_a) + + println("$problem_name: ", model) # Compute derivatives # Δp = [0.001; 0.0; 0.0] p_b = p_a + Δp (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) - ineq_locations = find_inequealities(cons) - # Check solution - # s_a = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) - # sp = stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) - # @test all(isapprox.(sp, sp_approx; atol = 1e-6)) + leq_locations, geq_locations = find_inequealities(cons) # Check derivatives using finite differences - ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(cons, ineq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) + ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) Δs_fd = ∂s_fd * Δp - - println("$problem_name: ", model) - if all(isapprox.(Δs, Δs_fd; atol = 1e-6)) + # actual solution + sp = stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) + num_important = length(primal_vars) + length(cons) + length(cons) + # Check sensitivities + if all(isapprox.(Δs, Δs_fd; atol = 1e-6)) || all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; atol = 1e-6)) println("All sensitivities are correct") else @show Δp - print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, ineq_locations) + print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, leq_locations, geq_locations) + # Check solution + println(all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; atol = 1e-6))) end println("--------------------") end From bb753ff8961e5756c80f183fdf51dc9b14d68119 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Thu, 25 Jul 2024 10:57:57 -0600 Subject: [PATCH 047/108] increase tests --- nlp_utilities_test.jl | 83 +++++++++++++++++++++++++++++++++++++++++++ testing_barrier.jl | 2 ++ 2 files changed, 85 insertions(+) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 06c5a9ad..e2e64fd6 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -142,6 +142,89 @@ function test_compute_derivatives() end end +################################################ +#= +# Test Sensitivity through analytical +=# +################################################ + +function create_jump_model_1(p_val = 1.5) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val)) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x >= p) + @constraint(model, con2, x >= 2) + @objective(model, Min, x^2) + + return model, [x], [con1; con2], [p] +end + +function create_jump_model_2(p_val = 1.5) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val)) + + # Variables + @variable(model, x >= 2.0) + + # Constraints + @constraint(model, con1, x >= p) + @objective(model, Min, x^2) + + return model, [x], [con1], [p] +end + +function create_jump_model_3(p_val = 1.5) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val)) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x <= p) + @constraint(model, con2, x <= 2) + @objective(model, Min, -x^2) + + return model, [x], [con1; con2], [p] +end + + +DICT_PROBLEMS_Analytical = Dict( + "geq no impact" => (p_a=1.5, Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), + "geq active constraint change" => (p_a=1.9, Δp=[0.2], Δs_a=[0.1; -0.1; 0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), + "geq impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), + "geq active bound change" => (p_a=2.1, Δp=[-0.2], Δs_a=[-0.1; 0.1; 0.0; 0.0; 0.0], model_generator=create_jump_model_2), + "get bound impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_1), + "leq no impact" => (p_a=1.5, Δp=[0.2], Δs_a=[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), +) + +function test_compute_derivatives_Analytical() + @testset "Compute Derivatives Analytical" begin + for (problem_name, (p_a, Δp, Δs_a, model_generator)) in DICT_PROBLEMS_Analytical + # OPT Problem + model, primal_vars, cons, params = model_generator() + eval_model_jump(model, primal_vars, cons, params, p_a) + # Compute derivatives + (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) + # Check sensitivities + @test all(isapprox.(Δs, Δs_a; atol = 1e-6)) + end + end +end + ################################################ #= # Test Sensitivity through finite differences diff --git a/testing_barrier.jl b/testing_barrier.jl index 25b2ff1f..acbb0b50 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -19,6 +19,8 @@ test_compute_derivatives() test_compute_derivatives_Finite_Diff() +test_compute_derivatives_Analytical() + ####################### # Test Bilevel (WIP) From 93d3981683605bf0c0775323a0fdef11fa52c082 Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Thu, 25 Jul 2024 13:26:54 -0600 Subject: [PATCH 048/108] more unit-tests --- nlp_utilities_test.jl | 29 ++++++++++++++--------------- testing_barrier.jl | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index e2e64fd6..7081235d 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -183,7 +183,7 @@ function create_jump_model_2(p_val = 1.5) return model, [x], [con1], [p] end -function create_jump_model_3(p_val = 1.5) +function create_jump_model_3(p_val = -1.5) model = Model(Ipopt.Optimizer) set_silent(model) @@ -195,8 +195,8 @@ function create_jump_model_3(p_val = 1.5) # Constraints @constraint(model, con1, x <= p) - @constraint(model, con2, x <= 2) - @objective(model, Min, -x^2) + @constraint(model, con2, x <= -2) + @objective(model, Min, -x) return model, [x], [con1; con2], [p] end @@ -207,21 +207,20 @@ DICT_PROBLEMS_Analytical = Dict( "geq active constraint change" => (p_a=1.9, Δp=[0.2], Δs_a=[0.1; -0.1; 0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), "geq impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), "geq active bound change" => (p_a=2.1, Δp=[-0.2], Δs_a=[-0.1; 0.1; 0.0; 0.0; 0.0], model_generator=create_jump_model_2), - "get bound impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_1), - "leq no impact" => (p_a=1.5, Δp=[0.2], Δs_a=[0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), + "geq bound impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), + "leq no impact" => (p_a=-1.5, Δp=[-0.2], Δs_a=[0.0; -2.0; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), + "leq active constraint change" => (p_a=-1.9, Δp=[-0.2], Δs_a=[-0.1; 2.0; -0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), ) function test_compute_derivatives_Analytical() - @testset "Compute Derivatives Analytical" begin - for (problem_name, (p_a, Δp, Δs_a, model_generator)) in DICT_PROBLEMS_Analytical - # OPT Problem - model, primal_vars, cons, params = model_generator() - eval_model_jump(model, primal_vars, cons, params, p_a) - # Compute derivatives - (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) - # Check sensitivities - @test all(isapprox.(Δs, Δs_a; atol = 1e-6)) - end + @testset "Compute Derivatives Analytical: $problem_name" for (problem_name, (p_a, Δp, Δs_a, model_generator)) in DICT_PROBLEMS_Analytical + # OPT Problem + model, primal_vars, cons, params = model_generator() + eval_model_jump(model, primal_vars, cons, params, p_a) + # Compute derivatives + (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) + # Check sensitivities + @test all(isapprox.(Δs, Δs_a; atol = 1e-6)) end end diff --git a/testing_barrier.jl b/testing_barrier.jl index acbb0b50..9b063b22 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -23,7 +23,7 @@ test_compute_derivatives_Analytical() ####################### -# Test Bilevel (WIP) +# Test Bilevel (LP) ####################### using BilevelJuMP, Ipopt From e20f4a809bfdd581d6dddf14c471ac9c13036cda Mon Sep 17 00:00:00 2001 From: andrewrosemberg Date: Thu, 25 Jul 2024 16:30:30 -0600 Subject: [PATCH 049/108] update slack approximation --- nlp_utilities.jl | 7 +++++++ nlp_utilities_test.jl | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 0ff37ecc..5a1cf7f9 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -424,6 +424,7 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 ) where {T<:Real} num_cons = length(cons) + num_var = length(primal_vars) # Solution and bounds X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) # Compute derivatives @@ -431,6 +432,9 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) Δs = ∂s * Δp Λ = dual.(cons) + for i in leq_locations # slack of leq constraints + Δs[num_var+i] = - Δs[num_var+i] + end sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) @@ -441,6 +445,9 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form + for i in leq_locations # slack of leq constraints + Δs[num_var+i] = - Δs[num_var+i] + end return Δs, sp end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 7081235d..0d26361d 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -148,12 +148,12 @@ end =# ################################################ -function create_jump_model_1(p_val = 1.5) +function create_jump_model_1(p_val = [1.5]) model = Model(Ipopt.Optimizer) set_silent(model) # Parameters - @variable(model, p ∈ MOI.Parameter(p_val)) + @variable(model, p ∈ MOI.Parameter(p_val[1])) # Variables @variable(model, x) @@ -166,12 +166,12 @@ function create_jump_model_1(p_val = 1.5) return model, [x], [con1; con2], [p] end -function create_jump_model_2(p_val = 1.5) +function create_jump_model_2(p_val = [1.5]) model = Model(Ipopt.Optimizer) set_silent(model) # Parameters - @variable(model, p ∈ MOI.Parameter(p_val)) + @variable(model, p ∈ MOI.Parameter(p_val[1])) # Variables @variable(model, x >= 2.0) @@ -183,12 +183,12 @@ function create_jump_model_2(p_val = 1.5) return model, [x], [con1], [p] end -function create_jump_model_3(p_val = -1.5) +function create_jump_model_3(p_val = [-1.5]) model = Model(Ipopt.Optimizer) set_silent(model) # Parameters - @variable(model, p ∈ MOI.Parameter(p_val)) + @variable(model, p ∈ MOI.Parameter(p_val[1])) # Variables @variable(model, x) @@ -203,13 +203,13 @@ end DICT_PROBLEMS_Analytical = Dict( - "geq no impact" => (p_a=1.5, Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), - "geq active constraint change" => (p_a=1.9, Δp=[0.2], Δs_a=[0.1; -0.1; 0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), - "geq impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), - "geq active bound change" => (p_a=2.1, Δp=[-0.2], Δs_a=[-0.1; 0.1; 0.0; 0.0; 0.0], model_generator=create_jump_model_2), - "geq bound impact" => (p_a=2.1, Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), - "leq no impact" => (p_a=-1.5, Δp=[-0.2], Δs_a=[0.0; -2.0; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), - "leq active constraint change" => (p_a=-1.9, Δp=[-0.2], Δs_a=[-0.1; 2.0; -0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), + "geq no impact" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), + "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), + "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), + "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1; 0.0; 0.0; 0.0], model_generator=create_jump_model_2), + "geq bound impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), + "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; -2.0; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), + "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; -0.1; -0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), ) function test_compute_derivatives_Analytical() @@ -452,7 +452,7 @@ function test_compute_derivatives_Finite_Diff() println("$problem_name: ", model) # Compute derivatives # Δp = [0.001; 0.0; 0.0] - p_b = p_a + Δp + p_b = p_a .+ Δp (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) leq_locations, geq_locations = find_inequealities(cons) # Check derivatives using finite differences From 7ddb3d22f72220785a63c6bb3c4228cfd831e0b6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 29 Jul 2024 11:40:49 -0600 Subject: [PATCH 050/108] fix order sign change --- nlp_utilities.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 5a1cf7f9..e56793e2 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -431,23 +431,24 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) Δs = ∂s * Δp + num_bounds = length(has_up) + length(has_low) Λ = dual.(cons) for i in leq_locations # slack of leq constraints Δs[num_var+i] = - Δs[num_var+i] end + Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) - num_bounds = length(has_up) + length(has_low) if !isempty(r1) @warn "Relaxation needed" Δs = fix_and_relax(E, K, N, r1, Δp, num_bounds) + for i in leq_locations # slack of leq constraints + Δs[num_var+i] = - Δs[num_var+i] + end + Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end - Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form - for i in leq_locations # slack of leq constraints - Δs[num_var+i] = - Δs[num_var+i] - end return Δs, sp end From 7b3e59155b269cb92e74cb0845abcb00f8bca832 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 29 Jul 2024 11:52:39 -0600 Subject: [PATCH 051/108] update tests --- nlp_utilities_test.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 0d26361d..ceb20034 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -204,12 +204,12 @@ end DICT_PROBLEMS_Analytical = Dict( "geq no impact" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), - "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), + "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_1), "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), - "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1; 0.0; 0.0; 0.0], model_generator=create_jump_model_2), + "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1], model_generator=create_jump_model_2), "geq bound impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), - "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; -2.0; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), - "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; -0.1; -0.1; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), + "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; 0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), + "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; 0.1; -0.1], model_generator=create_jump_model_3), ) function test_compute_derivatives_Analytical() @@ -220,7 +220,7 @@ function test_compute_derivatives_Analytical() # Compute derivatives (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) # Check sensitivities - @test all(isapprox.(Δs, Δs_a; atol = 1e-6)) + @test all(isapprox.(Δs[1:length(Δs_a)], Δs_a; atol = 1e-6)) end end @@ -460,7 +460,7 @@ function test_compute_derivatives_Finite_Diff() Δs_fd = ∂s_fd * Δp # actual solution sp = stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) - num_important = length(primal_vars) + length(cons) + length(cons) + num_important = length(primal_vars) # Check sensitivities if all(isapprox.(Δs, Δs_fd; atol = 1e-6)) || all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; atol = 1e-6)) println("All sensitivities are correct") From 839158dbc414f610c192ff60ca497282f51e2de2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 29 Jul 2024 17:56:08 -0600 Subject: [PATCH 052/108] add singular example and create bilevel file --- bilevel_diffopt.jl | 135 ++++++++++++++++++++++++++++++++++++++++++ nlp_utilities.jl | 4 +- nlp_utilities_test.jl | 2 +- 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 bilevel_diffopt.jl diff --git a/bilevel_diffopt.jl b/bilevel_diffopt.jl new file mode 100644 index 00000000..6ccd5d55 --- /dev/null +++ b/bilevel_diffopt.jl @@ -0,0 +1,135 @@ +using JuMP +using SparseArrays +using LinearAlgebra +using Ipopt + +################################################ +#= +# Test Linear Bilevel +=# +################################################ + +function constraint_upper!(model, x, y) + @constraints(model, begin + x <= 5 + y <= 8 + y >= 0 + end) +end + +function obj_upper!(model, x, y) + @objective(model, Min, 3x + y) +end + +function build_upper!(model, x, y) + obj_upper!(model, x, y) + constraint_upper!(model, x, y) +end + +function constraint_lower!(model, x, y; slack = zeros(4)) + @constraints(model, begin + x + y + slack[1] <= 8 + 4x + y + slack[2] >= 8 + 2x + y + slack[3] <= 13 + 2x - 7y + slack[4] <= 0 + end) +end + +function obj_lower!(model, x, y, slack) + @objective(model, Min, -x + 10 * sum(s^2 for s in slack)) +end + +function build_lower!(model, x, y, slack) + obj_lower!(model, x, y, slack) + constraint_lower!(model, x, y, slack = slack) +end + +function memoize(foo::Function) + last_x, last_f = nothing, nothing + last_dx, last_dfdx = nothing, nothing + function foo_i(x::T...) where {T<:Real} + if T == Float64 + if x !== last_x + last_x, last_f = x, foo(x...) + end + return last_f::T + else + if x !== last_dx + last_dx, last_dfdx = x, foo(x...) + end + return last_dfdx::T + end + end + return (x...) -> foo_i(x...) +end + +# using BilevelJuMP + +# model = BilevelModel(Ipopt.Optimizer, mode = BilevelJuMP.ProductMode(1e-5)) + +# @variable(Lower(model), x_b) +# @variable(Upper(model), y_b) + +# build_upper!(Upper(model), x_b, y_b) +# build_lower!(Lower(model), x_b, y_b, zeros(4)) +# optimize!(model) + +# objective_value(model) # = 3 * (3.5 * 8/15) + 8/15 # = 6.13... +# value(x_b) # = 3.5 * 8/15 # = 1.86... +# value(y_b) # = 8/15 # = 0.53... + +function test_bilevel_linear() + model_upper = Model(Ipopt.Optimizer) + model_lower = Model(Ipopt.Optimizer) + + @variable(model_upper, y) + @variable(model_upper, x_star) + @variable(model_upper, x_aux) + @variable(model_lower, x) + @variable(model_lower, y_p ∈ MOI.Parameter(1.0)) + @variable(model_lower, slack[1:4]) + + build_upper!(model_upper, x_star, y) + constraint_lower!(model_upper, x_aux, y) + build_lower!(model_lower, x, y_p, slack) + primal_vars = [x; slack] + params = [y_p] + evaluator, cons = create_evaluator(model_lower; x=[primal_vars; params]) + + # Define `f(y) = x_star` & `f'(y)` + function f(y_val) + set_parameter_value(y_p, y_val) + optimize!(model_lower) + @assert is_solved_and_feasible(model_lower) + return value(x) + end + + function ∇f0(y_val) + @assert value(y_p) == y_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.00]; primal_vars=primal_vars, params=params) + return Δs[1] + end + + function ∇f(y_val) + @assert value(y_p) == y_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) + return Δs[1] + end + + memoized_f = memoize(f) + + @operator(model_upper, op_f, 1, memoized_f, ∇f) + @constraint(model_upper, x_star == op_f(y)) + + optimize!(model_upper) + + @test objective_value(model_upper) ≈ 6.13 atol=0.05 + @test value(x_star) ≈ 1.86 atol=0.05 + @test value(y) ≈ 0.53 atol=0.05 +end + +################################################ +#= +# Test Non-Linear Bilevel +=# +################################################ \ No newline at end of file diff --git a/nlp_utilities.jl b/nlp_utilities.jl index e56793e2..0644c4bf 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -335,7 +335,7 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # ∂s = - (K \ N) # Sensitivity ldiv!(∂s, K, N) ∂s = - ∂s - # ∂s[end-num_bounds+1:end,:] = ∂s[end-num_bounds+1:end,:] .* -1.0 # Correcting the sign of the bounds duals for the standard form + return ∂s, K, N end @@ -361,7 +361,7 @@ function fix_and_relax(E, K, N, r1, Δp, num_bounds) aux = - (rs + E * ∆ν¯)[:,:] ∆s = zeros(size(K, 1), size(aux, 2)) ldiv!(∆s, K, aux) - # ∆s[end-num_bounds+1:end] = ∆s[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form + return ∆s end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index ceb20034..94192c4a 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -345,7 +345,7 @@ function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]) # @variable(model, f[1:2]) # Constraints - # @constraint(model, con1, x[2] - 0.0001 * x[1]^2 - 0.2 * z^2 - 0.3 * w^2 >= p[1] + 1) + @constraint(model, con1, x[2] - 0.0001 * x[1]^2 - 0.2 * z^2 - 0.3 * w^2 >= p[1] + 1) @constraint(model, con2, x[1] + 0.001 * x[2]^2 + 0.5 * w^2 + 0.4 * z^2 <= 10 * p[1] + 2) @constraint(model, con3, z^2 + w^2 == 13) @objective(model, Min, x[2] - x[1] + z - w) From 3fdfc0ffa47821b99d70254255e5b683c8b96dc0 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 29 Jul 2024 18:21:20 -0600 Subject: [PATCH 053/108] add singular correction --- nlp_utilities.jl | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 0644c4bf..86ca5ccc 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -312,6 +312,20 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe return M, N end +function inertia_corrector_factorization(M; st=1e-6) + # Factorization + K = lu(M; check=false) + # Inertia correction + status = K.status + while status == 1 + println("Inertia correction") + M = M + st * I(size(M, 1)) + K = lu(M; check=false) + status = K.status + end + return K +end + """ compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, @@ -330,7 +344,7 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) # Sesitivity of the solution (primal-dual_constraints-dual_bounds) w.r.t. the parameters - K = lu(M) # Factorization + K = inertia_corrector_factorization(M) # Factorization ∂s = zeros(size(M, 1), size(N, 2)) # ∂s = - (K \ N) # Sensitivity ldiv!(∂s, K, N) From c474ddaed5258d0238b3ec711dd78359c33c149e Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 30 Jul 2024 09:20:19 -0600 Subject: [PATCH 054/108] add non-linear bilevel test --- bilevel_diffopt.jl | 54 +++++++++++++++++++++++++++++++++++++++++++++- nlp_utilities.jl | 15 +++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/bilevel_diffopt.jl b/bilevel_diffopt.jl index 6ccd5d55..bab42d2b 100644 --- a/bilevel_diffopt.jl +++ b/bilevel_diffopt.jl @@ -132,4 +132,56 @@ end #= # Test Non-Linear Bilevel =# -################################################ \ No newline at end of file +################################################ +function test_bilevel_nonlinear() + upper_model = Model(Ipopt.Optimizer) + lower_model = Model(Ipopt.Optimizer) + + @variable(upper_model, x >= 2) + @variable(lower_model, x_p ∈ MOI.Parameter(1.0)) + + @variable(lower_model, 3 <= y <= 5) + @variable(upper_model, y_star) + + @objective(upper_model, Min, x^4 - sin(y_star)) + + @constraint(upper_model, x^3 + y_star^3 <= 1000) + + @objective(lower_model, Min, y^2 + x_p) + + # Define `f(x_p) = y_star` & `f'(x_p)` + primal_vars = [y] + params = [x_p] + evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) + + function f(x_p_val) + set_parameter_value(x_p, x_p_val) + optimize!(lower_model) + @assert is_solved_and_feasible(lower_model) + return value(y) + end + + function ∇f(x_p_val) + @assert value(x_p) == x_p_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) + return Δs[1] + end + + function ∇f0(x_p_val) + @assert value(x_p) == x_p_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.00]; primal_vars=primal_vars, params=params) + return Δs[1] + end + + memoized_f = memoize(f) + + @operator(upper_model, op_f, 1, memoized_f, ∇f) + + @constraint(upper_model, y_star == op_f(x)) + + @time optimize!(upper_model) + + @test objective_value(upper_model) ≈ 15.85 atol=0.05 + @test value(x) ≈ 2.0 atol=0.05 + @test value(y) ≈ 3.0 atol=0.05 +end \ No newline at end of file diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 86ca5ccc..d907c789 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -384,8 +384,19 @@ end First order approximation the primal-dual solution. """ -function approximate_solution(X, Λ, V_L, V_U, Δs) - return [X; Λ; V_L; V_U] .+ Δs +function approximate_solution(X::Vector{T}, Λ, V_L, V_U, Δs) where T + num_var = length(X) + num_cons = length(Λ) + num_low = length(V_L) + num_up = length(V_U) + sp = Vector{T}(undef, num_var + num_cons + num_low + num_up) + sp[1:num_var] = X + sp[num_var+1:num_var+num_cons] = Λ + sp[num_var+num_cons+1:num_var+num_cons+num_low] = V_L + sp[num_var+num_cons+num_low+1:num_var+num_cons+num_low+num_up] = V_U + sp = sp + Δs + + return sp end """ From 276b6ef6764a536bc05f44eb36e001b1caef0846 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 30 Jul 2024 11:47:53 -0600 Subject: [PATCH 055/108] add strategic bidding --- bilevel_diffopt.jl | 112 ++++++++++++++++++++++++++++++++++++++++++++- nlp_utilities.jl | 47 ++++++++++++++++--- 2 files changed, 151 insertions(+), 8 deletions(-) diff --git a/bilevel_diffopt.jl b/bilevel_diffopt.jl index bab42d2b..73fc723f 100644 --- a/bilevel_diffopt.jl +++ b/bilevel_diffopt.jl @@ -184,4 +184,114 @@ function test_bilevel_nonlinear() @test objective_value(upper_model) ≈ 15.85 atol=0.05 @test value(x) ≈ 2.0 atol=0.05 @test value(y) ≈ 3.0 atol=0.05 -end \ No newline at end of file +end + +################################################ +#= +# Test Stratigic Bidding +=# +################################################ + +function build_lower() + lower_model = Model(Ipopt.Optimizer) + + @variable(lower_model, qS_p ∈ MOI.Parameter(1.0)) + @variable(lower_model, 0 <= gS <= 100) + @variable(lower_model, 0 <= gR1 <= 40) + @variable(lower_model, 0 <= gR2 <= 40) + @variable(lower_model, 0 <= gD <= 100) + @objective(lower_model, Min, 0.5gR1^2 + 1gR2^2 + 2gD^2) + @constraint(lower_model, bid, gS <= qS_p) + @constraint(lower_model, demand_equilibrium, gS + gR1 + gR2 + gD == 100) + primal_vars = [gS; gR1; gR2; gD] + params = [qS_p] + return lower_model, qS_p, demand_equilibrium, primal_vars, params +end + +function build_upper() + upper_model = Model(Ipopt.Optimizer) + + @variable(upper_model, 0 <= qS <= 100) + @variable(upper_model, lambda) + @objective(upper_model, Max, lambda*qS) + return upper_model, lambda, qS +end + +# test derivative of the dual of the demand equilibrium constraint + +lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() +upper_model, lambda, qS = build_upper() + +optimize!(lower_model) + +evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) +Δs, sp = compute_sensitivity(evaluator, cons, [0.5]; primal_vars=primal_vars, params=params) + +set_parameter_value(qS_p, 1.5) + +optimize!(lower_model) + +@test dual(demand_equilibrium) ≈ sp[6] +@test dual(bid) ≈ sp[7] + +# test bilevel strategic bidding + +lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() +upper_model, lambda, qS = build_upper() + +evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) + +function f(qS_val) + set_parameter_value(qS_p, qS_val) + optimize!(lower_model) + @assert is_solved_and_feasible(lower_model) + return dual(demand_equilibrium) +end + +function ∇f(qS_val) + @assert value(qS_p) == qS_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) + return Δs[6] +end + +memoized_f = memoize(f) + +@operator(upper_model, op_f, 1, memoized_f, ∇f) + +@constraint(upper_model, lambda == op_f(qS)) + +# 0.68s +@time optimize!(upper_model) + +@test objective_value(upper_model) ≈ 0.00508 + +###### no derivative + +lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() +upper_model, lambda, qS = build_upper() + +evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) + +function f(qS_val) + set_parameter_value(qS_p, qS_val) + optimize!(lower_model) + @assert is_solved_and_feasible(lower_model) + return dual(demand_equilibrium) +end + +function ∇f(qS_val) + @assert value(qS_p) == qS_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.00]; primal_vars=primal_vars, params=params) + return Δs[6] +end + +memoized_f = memoize(f) + +@operator(upper_model, op_f, 1, memoized_f, ∇f) + +@constraint(upper_model, lambda == op_f(qS)) + +# 1.41s +@time optimize!(upper_model) + +@test objective_value(upper_model) ≈ 0.00508 \ No newline at end of file diff --git a/nlp_utilities.jl b/nlp_utilities.jl index d907c789..207c91ef 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -312,20 +312,44 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe return M, N end -function inertia_corrector_factorization(M; st=1e-6) +function inertia_corrector_factorization(M::SparseMatrixCSC; st=1e-6, max_corrections=10) # Factorization K = lu(M; check=false) # Inertia correction status = K.status - while status == 1 + num_c = 0 + while status == 1 && num_c < max_corrections println("Inertia correction") M = M + st * I(size(M, 1)) K = lu(M; check=false) status = K.status + num_c += 1 + end + if status != 0 + @warn "Inertia correction failed" + return nothing end return K end +function inertia_corrector_factorization(M; st=1e-6, max_corrections=10) + num_c = 0 + if cond(M) > 1/st + @warn "Inertia correction" + M = M + st * I(size(M, 1)) + num_c += 1 + end + while cond(M) > 1/st && num_c < max_corrections + M = M + st * I(size(M, 1)) + num_c += 1 + end + if num_c == max_corrections + @warn "Inertia correction failed" + return nothing + end + return lu(M) +end + """ compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, primal_vars::Vector{VariableRef}, params::Vector{VariableRef}, @@ -345,6 +369,9 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # Sesitivity of the solution (primal-dual_constraints-dual_bounds) w.r.t. the parameters K = inertia_corrector_factorization(M) # Factorization + if isnothing(K) + return zeros(size(M, 1), size(N, 2)), K, N + end ∂s = zeros(size(M, 1), size(N, 2)) # ∂s = - (K \ N) # Sensitivity ldiv!(∂s, K, N) @@ -369,7 +396,11 @@ function fix_and_relax(E, K, N, r1, Δp, num_bounds) # ∆ν¯ = C \ (E' * (K \ rs) - r1) aux = zeros(size(K, 1), size(rs, 2)) ldiv!(aux, K, rs) - ∆ν¯ = C \ (E' * aux - r1) + C_fact = inertia_corrector_factorization(C) + if isnothing(C_fact) + return zeros(size(K, 1), size(aux, 2)) + end + ∆ν¯ = C_fact \ (E' * aux - r1) # K ∆s = − (rs + E∆ν¯) # ∆s = K \ (- (rs + E * ∆ν¯)) aux = - (rs + E * ∆ν¯)[:,:] @@ -458,8 +489,10 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co Δs = ∂s * Δp num_bounds = length(has_up) + length(has_low) Λ = dual.(cons) - for i in leq_locations # slack of leq constraints - Δs[num_var+i] = - Δs[num_var+i] + num_geq = length(geq_locations) + num_leq = length(leq_locations) + for i in 1:num_leq # slack of leq constraints + Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] end Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) @@ -468,8 +501,8 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co if !isempty(r1) @warn "Relaxation needed" Δs = fix_and_relax(E, K, N, r1, Δp, num_bounds) - for i in leq_locations # slack of leq constraints - Δs[num_var+i] = - Δs[num_var+i] + for i in 1:num_leq # slack of leq constraints + Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] end Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) From 873e080c4eea2dd528e6157a5d348c95e56fc4f4 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 6 Aug 2024 14:32:59 -0400 Subject: [PATCH 056/108] update test --- nlp_utilities_test.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 94192c4a..1acdd5cd 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -462,7 +462,7 @@ function test_compute_derivatives_Finite_Diff() sp = stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) num_important = length(primal_vars) # Check sensitivities - if all(isapprox.(Δs, Δs_fd; atol = 1e-6)) || all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; atol = 1e-6)) + if all(isapprox.(Δs, Δs_fd; rtol = 1e-3, atol=14-6)) || all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; rtol = 1e-3, atol=14-6)) println("All sensitivities are correct") else @show Δp From 5032c4598a099f8545fd71dbb0957271eab6a252 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 6 Aug 2024 16:17:54 -0400 Subject: [PATCH 057/108] add opf --- bilevel_diffopt.jl | 111 ++++++++++++++-------------- opf.jl | 180 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 55 deletions(-) create mode 100644 opf.jl diff --git a/bilevel_diffopt.jl b/bilevel_diffopt.jl index 73fc723f..41ed924d 100644 --- a/bilevel_diffopt.jl +++ b/bilevel_diffopt.jl @@ -205,7 +205,7 @@ function build_lower() @constraint(lower_model, demand_equilibrium, gS + gR1 + gR2 + gD == 100) primal_vars = [gS; gR1; gR2; gD] params = [qS_p] - return lower_model, qS_p, demand_equilibrium, primal_vars, params + return lower_model, qS_p, demand_equilibrium, primal_vars, params, bid end function build_upper() @@ -218,80 +218,81 @@ function build_upper() end # test derivative of the dual of the demand equilibrium constraint +function test_bilevel_strategic_bidding() + lower_model, qS_p, demand_equilibrium, primal_vars, params, bid = build_lower() + upper_model, lambda, qS = build_upper() -lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() -upper_model, lambda, qS = build_upper() - -optimize!(lower_model) - -evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) -Δs, sp = compute_sensitivity(evaluator, cons, [0.5]; primal_vars=primal_vars, params=params) + optimize!(lower_model) -set_parameter_value(qS_p, 1.5) + evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) + Δs, sp = compute_sensitivity(evaluator, cons, [0.5]; primal_vars=primal_vars, params=params) -optimize!(lower_model) + set_parameter_value(qS_p, 1.5) -@test dual(demand_equilibrium) ≈ sp[6] -@test dual(bid) ≈ sp[7] + optimize!(lower_model) -# test bilevel strategic bidding + @test dual(demand_equilibrium) ≈ sp[6] + @test dual(bid) ≈ sp[7] -lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() -upper_model, lambda, qS = build_upper() + # test bilevel strategic bidding -evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) + lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() + upper_model, lambda, qS = build_upper() -function f(qS_val) - set_parameter_value(qS_p, qS_val) - optimize!(lower_model) - @assert is_solved_and_feasible(lower_model) - return dual(demand_equilibrium) -end - -function ∇f(qS_val) - @assert value(qS_p) == qS_val - Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) - return Δs[6] -end + evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) -memoized_f = memoize(f) + function f(qS_val) + set_parameter_value(qS_p, qS_val) + optimize!(lower_model) + @assert is_solved_and_feasible(lower_model) + return dual(demand_equilibrium) + end -@operator(upper_model, op_f, 1, memoized_f, ∇f) + function ∇f(qS_val) + @assert value(qS_p) == qS_val + Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) + return Δs[6] + end -@constraint(upper_model, lambda == op_f(qS)) + memoized_f = memoize(f) -# 0.68s -@time optimize!(upper_model) + @operator(upper_model, op_f, 1, memoized_f, ∇f) -@test objective_value(upper_model) ≈ 0.00508 + @constraint(upper_model, lambda == op_f(qS)) -###### no derivative + # 0.68s + optimize!(upper_model) + @test solve_time(upper_model) < 1.0 + @test objective_value(upper_model) ≈ 0.00508 rtol=1e-2 -lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() -upper_model, lambda, qS = build_upper() + ###### no derivative -evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) + lower_model, qS_p, demand_equilibrium, primal_vars, params = build_lower() + upper_model, lambda, qS = build_upper() -function f(qS_val) - set_parameter_value(qS_p, qS_val) - optimize!(lower_model) - @assert is_solved_and_feasible(lower_model) - return dual(demand_equilibrium) -end + evaluator, cons = create_evaluator(lower_model; x=[primal_vars; params]) -function ∇f(qS_val) - @assert value(qS_p) == qS_val - Δs, sp = compute_sensitivity(evaluator, cons, [0.00]; primal_vars=primal_vars, params=params) - return Δs[6] -end + function f(qS_val) + set_parameter_value(qS_p, qS_val) + optimize!(lower_model) + @assert is_solved_and_feasible(lower_model) + return dual(demand_equilibrium) + end -memoized_f = memoize(f) + # function ∇f(qS_val) + # @assert value(qS_p) == qS_val + # Δs, sp = compute_sensitivity(evaluator, cons, [0.00]; primal_vars=primal_vars, params=params) + # return Δs[6] + # end -@operator(upper_model, op_f, 1, memoized_f, ∇f) + memoized_f = memoize(f) -@constraint(upper_model, lambda == op_f(qS)) + @operator(upper_model, op_f, 1, memoized_f, (x) -> 0.0) -# 1.41s -@time optimize!(upper_model) + @constraint(upper_model, lambda == op_f(qS)) -@test objective_value(upper_model) ≈ 0.00508 \ No newline at end of file + # 1.09s + optimize!(upper_model) + @test solve_time(upper_model) > 1.0 + @test objective_value(upper_model) ≈ 0.00508 rtol=1e-2 +end diff --git a/opf.jl b/opf.jl new file mode 100644 index 00000000..2e668ba5 --- /dev/null +++ b/opf.jl @@ -0,0 +1,180 @@ +#!/usr/bin/env julia +###### AC-OPF using JuMP ###### +# +# implementation reference: https://github.com/lanl-ansi/PowerModelsAnnex.jl/blob/master/src/model/ac-opf.jl +# only the built-in AD library is supported +# + +using PowerModels +using PGLib +import Ipopt +import JuMP +using LinearAlgebra + +function build_gen(gen_bus, id, pmax, qmax) + return Dict{String, Any}( + "pg" => 0.5, + "model" => 2, + "shutdown" => 0.0, + "startup" => 0.0, + "qg" => 0.0, + "gen_bus" => gen_bus, + "pmax" => pmax, + "vg" => 1.0, + "mbase" => 100.0, + "source_id" => Any["gen", id], + "index" => id, + "cost" => [0.0, 0.0, 0.0], + "qmax" => qmax, + "gen_status" => 1, + "qmin" => - qmax, + "pmin" => 0.0, + "ncost" => 3, + ) +end + +function build_opf(case_name; percen_bidding_nodes=0.1) + data = make_basic_network(pglib(case_name)) + data["basic_network"] = false + # add bidding generators + num_bidding_nodes = ceil(Int, length(data["bus"]) * percen_bidding_nodes) + bidding_nodes = parse.(Int, rand(keys(data["bus"]), num_bidding_nodes)) + existing_gens = maximum(parse.(Int, collect(keys(data["gen"])))) + pmax = maximum([data["gen"][g]["pmax"] for g in keys(data["gen"])]) + qmax = maximum([data["gen"][g]["qmax"] for g in keys(data["gen"])]) + bidding_gen_ids = existing_gens + 1:existing_gens + num_bidding_nodes + for (i, node) in enumerate(bidding_nodes) + data["gen"]["$(bidding_gen_ids[i])"] = build_gen(node, bidding_gen_ids[i], pmax, qmax) + end + data = make_basic_network(data) + # create ref + PowerModels.standardize_cost_terms!(data, order=2) + PowerModels.calc_thermal_limits!(data) + ref = PowerModels.build_ref(data)[:it][:pm][:nw][0] + + # Model + model = JuMP.Model(Ipopt.Optimizer) + #JuMP.set_optimizer_attribute(model, "print_level", 0) + + JuMP.@variable(model, va[i in keys(ref[:bus])]) + JuMP.@variable(model, ref[:bus][i]["vmin"] <= vm[i in keys(ref[:bus])] <= ref[:bus][i]["vmax"], start=1.0) + for i in keys(ref[:bus]) + JuMP.set_name(va[i], "va_$i") + JuMP.set_name(vm[i], "vm_$i") + end + JuMP.@variable(model, ref[:gen][i]["pmin"] <= pg[i in keys(ref[:gen])] <= ref[:gen][i]["pmax"]) + JuMP.@variable(model, ref[:gen][i]["qmin"] <= qg[i in keys(ref[:gen])] <= ref[:gen][i]["qmax"]) + + # add bids + JuMP.@variable(model, pg_bid[i in bidding_gen_ids] ∈ MOI.Parameter.(1.0)) + @constraint(model, bid[i in bidding_gen_ids], pg[i] <= pg_bid[i]) + + for i in keys(ref[:gen]) + JuMP.set_name(pg[i], "pg_$i") + JuMP.set_name(qg[i], "qg_$i") + end + JuMP.@variable(model, -ref[:branch][l]["rate_a"] <= p[(l,i,j) in ref[:arcs]] <= ref[:branch][l]["rate_a"]) + JuMP.@variable(model, -ref[:branch][l]["rate_a"] <= q[(l,i,j) in ref[:arcs]] <= ref[:branch][l]["rate_a"]) + for (l, i, j) in ref[:arcs] + JuMP.set_name(p[(l, i, j)], "p_$(l)_$(i)_$(j)") + JuMP.set_name(q[(l, i, j)], "q_$(l)_$(i)_$(j)") + end + JuMP.@objective(model, Min, sum(gen["cost"][1]*pg[i]^2 + gen["cost"][2]*pg[i] + gen["cost"][3] for (i,gen) in ref[:gen])) + + for (i,bus) in ref[:ref_buses] + JuMP.@constraint(model, va[i] == 0) + end + + demand_equilibrium = Dict{Int, JuMP.ConstraintRef}() + for (i,bus) in ref[:bus] + bus_loads = [ref[:load][l] for l in ref[:bus_loads][i]] + bus_shunts = [ref[:shunt][s] for s in ref[:bus_shunts][i]] + + active_balance = JuMP.@constraint(model, + sum(p[a] for a in ref[:bus_arcs][i]) == + sum(pg[g] for g in ref[:bus_gens][i]) - + sum(load["pd"] for load in bus_loads) - + sum(shunt["gs"] for shunt in bus_shunts)*vm[i]^2 + ) + + JuMP.@constraint(model, + sum(q[a] for a in ref[:bus_arcs][i]) == + sum(qg[g] for g in ref[:bus_gens][i]) - + sum(load["qd"] for load in bus_loads) + + sum(shunt["bs"] for shunt in bus_shunts)*vm[i]^2 + ) + + demand_equilibrium[bus] = active_balance + end + + # Branch power flow physics and limit constraints + for (i,branch) in ref[:branch] + f_idx = (i, branch["f_bus"], branch["t_bus"]) + t_idx = (i, branch["t_bus"], branch["f_bus"]) + + p_fr = p[f_idx] + q_fr = q[f_idx] + p_to = p[t_idx] + q_to = q[t_idx] + + vm_fr = vm[branch["f_bus"]] + vm_to = vm[branch["t_bus"]] + va_fr = va[branch["f_bus"]] + va_to = va[branch["t_bus"]] + + g, b = PowerModels.calc_branch_y(branch) + tr, ti = PowerModels.calc_branch_t(branch) + ttm = tr^2 + ti^2 + g_fr = branch["g_fr"] + b_fr = branch["b_fr"] + g_to = branch["g_to"] + b_to = branch["b_to"] + + # From side of the branch flow + JuMP.@constraint(model, p_fr == (g+g_fr)/ttm*vm_fr^2 + (-g*tr+b*ti)/ttm*(vm_fr*vm_to*cos(va_fr-va_to)) + (-b*tr-g*ti)/ttm*(vm_fr*vm_to*sin(va_fr-va_to)) ) + JuMP.@constraint(model, q_fr == -(b+b_fr)/ttm*vm_fr^2 - (-b*tr-g*ti)/ttm*(vm_fr*vm_to*cos(va_fr-va_to)) + (-g*tr+b*ti)/ttm*(vm_fr*vm_to*sin(va_fr-va_to)) ) + + # To side of the branch flow + JuMP.@constraint(model, p_to == (g+g_to)*vm_to^2 + (-g*tr-b*ti)/ttm*(vm_to*vm_fr*cos(va_to-va_fr)) + (-b*tr+g*ti)/ttm*(vm_to*vm_fr*sin(va_to-va_fr)) ) + JuMP.@constraint(model, q_to == -(b+b_to)*vm_to^2 - (-b*tr+g*ti)/ttm*(vm_to*vm_fr*cos(va_to-va_fr)) + (-g*tr-b*ti)/ttm*(vm_to*vm_fr*sin(va_to-va_fr)) ) + + # Voltage angle difference limit + JuMP.@constraint(model, branch["angmin"] <= va_fr - va_to <= branch["angmax"]) + + # Apparent power limit, from side and to side + JuMP.@constraint(model, p_fr^2 + q_fr^2 <= branch["rate_a"]^2) + JuMP.@constraint(model, p_to^2 + q_to^2 <= branch["rate_a"]^2) + end + + model_variables = JuMP.num_variables(model) + + # for consistency with other solvers, skip the variable bounds in the constraint count + model_constraints = JuMP.num_constraints(model; count_variable_in_set_constraints = false) + + println("") + println("\033[1mSummary\033[0m") + println(" case........: $(file_name)") + println(" variables...: $(model_variables)") + println(" constraints.: $(model_constraints)") + + println("") + + return Dict( + "case" => file_name, + "model_variables" => model_variables, + "bidding_generators_dispatch" => [pg[i] for i in bidding_gen_ids], + "bidding_lmps" => [demand_equilibrium[i] for i in bidding_nodes], + "model" => model, + "bid" => [pg_bid[i] for i in bidding_gen_ids], + "pmax" => pmax, + ) +end + +function build_bidding_upper(num_bidding_nodes, pmax) + upper_model = Model(Ipopt.Optimizer) + + @variable(upper_model, 0 <= pg_bid[i=1:num_bidding_nodes] <= pmax) + @variable(upper_model, lambda[i=1:num_bidding_nodes]) + @objective(upper_model, Max, dot(lambda, pg_bid)) + return upper_model, lambda, pg_bid +end From 66dfe60b452e8795450d9acad8d16a54025fcbbf Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 6 Aug 2024 17:50:10 -0400 Subject: [PATCH 058/108] add ac stregic bidding --- bilevel_diffopt.jl | 2 +- opf.jl | 98 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/bilevel_diffopt.jl b/bilevel_diffopt.jl index 41ed924d..2747e0fd 100644 --- a/bilevel_diffopt.jl +++ b/bilevel_diffopt.jl @@ -217,8 +217,8 @@ function build_upper() return upper_model, lambda, qS end -# test derivative of the dual of the demand equilibrium constraint function test_bilevel_strategic_bidding() + # test derivative of the dual of the demand equilibrium constraint lower_model, qS_p, demand_equilibrium, primal_vars, params, bid = build_lower() upper_model, lambda, qS = build_upper() diff --git a/opf.jl b/opf.jl index 2e668ba5..b1ee330f 100644 --- a/opf.jl +++ b/opf.jl @@ -33,7 +33,7 @@ function build_gen(gen_bus, id, pmax, qmax) ) end -function build_opf(case_name; percen_bidding_nodes=0.1) +function build_opf_model(case_name; percen_bidding_nodes=0.1) data = make_basic_network(pglib(case_name)) data["basic_network"] = false # add bidding generators @@ -66,7 +66,7 @@ function build_opf(case_name; percen_bidding_nodes=0.1) JuMP.@variable(model, ref[:gen][i]["qmin"] <= qg[i in keys(ref[:gen])] <= ref[:gen][i]["qmax"]) # add bids - JuMP.@variable(model, pg_bid[i in bidding_gen_ids] ∈ MOI.Parameter.(1.0)) + JuMP.@variable(model, pg_bid[i in bidding_gen_ids] ∈ MOI.Parameter.(pmax * 0.5)) @constraint(model, bid[i in bidding_gen_ids], pg[i] <= pg_bid[i]) for i in keys(ref[:gen]) @@ -104,7 +104,7 @@ function build_opf(case_name; percen_bidding_nodes=0.1) sum(shunt["bs"] for shunt in bus_shunts)*vm[i]^2 ) - demand_equilibrium[bus] = active_balance + demand_equilibrium[i] = active_balance end # Branch power flow physics and limit constraints @@ -153,19 +153,24 @@ function build_opf(case_name; percen_bidding_nodes=0.1) println("") println("\033[1mSummary\033[0m") - println(" case........: $(file_name)") + println(" case........: $(case_name)") println(" variables...: $(model_variables)") println(" constraints.: $(model_constraints)") println("") + bidding_generators_dispatch = [pg[i] for i in bidding_gen_ids] + _pg_bid = [pg_bid[i] for i in bidding_gen_ids] + all_primal_variables = all_variables(model) + remaining_vars = setdiff(all_primal_variables, bidding_generators_dispatch) + remaining_vars = setdiff(remaining_vars, _pg_bid) return Dict( - "case" => file_name, - "model_variables" => model_variables, - "bidding_generators_dispatch" => [pg[i] for i in bidding_gen_ids], + "case" => case_name, + "model_variables" => remaining_vars, + "bidding_generators_dispatch" => bidding_generators_dispatch, "bidding_lmps" => [demand_equilibrium[i] for i in bidding_nodes], "model" => model, - "bid" => [pg_bid[i] for i in bidding_gen_ids], + "bid" => _pg_bid, "pmax" => pmax, ) end @@ -174,7 +179,80 @@ function build_bidding_upper(num_bidding_nodes, pmax) upper_model = Model(Ipopt.Optimizer) @variable(upper_model, 0 <= pg_bid[i=1:num_bidding_nodes] <= pmax) + @variable(upper_model, pg[i=1:num_bidding_nodes]) @variable(upper_model, lambda[i=1:num_bidding_nodes]) - @objective(upper_model, Max, dot(lambda, pg_bid)) - return upper_model, lambda, pg_bid + @objective(upper_model, Max, dot(lambda, pg)) + return upper_model, lambda, pg_bid, pg end + +function memoize(foo::Function, n_outputs::Int) + last_x, last_f = nothing, nothing + last_dx, last_dfdx = nothing, nothing + function foo_i(i, x...) + if x !== last_x + last_x, last_f = x, foo(x...) + end + return last_f[i] + end + return [(x...) -> foo_i(i, x...) for i in 1:n_outputs] +end + +function test_bilevel_ac_strategic_bidding() + # test derivative of the dual of the demand equilibrium constraint + data = build_opf_model("case14") + pmax = data["pmax"] + primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] + num_bidding_nodes = length(data["bidding_generators_dispatch"]) + set_parameter_value.(data["bid"], 0.5) + optimize!(data["model"]) + evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) + leq_locations, geq_locations = find_inequealities(cons) + num_ineq = length(leq_locations) + length(geq_locations) + bidding_lmps_index = findall(x -> x in data["bidding_lmps"], cons) + Δp = rand(-pmax*0.1:0.001:pmax*0.1, num_bidding_nodes) + Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) + + set_parameter_value.(data["bid"], 0.5 .+ Δp) + optimize!(data["model"]) + + @test dual.(data["bidding_lmps"]) ≈ Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index] + + # test bilevel strategic bidding + upper_model, lambda, pg_bid, pg = build_bidding_upper(num_bidding_nodes, pmax) + evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) + + function f(pg_bid_val...) + set_parameter_value.(data["bid"], pg_bid_val) + optimize!(data["model"]) + @assert is_solved_and_feasible(data["model"]) + return [value.(data["bidding_generators_dispatch"]); -dual.(data["bidding_lmps"])] + end + + function ∇f(pg_bid_val...) + if value.(data["bid"]) == pg_bid_val + set_parameter_value.(data["bid"], pg_bid_val) + optimize!(data["model"]) + @assert is_solved_and_feasible(data["model"]) + end + Δs, sp = compute_sensitivity(evaluator, cons, fill(0.001, num_bidding_nodes); primal_vars=primal_vars, params=data["bid"]) + return [Δs[1:num_bidding_nodes]; -(Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index])] + end + + memoized_f = memoize(f, 2 * num_bidding_nodes) + memoized_∇f = memoize(∇f, 2 * num_bidding_nodes) + for i in 1:num_bidding_nodes + op_pg = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[i], memoized_∇f[i]; name = Symbol("op_pg_$i")) + @constraint(upper_model, pg[i] == op_pg(pg_bid...)) + end + + for i in 1:num_bidding_nodes + op_lambda = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[num_bidding_nodes + i], memoized_∇f[num_bidding_nodes + i]; name = Symbol("op_lambda_$i")) + @constraint(upper_model, lambda[i] == op_lambda(pg_bid...)) + end + + optimize!(upper_model) + + @test objective_value(upper_model) ≈ 0.0 rtol=1e-2 + + +end \ No newline at end of file From 3cdc51c905d8cce3816aa7ec64554150dd3e865d Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 6 Aug 2024 18:01:33 -0400 Subject: [PATCH 059/108] update test --- opf.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/opf.jl b/opf.jl index b1ee330f..a61a36b0 100644 --- a/opf.jl +++ b/opf.jl @@ -203,19 +203,19 @@ function test_bilevel_ac_strategic_bidding() pmax = data["pmax"] primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) - set_parameter_value.(data["bid"], 0.5) + set_parameter_value.(data["bid"], 0.01) optimize!(data["model"]) evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) leq_locations, geq_locations = find_inequealities(cons) num_ineq = length(leq_locations) + length(geq_locations) - bidding_lmps_index = findall(x -> x in data["bidding_lmps"], cons) - Δp = rand(-pmax*0.1:0.001:pmax*0.1, num_bidding_nodes) - Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) + bidding_lmps_index = [findall(x -> x == i, cons)[1] for i in data["bidding_lmps"]] + # Δp = rand(-pmax*0.1:0.001:pmax*0.1, num_bidding_nodes) + # Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) - set_parameter_value.(data["bid"], 0.5 .+ Δp) - optimize!(data["model"]) + # set_parameter_value.(data["bid"], 0.5 .+ Δp) + # optimize!(data["model"]) - @test dual.(data["bidding_lmps"]) ≈ Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index] + # @test dual.(data["bidding_lmps"]) ≈ Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index] # test bilevel strategic bidding upper_model, lambda, pg_bid, pg = build_bidding_upper(num_bidding_nodes, pmax) @@ -234,7 +234,7 @@ function test_bilevel_ac_strategic_bidding() optimize!(data["model"]) @assert is_solved_and_feasible(data["model"]) end - Δs, sp = compute_sensitivity(evaluator, cons, fill(0.001, num_bidding_nodes); primal_vars=primal_vars, params=data["bid"]) + Δs, sp = compute_sensitivity(evaluator, cons, fill(0.0001, num_bidding_nodes); primal_vars=primal_vars, params=data["bid"]) return [Δs[1:num_bidding_nodes]; -(Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index])] end @@ -252,7 +252,7 @@ function test_bilevel_ac_strategic_bidding() optimize!(upper_model) - @test objective_value(upper_model) ≈ 0.0 rtol=1e-2 + objective_value(upper_model) end \ No newline at end of file From a32fd70788fe6fe48497a6888e3609ecd7a2b716 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 7 Aug 2024 14:54:18 -0400 Subject: [PATCH 060/108] improve opf example --- opf.jl | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/opf.jl b/opf.jl index a61a36b0..0096d61d 100644 --- a/opf.jl +++ b/opf.jl @@ -10,6 +10,7 @@ using PGLib import Ipopt import JuMP using LinearAlgebra +using ForwardDiff function build_gen(gen_bus, id, pmax, qmax) return Dict{String, Any}( @@ -187,16 +188,48 @@ end function memoize(foo::Function, n_outputs::Int) last_x, last_f = nothing, nothing - last_dx, last_dfdx = nothing, nothing function foo_i(i, x...) if x !== last_x - last_x, last_f = x, foo(x...) + ret = foo(x...) + if !isnothing(ret) + last_x, last_f = x, ret + end end return last_f[i] end return [(x...) -> foo_i(i, x...) for i in 1:n_outputs] end +function memoize!(foo::Function, n_outputs::Int) + last_x, last_f = nothing, nothing + function foo_i(i, g, x...) + if x !== last_x + ret = foo(g, x...) + if !isnothing(ret) + last_x, last_f = x, ret + end + end + g .= last_f[i] + return last_f[i] + end + return [(g, x...) -> foo_i(i, g, x...) for i in 1:n_outputs] +end + +function fdiff_derivatives(f::Function) + function ∇f(g::AbstractVector{T}, x::Vararg{T,N}) where {T,N} + FiniteDiff.finite_difference_gradient!(g, y -> f(y...), collect(x)) + return + end + function ∇²f(H::AbstractMatrix{T}, x::Vararg{T,N}) where {T,N} + h = FiniteDiff.finite_difference_hessian!(y -> f(y...), collect(x)) + for i in 1:N, j in 1:i + H[i, j] = h[i, j] + end + return + end + return ∇f, ∇²f +end + function test_bilevel_ac_strategic_bidding() # test derivative of the dual of the demand equilibrium constraint data = build_opf_model("case14") @@ -208,6 +241,7 @@ function test_bilevel_ac_strategic_bidding() evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) leq_locations, geq_locations = find_inequealities(cons) num_ineq = length(leq_locations) + length(geq_locations) + num_primal = length(primal_vars) bidding_lmps_index = [findall(x -> x == i, cons)[1] for i in data["bidding_lmps"]] # Δp = rand(-pmax*0.1:0.001:pmax*0.1, num_bidding_nodes) # Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) @@ -215,7 +249,7 @@ function test_bilevel_ac_strategic_bidding() # set_parameter_value.(data["bid"], 0.5 .+ Δp) # optimize!(data["model"]) - # @test dual.(data["bidding_lmps"]) ≈ Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index] + # @test dual.(data["bidding_lmps"]) ≈ Δs[(num_primal + num_ineq) .+ bidding_lmps_index] # test bilevel strategic bidding upper_model, lambda, pg_bid, pg = build_bidding_upper(num_bidding_nodes, pmax) @@ -224,29 +258,39 @@ function test_bilevel_ac_strategic_bidding() function f(pg_bid_val...) set_parameter_value.(data["bid"], pg_bid_val) optimize!(data["model"]) - @assert is_solved_and_feasible(data["model"]) + if !is_solved_and_feasible(data["model"]) + return nothing + end return [value.(data["bidding_generators_dispatch"]); -dual.(data["bidding_lmps"])] end - function ∇f(pg_bid_val...) + function ∇f(g::AbstractVector{T}, pg_bid_val...) where {T} if value.(data["bid"]) == pg_bid_val set_parameter_value.(data["bid"], pg_bid_val) optimize!(data["model"]) @assert is_solved_and_feasible(data["model"]) end Δs, sp = compute_sensitivity(evaluator, cons, fill(0.0001, num_bidding_nodes); primal_vars=primal_vars, params=data["bid"]) - return [Δs[1:num_bidding_nodes]; -(Δs[(length(primal_vars) + num_ineq) .+ bidding_lmps_index])] + for i in 1:num_bidding_nodes + g[i] = Δs[i] + g[num_bidding_nodes + i] = -Δs[num_primal + num_ineq + i] + end + return g + # return [Δs[1:num_bidding_nodes]; -(Δs[(num_primal + num_ineq) .+ bidding_lmps_index])] end memoized_f = memoize(f, 2 * num_bidding_nodes) - memoized_∇f = memoize(∇f, 2 * num_bidding_nodes) + memoized_∇f = memoize!(∇f, 2 * num_bidding_nodes) + for i in 1:num_bidding_nodes op_pg = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[i], memoized_∇f[i]; name = Symbol("op_pg_$i")) + # op_pg = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[i], fdiff_derivatives(memoized_f[i])...; name = Symbol("op_pg_$i")) @constraint(upper_model, pg[i] == op_pg(pg_bid...)) end for i in 1:num_bidding_nodes op_lambda = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[num_bidding_nodes + i], memoized_∇f[num_bidding_nodes + i]; name = Symbol("op_lambda_$i")) + # op_lambda = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[num_bidding_nodes + i], fdiff_derivatives(memoized_f[num_bidding_nodes + i])...; name = Symbol("op_lambda_$i")) @constraint(upper_model, lambda[i] == op_lambda(pg_bid...)) end From e3d88ab729104c392795312cd24e2d2483773552 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 7 Aug 2024 15:18:53 -0400 Subject: [PATCH 061/108] update restoration --- nlp_utilities.jl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 207c91ef..3d8c8f67 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -312,15 +312,18 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe return M, N end -function inertia_corrector_factorization(M::SparseMatrixCSC; st=1e-6, max_corrections=10) +function inertia_corrector_factorization(M::SparseMatrixCSC, num_w, num_cons; st=1e-6, max_corrections=50) # Factorization K = lu(M; check=false) # Inertia correction status = K.status num_c = 0 + diag_mat = ones(size(M, 1)) + diag_mat[num_w+1:num_w+num_cons] .= -1 + diag_mat = sparse(diagm(diag_mat)) while status == 1 && num_c < max_corrections println("Inertia correction") - M = M + st * I(size(M, 1)) + M = M + st * diag_mat K = lu(M; check=false) status = K.status num_c += 1 @@ -332,7 +335,7 @@ function inertia_corrector_factorization(M::SparseMatrixCSC; st=1e-6, max_correc return K end -function inertia_corrector_factorization(M; st=1e-6, max_corrections=10) +function inertia_corrector_factorization(M; st=1e-6, max_corrections=50) num_c = 0 if cond(M) > 1/st @warn "Inertia correction" @@ -368,7 +371,7 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: M, N = build_M_N(evaluator, cons, primal_vars, params, _X, _V_L, _X_L, _V_U, _X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) # Sesitivity of the solution (primal-dual_constraints-dual_bounds) w.r.t. the parameters - K = inertia_corrector_factorization(M) # Factorization + K = inertia_corrector_factorization(M, length(primal_vars) + length(ineq_locations), length(cons)) # Factorization if isnothing(K) return zeros(size(M, 1), size(N, 2)), K, N end @@ -381,11 +384,11 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: end """ - fix_and_relax(E, K, N, r1, Δp, num_bounds) + fix_and_relax(E, K, N, r1, Δp, num_w, num_cons) Fix the violations and relax complementary slackness. """ -function fix_and_relax(E, K, N, r1, Δp, num_bounds) +function fix_and_relax(E, K, N, r1, Δp) rs = (N * Δp)[:,:] # C = −E' inv(K) E # C = - E' * (K \ E) @@ -500,7 +503,7 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) if !isempty(r1) @warn "Relaxation needed" - Δs = fix_and_relax(E, K, N, r1, Δp, num_bounds) + Δs = fix_and_relax(E, K, N, r1, Δp) for i in 1:num_leq # slack of leq constraints Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] end From 9b27e3b0737ba6d5cb75b843d22e1150f2efe56d Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 7 Aug 2024 15:32:11 -0400 Subject: [PATCH 062/108] update script example sb ac --- opf.jl | 12 +++++++----- testing_barrier.jl | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/opf.jl b/opf.jl index 0096d61d..1bca433c 100644 --- a/opf.jl +++ b/opf.jl @@ -230,9 +230,9 @@ function fdiff_derivatives(f::Function) return ∇f, ∇²f end -function test_bilevel_ac_strategic_bidding() +function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1) # test derivative of the dual of the demand equilibrium constraint - data = build_opf_model("case14") + data = build_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes) pmax = data["pmax"] primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) @@ -296,7 +296,9 @@ function test_bilevel_ac_strategic_bidding() optimize!(upper_model) - objective_value(upper_model) - - + println("Status: ", termination_status(upper_model)) + println("Objective: ", objective_value(upper_model)) + println("Duals: ", value.(lambda)) + println("Bids: ", value.(pg_bid)) + println("Dispatch: ", value.(pg)) end \ No newline at end of file diff --git a/testing_barrier.jl b/testing_barrier.jl index 9b063b22..842cff4b 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -11,6 +11,7 @@ using Ipopt include("nlp_utilities.jl") include("nlp_utilities_test.jl") +include("opf.jl") test_compute_optimal_hess_jacobian() @@ -21,6 +22,7 @@ test_compute_derivatives_Finite_Diff() test_compute_derivatives_Analytical() +test_bilevel_ac_strategic_bidding("pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.5) ####################### # Test Bilevel (LP) From 6346d33ee3fa9a374d93fd33f28608b31bebe468 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 8 Aug 2024 16:11:37 -0400 Subject: [PATCH 063/108] update code working --- nlp_utilities.jl | 12 +++++++++--- opf.jl | 12 +++++++++--- testing_barrier.jl | 3 ++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 3d8c8f67..f9ec42ef 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -480,8 +480,8 @@ end function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, - Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 -) where {T<:Real} + Δp; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 +) num_cons = length(cons) num_var = length(primal_vars) # Solution and bounds @@ -489,6 +489,12 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Compute derivatives # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) + if isnothing(Δp) || iszero(Δp) + Λ = dual.(cons) + Δs = ∂s * ones(size(∂s, 2)) + sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) + return Δs, sp + end Δs = ∂s * Δp num_bounds = length(has_up) + length(has_low) Λ = dual.(cons) @@ -518,7 +524,7 @@ end Compute the sensitivity of the solution given sensitivity of the parameters (Δp). """ -function compute_sensitivity(model::Model, Δp::Vector{T}; primal_vars=all_primal_vars(model), params=all_params(model)) where {T<:Real} +function compute_sensitivity(model::Model, Δp; primal_vars=all_primal_vars(model), params=all_params(model)) evaluator, cons = create_evaluator(model; x=[primal_vars; params]) return compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=params), evaluator, cons end diff --git a/opf.jl b/opf.jl index 1bca433c..1bf9af71 100644 --- a/opf.jl +++ b/opf.jl @@ -230,7 +230,7 @@ function fdiff_derivatives(f::Function) return ∇f, ∇²f end -function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1) +function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001) # test derivative of the dual of the demand equilibrium constraint data = build_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes) pmax = data["pmax"] @@ -243,6 +243,9 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe num_ineq = length(leq_locations) + length(geq_locations) num_primal = length(primal_vars) bidding_lmps_index = [findall(x -> x == i, cons)[1] for i in data["bidding_lmps"]] + if !isnothing(Δp) + Δp = fill(Δp, num_bidding_nodes) + end # Δp = rand(-pmax*0.1:0.001:pmax*0.1, num_bidding_nodes) # Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) @@ -270,10 +273,13 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe optimize!(data["model"]) @assert is_solved_and_feasible(data["model"]) end - Δs, sp = compute_sensitivity(evaluator, cons, fill(0.0001, num_bidding_nodes); primal_vars=primal_vars, params=data["bid"]) + + Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) for i in 1:num_bidding_nodes g[i] = Δs[i] - g[num_bidding_nodes + i] = -Δs[num_primal + num_ineq + i] + end + for (i, b_idx) in enumerate(bidding_lmps_index) + g[i] = -Δs[num_primal + num_ineq + b_idx] end return g # return [Δs[1:num_bidding_nodes]; -(Δs[(num_primal + num_ineq) .+ bidding_lmps_index])] diff --git a/testing_barrier.jl b/testing_barrier.jl index 842cff4b..8e06093f 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -22,7 +22,8 @@ test_compute_derivatives_Finite_Diff() test_compute_derivatives_Analytical() -test_bilevel_ac_strategic_bidding("pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.5) +# "pglib_opf_case5_pjm.m" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" +test_bilevel_ac_strategic_bidding("pglib_opf_case300_ieee"; percen_bidding_nodes=0.1, Δp=nothing) ####################### # Test Bilevel (LP) From 1920286a748750b9718f41677170d2bcfb18ebcb Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 8 Aug 2024 17:08:50 -0400 Subject: [PATCH 064/108] update example --- opf.jl | 2 +- testing_barrier.jl | 125 ++------------------------------------------- 2 files changed, 6 insertions(+), 121 deletions(-) diff --git a/opf.jl b/opf.jl index 1bf9af71..f941367b 100644 --- a/opf.jl +++ b/opf.jl @@ -286,7 +286,7 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe end memoized_f = memoize(f, 2 * num_bidding_nodes) - memoized_∇f = memoize!(∇f, 2 * num_bidding_nodes) + memoized_∇f = !isnothing(Δp) && iszero(Δp) ? [(args...) -> nothing for i in 1:2 * num_bidding_nodes] : memoize!(∇f, 2 * num_bidding_nodes) for i in 1:num_bidding_nodes op_pg = add_nonlinear_operator(upper_model, num_bidding_nodes, memoized_f[i], memoized_∇f[i]; name = Symbol("op_pg_$i")) diff --git a/testing_barrier.jl b/testing_barrier.jl index 8e06093f..d5ee3ca1 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -6,6 +6,7 @@ using JuMP using SparseArrays using LinearAlgebra using Ipopt +using Random # using MadNLP # using KNITRO @@ -23,124 +24,8 @@ test_compute_derivatives_Finite_Diff() test_compute_derivatives_Analytical() # "pglib_opf_case5_pjm.m" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" -test_bilevel_ac_strategic_bidding("pglib_opf_case300_ieee"; percen_bidding_nodes=0.1, Δp=nothing) +Random.seed!(1234) +@time test_bilevel_ac_strategic_bidding("pglib_opf_case300_ieee"; percen_bidding_nodes=0.1) -####################### -# Test Bilevel (LP) -####################### - -using BilevelJuMP, Ipopt - -function constraint_upper!(model, x, y) - @constraints(model, begin - x <= 5 - y <= 8 - y >= 0 - end) -end - -function obj_upper!(model, x, y) - @objective(model, Min, 3x + y) -end - -function build_upper!(model, x, y) - obj_upper!(model, x, y) - constraint_upper!(model, x, y) -end - -function constraint_lower!(model, x, y; slack = zeros(4)) - @constraints(model, begin - x + y + slack[1] <= 8 - 4x + y + slack[2] >= 8 - 2x + y + slack[3] <= 13 - 2x - 7y + slack[4] <= 0 - end) -end - -function obj_lower!(model, x, y, slack) - @objective(model, Min, -x + 10 * sum(s^2 for s in slack)) -end - -function build_lower!(model, x, y, slack) - obj_lower!(model, x, y, slack) - constraint_lower!(model, x, y, slack = slack) -end - -### Test BilevelJuMP ### -using BilevelJuMP - -model = BilevelModel(Ipopt.Optimizer, mode = BilevelJuMP.ProductMode(1e-5)) - -@variable(Lower(model), x_b) -@variable(Upper(model), y_b) - -build_upper!(Upper(model), x_b, y_b) -build_lower!(Lower(model), x_b, y_b, zeros(4)) -optimize!(model) - -objective_value(model) # = 3 * (3.5 * 8/15) + 8/15 # = 6.13... -value(x_b) # = 3.5 * 8/15 # = 1.86... -value(y_b) # = 8/15 # = 0.53... - -### Test DiffOpt Non-Convex ### - -model_upper = Model(Ipopt.Optimizer) -model_lower = Model(Ipopt.Optimizer) - -@variable(model_upper, y) -@variable(model_upper, x_star) -@variable(model_upper, x_aux) -@variable(model_lower, x) -@variable(model_lower, y_p ∈ MOI.Parameter(1.0)) -@variable(model_lower, slack[1:4]) - -build_upper!(model_upper, x_star, y) -constraint_lower!(model_upper, x_aux, y) -build_lower!(model_lower, x, y_p, slack) -primal_vars = [x; slack] -params = [y_p] -evaluator, cons = create_evaluator(model_lower; x=[primal_vars; params]) - -# Define `f(y) = x_star` & `f'(y)` -function f(y_val) - set_parameter_value(y_p, y_val) - optimize!(model_lower) - @assert is_solved_and_feasible(model_lower) - return value(x) -end - -function ∇f(y_val) - @assert value(y_p) == y_val - Δs, sp = compute_sensitivity(evaluator, cons, [0.001]; primal_vars=primal_vars, params=params) - return Δs[1] -end - -function memoize(foo::Function) - last_x, last_f = nothing, nothing - last_dx, last_dfdx = nothing, nothing - function foo_i(x::T...) where {T<:Real} - if T == Float64 - if x !== last_x - last_x, last_f = x, foo(x...) - end - return last_f::T - else - if x !== last_dx - last_dx, last_dfdx = x, foo(x...) - end - return last_dfdx::T - end - end - return (x...) -> foo_i(x...) -end - -memoized_f = memoize(f) - -@operator(model_upper, op_f, 1, memoized_f, ∇f) -@constraint(model_upper, x_star == op_f(y)) - -optimize!(model_upper) - -@test objective_value(model_upper) ≈ objective_value(model) atol=0.05 -@test value(x_star) ≈ value(x_b) atol=0.05 -@test value(y) ≈ value(y_b) atol=0.05 \ No newline at end of file +# Δp=0.0 (no derivative): time=4.61s | obj= $474.18 +# Δp=nothing (no restoration): time=4.61s | obj= $474.18 From c74e9303fad0d6382fae8b7dd4a4e577ca9db0e3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 15 Aug 2024 14:16:59 -0400 Subject: [PATCH 065/108] update --- testing_barrier.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index d5ee3ca1..638ca60d 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -27,5 +27,7 @@ test_compute_derivatives_Analytical() Random.seed!(1234) @time test_bilevel_ac_strategic_bidding("pglib_opf_case300_ieee"; percen_bidding_nodes=0.1) +# Random.seed!(1234) # Δp=0.0 (no derivative): time=4.61s | obj= $474.18 -# Δp=nothing (no restoration): time=4.61s | obj= $474.18 +# Δp=nothing (no restoration): time=452.65s | obj= $79886.16 +# Δp=0.001 (with derivative): time=54.64s | obj= $474.18 From f4468409a3409e699cd8a85679d51fd409b68294 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 15 Aug 2024 16:52:03 -0400 Subject: [PATCH 066/108] add example --- nlp_utilities.jl | 5 ++- opf.jl | 88 +++++++++++++++++++++++++++++++++------------- testing_barrier.jl | 8 +++-- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index f9ec42ef..1d5e56c8 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -3,6 +3,7 @@ using MathOptInterface import MathOptInterface: ConstraintSet, CanonicalConstraintFunction using SparseArrays using LinearAlgebra +import JuMP.index """ create_nlp_model(model::JuMP.Model) @@ -107,10 +108,12 @@ all_params(model::Model) = filter(x -> is_parameter(x), all_variables(model)) Create an evaluator for the model. """ +index(x::JuMP.Containers.DenseAxisArray) = index.(x).data + function create_evaluator(model::Model; x=all_variables(model)) nlp, rows = create_nlp_model(model) backend = MOI.Nonlinear.SparseReverseMode() - evaluator = MOI.Nonlinear.Evaluator(nlp, backend, index.(x)) + evaluator = MOI.Nonlinear.Evaluator(nlp, backend, vcat(index.(x)...)) MOI.initialize(evaluator, [:Hess, :Jac]) return evaluator, rows end diff --git a/opf.jl b/opf.jl index f941367b..705bcc52 100644 --- a/opf.jl +++ b/opf.jl @@ -34,28 +34,14 @@ function build_gen(gen_bus, id, pmax, qmax) ) end -function build_opf_model(case_name; percen_bidding_nodes=0.1) - data = make_basic_network(pglib(case_name)) - data["basic_network"] = false - # add bidding generators - num_bidding_nodes = ceil(Int, length(data["bus"]) * percen_bidding_nodes) - bidding_nodes = parse.(Int, rand(keys(data["bus"]), num_bidding_nodes)) - existing_gens = maximum(parse.(Int, collect(keys(data["gen"])))) - pmax = maximum([data["gen"][g]["pmax"] for g in keys(data["gen"])]) - qmax = maximum([data["gen"][g]["qmax"] for g in keys(data["gen"])]) - bidding_gen_ids = existing_gens + 1:existing_gens + num_bidding_nodes - for (i, node) in enumerate(bidding_nodes) - data["gen"]["$(bidding_gen_ids[i])"] = build_gen(node, bidding_gen_ids[i], pmax, qmax) - end - data = make_basic_network(data) +function build_opf_model(data; add_param_load=false, solver=Ipopt.Optimizer) # create ref PowerModels.standardize_cost_terms!(data, order=2) PowerModels.calc_thermal_limits!(data) ref = PowerModels.build_ref(data)[:it][:pm][:nw][0] - # Model - model = JuMP.Model(Ipopt.Optimizer) - #JuMP.set_optimizer_attribute(model, "print_level", 0) + # create model + model = JuMP.Model(solver) JuMP.@variable(model, va[i in keys(ref[:bus])]) JuMP.@variable(model, ref[:bus][i]["vmin"] <= vm[i in keys(ref[:bus])] <= ref[:bus][i]["vmax"], start=1.0) @@ -66,10 +52,6 @@ function build_opf_model(case_name; percen_bidding_nodes=0.1) JuMP.@variable(model, ref[:gen][i]["pmin"] <= pg[i in keys(ref[:gen])] <= ref[:gen][i]["pmax"]) JuMP.@variable(model, ref[:gen][i]["qmin"] <= qg[i in keys(ref[:gen])] <= ref[:gen][i]["qmax"]) - # add bids - JuMP.@variable(model, pg_bid[i in bidding_gen_ids] ∈ MOI.Parameter.(pmax * 0.5)) - @constraint(model, bid[i in bidding_gen_ids], pg[i] <= pg_bid[i]) - for i in keys(ref[:gen]) JuMP.set_name(pg[i], "pg_$i") JuMP.set_name(qg[i], "qg_$i") @@ -86,6 +68,14 @@ function build_opf_model(case_name; percen_bidding_nodes=0.1) JuMP.@constraint(model, va[i] == 0) end + if add_param_load + @variable(model, pload[i in keys(ref[:bus])] ∈ MOI.Parameter.(0.0)) + @variable(model, qload[i in keys(ref[:bus])] ∈ MOI.Parameter.(0.0)) + else + pload = zeros(length(ref[:bus])) + qload = zeros(length(ref[:bus])) + end + demand_equilibrium = Dict{Int, JuMP.ConstraintRef}() for (i,bus) in ref[:bus] bus_loads = [ref[:load][l] for l in ref[:bus_loads][i]] @@ -95,13 +85,15 @@ function build_opf_model(case_name; percen_bidding_nodes=0.1) sum(p[a] for a in ref[:bus_arcs][i]) == sum(pg[g] for g in ref[:bus_gens][i]) - sum(load["pd"] for load in bus_loads) - + pload[i] - sum(shunt["gs"] for shunt in bus_shunts)*vm[i]^2 ) JuMP.@constraint(model, sum(q[a] for a in ref[:bus_arcs][i]) == sum(qg[g] for g in ref[:bus_gens][i]) - - sum(load["qd"] for load in bus_loads) + + sum(load["qd"] for load in bus_loads) - + qload[i] + sum(shunt["bs"] for shunt in bus_shunts)*vm[i]^2 ) @@ -147,6 +139,30 @@ function build_opf_model(case_name; percen_bidding_nodes=0.1) JuMP.@constraint(model, p_to^2 + q_to^2 <= branch["rate_a"]^2) end + return model, ref, demand_equilibrium, p, q, pg, qg, va, vm, pload, qload +end + +function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipopt.Optimizer) + data = make_basic_network(pglib(case_name)) + data["basic_network"] = false + # add bidding generators + num_bidding_nodes = ceil(Int, length(data["bus"]) * percen_bidding_nodes) + bidding_nodes = parse.(Int, rand(keys(data["bus"]), num_bidding_nodes)) + existing_gens = maximum(parse.(Int, collect(keys(data["gen"])))) + pmax = maximum([data["gen"][g]["pmax"] for g in keys(data["gen"])]) + qmax = maximum([data["gen"][g]["qmax"] for g in keys(data["gen"])]) + bidding_gen_ids = existing_gens + 1:existing_gens + num_bidding_nodes + for (i, node) in enumerate(bidding_nodes) + data["gen"]["$(bidding_gen_ids[i])"] = build_gen(node, bidding_gen_ids[i], pmax, qmax) + end + data = make_basic_network(data) + + model, ref, demand_equilibrium, p, q, pg, qg, va, vm, pload, qload = build_opf_model(data; solver=solver) + + # add bids + JuMP.@variable(model, pg_bid[i in bidding_gen_ids] ∈ MOI.Parameter.(pmax * 0.5)) + @constraint(model, bid[i in bidding_gen_ids], pg[i] <= pg_bid[i]) + model_variables = JuMP.num_variables(model) # for consistency with other solvers, skip the variable bounds in the constraint count @@ -232,7 +248,7 @@ end function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001) # test derivative of the dual of the demand equilibrium constraint - data = build_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes) + data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes) pmax = data["pmax"] primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) @@ -307,4 +323,28 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe println("Duals: ", value.(lambda)) println("Bids: ", value.(pg_bid)) println("Dispatch: ", value.(pg)) -end \ No newline at end of file +end + +function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver=Ipopt.Optimizer) + data = make_basic_network(pglib(case_name)) + + model, ref, demand_equilibrium, p, q, pg, qg, va, vm, pload, qload = build_opf_model(data; solver=solver, add_param_load=true) + num_bus = length(ref[:bus]) + demand_equilibrium = [demand_equilibrium[i] for i in 1:num_bus] + + optimize!(model) + + params = vcat(pload.data, qload.data) + all_primal_variables = all_variables(model) + primal_vars = setdiff(all_primal_variables, params) + + evaluator, cons = create_evaluator(model; x=[primal_vars; params]) + + leq_locations, geq_locations = find_inequealities(cons) + num_ineq = length(leq_locations) + length(geq_locations) + num_primal = length(primal_vars) + lmps_index = [findall(x -> x == i, cons)[1] for i in demand_equilibrium] + + Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=params) + return Δs[1:num_primal], Δs[(num_primal + num_ineq) .+ lmps_index] +end diff --git a/testing_barrier.jl b/testing_barrier.jl index 638ca60d..8aae2513 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -23,11 +23,15 @@ test_compute_derivatives_Finite_Diff() test_compute_derivatives_Analytical() -# "pglib_opf_case5_pjm.m" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" +# "pglib_opf_case5_pjm" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" "pglib_opf_case24_ieee_rts" Random.seed!(1234) -@time test_bilevel_ac_strategic_bidding("pglib_opf_case300_ieee"; percen_bidding_nodes=0.1) +@time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) # Random.seed!(1234) # Δp=0.0 (no derivative): time=4.61s | obj= $474.18 # Δp=nothing (no restoration): time=452.65s | obj= $79886.16 # Δp=0.001 (with derivative): time=54.64s | obj= $474.18 + +Δp = zeros(48) +Δp[1] = 1.0 +Δs_primal, Δs_dual = sesitivity_load("pglib_opf_case24_ieee_rts"; Δp=Δp) \ No newline at end of file From 544aa0b294b09d8ae1cc6b95bff9de7c4d7330f2 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 15 Aug 2024 16:53:14 -0400 Subject: [PATCH 067/108] fix help --- nlp_utilities.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 1d5e56c8..c925e61f 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -108,7 +108,7 @@ all_params(model::Model) = filter(x -> is_parameter(x), all_variables(model)) Create an evaluator for the model. """ -index(x::JuMP.Containers.DenseAxisArray) = index.(x).data +JuMP.index(x::JuMP.Containers.DenseAxisArray) = index.(x).data function create_evaluator(model::Model; x=all_variables(model)) nlp, rows = create_nlp_model(model) From c3a1e9760eba8fa72d1f76896e870397880b2457 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 15 Aug 2024 17:00:02 -0400 Subject: [PATCH 068/108] update --- nlp_utilities.jl | 5 ++--- opf.jl | 5 ++++- testing_barrier.jl | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index c925e61f..a0b469de 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -494,9 +494,8 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) if isnothing(Δp) || iszero(Δp) Λ = dual.(cons) - Δs = ∂s * ones(size(∂s, 2)) - sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) - return Δs, sp + sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], ∂s * ones(size(∂s, 2))) + return ∂s, sp end Δs = ∂s * Δp num_bounds = length(has_up) + length(has_low) diff --git a/opf.jl b/opf.jl index 705bcc52..09a7ad3d 100644 --- a/opf.jl +++ b/opf.jl @@ -291,6 +291,9 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe end Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) + if isnothing(Δp) + Δs = Δs * ones(size(Δs, 2)) + end for i in 1:num_bidding_nodes g[i] = Δs[i] end @@ -346,5 +349,5 @@ function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver= lmps_index = [findall(x -> x == i, cons)[1] for i in demand_equilibrium] Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=params) - return Δs[1:num_primal], Δs[(num_primal + num_ineq) .+ lmps_index] + return Δs[1:num_primal, :], Δs[(num_primal + num_ineq) .+ lmps_index, :] end diff --git a/testing_barrier.jl b/testing_barrier.jl index 8aae2513..4921aa47 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -32,6 +32,6 @@ Random.seed!(1234) # Δp=nothing (no restoration): time=452.65s | obj= $79886.16 # Δp=0.001 (with derivative): time=54.64s | obj= $474.18 -Δp = zeros(48) -Δp[1] = 1.0 -Δs_primal, Δs_dual = sesitivity_load("pglib_opf_case24_ieee_rts"; Δp=Δp) \ No newline at end of file +Δs_primal, Δs_dual = sesitivity_load("pglib_opf_case24_ieee_rts") +Δs_dual[21,1:24] +Δs_dual[21,25:end] \ No newline at end of file From cff5ad032d847d9ef1eda728b5bf2d539929ae4b Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 26 Aug 2024 16:01:32 -0400 Subject: [PATCH 069/108] add moonlander example --- moonlanding.jl | 75 ++++++++++++++++++++++++++++++++++++++++++++++ testing_barrier.jl | 47 ++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 moonlanding.jl diff --git a/moonlanding.jl b/moonlanding.jl new file mode 100644 index 00000000..27ad2771 --- /dev/null +++ b/moonlanding.jl @@ -0,0 +1,75 @@ +function moonlander_JMP(;target::Array{Float64}=[5.0, 5.0],nh::Int64=500, _I = 0.1) + ## parameters + if size(target) != (2,) + error("The input matrix must be 2x1.") + end + m = 1.0 + g = 9.81 + D = 1.0 + max_thrust = 2*g + + ## define the problem + model = JuMP.Model() + + @variable(model, Isp ∈ MOI.Parameter(_I)) + + @variables(model, begin + 0.0 <= tf + # state variables + p1[k=0:nh] + p2[k=0:nh] + dp1[k=0:nh] + dp2[k=0:nh] + theta[k=0:nh] + dtheta[k=0:nh] + + # control variables + 0 <= F1[k=0:nh] <= max_thrust, (start = 5.0) + 0 <= F2[k=0:nh] <= max_thrust, (start = 5.0) + end) + + # Initial and final conditions + @constraints(model, begin + p1[0] == 0.0 + p2[0] == 0.0 + dp1[0] == 0.0 + dp2[0] == 0.0 + theta[0] == 0.0 + dtheta[0] == 0.0 + p1[nh] == target[1] + p2[nh] == target[2] + dp1[nh] == 0.0 + dp2[nh] == 0.0 + end) + + #dynamics + @expressions(model, begin + F_r[k=0:nh], [cos(theta[k]) -sin(theta[k]) p1[k]; + sin(theta[k]) cos(theta[k]) p2[k]; + 0.0 0.0 1.0] + end) + @expressions(model, begin + F_tot[k=0:nh], (F_r[k] * [0; F1[k] + F2[k]; 0])[1:2] + end) + @expressions(model, begin + ddp1[k=0:nh], (1/m) * F_tot[k][1] + ddp2[k=0:nh], (1/m) * F_tot[k][2] - g + ddtheta[k=0:nh], (1/Isp) * (D/2) * (F2[k] - F1[k]) + end) + @expressions(model, begin + step, tf / nh + end) + + @constraints(model, begin + d_p1[k=1:nh], p1[k] == p1[k-1] + 0.5 * step * (dp1[k] + dp1[k-1]) + d_p2[k=1:nh], p2[k] == p2[k-1] + 0.5 * step * (dp2[k] + dp2[k-1]) + d_dp1[k=1:nh], dp1[k] == dp1[k-1] + 0.5 * step * (ddp1[k] + ddp1[k-1]) + d_dp2[k=1:nh], dp2[k] == dp2[k-1] + 0.5 * step * (ddp2[k] + ddp2[k-1]) + d_theta[k=1:nh], theta[k] == theta[k-1] + 0.5 * step * (dtheta[k] + dtheta[k-1]) + d_dtheta[k=1:nh], dtheta[k] == dtheta[k-1] + 0.5 * step * (ddtheta[k] + ddtheta[k-1]) + end) + + @objective(model, Min, tf) + + return model, tf, Isp +end \ No newline at end of file diff --git a/testing_barrier.jl b/testing_barrier.jl index 4921aa47..c808fb91 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -23,6 +23,10 @@ test_compute_derivatives_Finite_Diff() test_compute_derivatives_Analytical() +################################################ +# Strategic bidding test +################################################ + # "pglib_opf_case5_pjm" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" "pglib_opf_case24_ieee_rts" Random.seed!(1234) @time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) @@ -34,4 +38,45 @@ Random.seed!(1234) Δs_primal, Δs_dual = sesitivity_load("pglib_opf_case24_ieee_rts") Δs_dual[21,1:24] -Δs_dual[21,25:end] \ No newline at end of file +Δs_dual[21,25:end] + +################################################ +# Moonlander test +################################################ + +include("moonlanding.jl") +using HSL_jll + +model, tf, Isp = moonlander_JMP() +set_optimizer(model, optimizer_with_attributes(Ipopt.Optimizer, + "print_level" => 0, + "hsllib" => HSL_jll.libhsl_path, + "linear_solver" => "MA57" +)) +JuMP.optimize!(model) +termination_status = JuMP.termination_status(model) +value(tf), value(Isp), termination_status + +function compute_sentitivity_for_var(model; _primal_vars=[], _cons=[], Δp=nothing) + primal_vars = all_primal_vars(model) + params = all_params(model) + vars_idx = [findall(x -> x == i, primal_vars)[1] for i in _primal_vars] + evaluator, cons = create_evaluator(model; x=[primal_vars; params]) + num_cons = length(cons) + cons_idx = [findall(x -> x == i, cons)[1] for i in _cons] + leq_locations, geq_locations = find_inequealities(cons) + num_ineq = length(leq_locations) + length(geq_locations) + num_primal = length(primal_vars) + + Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=params) + return Δs[vars_idx, :], Δs[num_primal+num_ineq.+cons_idx, :], sp[vars_idx, :], sp[num_primal+num_ineq.+cons_idx, :] +end + +Δp=nothing + +Δs_primal, Δs_dual, sp_primal, sp_dual = compute_sentitivity_for_var(model; _primal_vars=[tf], Δp=Δp) + +set_parameter_value(Isp, 1.1) +JuMP.optimize!(model) +termination_status = JuMP.termination_status(model) +value(tf), value(Isp), termination_status \ No newline at end of file From b3f4d9464f9aad64860bbc7e56492c0b21bef56b Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 26 Aug 2024 16:10:54 -0400 Subject: [PATCH 070/108] add moonlander test --- testing_barrier.jl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/testing_barrier.jl b/testing_barrier.jl index c808fb91..ec680a3d 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -47,7 +47,8 @@ Random.seed!(1234) include("moonlanding.jl") using HSL_jll -model, tf, Isp = moonlander_JMP() +Isp_0 = 0.3 +model, tf, Isp = moonlander_JMP(_I=Isp_0) set_optimizer(model, optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0, "hsllib" => HSL_jll.libhsl_path, @@ -55,7 +56,7 @@ set_optimizer(model, optimizer_with_attributes(Ipopt.Optimizer, )) JuMP.optimize!(model) termination_status = JuMP.termination_status(model) -value(tf), value(Isp), termination_status +tf_0 = value(tf) function compute_sentitivity_for_var(model; _primal_vars=[], _cons=[], Δp=nothing) primal_vars = all_primal_vars(model) @@ -75,8 +76,12 @@ end Δp=nothing Δs_primal, Δs_dual, sp_primal, sp_dual = compute_sentitivity_for_var(model; _primal_vars=[tf], Δp=Δp) +Δp = 0.001 +tf_p = tf_0 + Δs_primal[1] * Δp -set_parameter_value(Isp, 1.1) +set_parameter_value(Isp, Isp_0+Δp) JuMP.optimize!(model) termination_status = JuMP.termination_status(model) -value(tf), value(Isp), termination_status \ No newline at end of file +tf_p_true = value(tf) + +@test tf_p ≈ tf_p_true rtol=1e-3 \ No newline at end of file From 9abb1948d1235acdf780a15018e6479e77901b6d Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 4 Sep 2024 18:25:36 -0400 Subject: [PATCH 071/108] update tests --- nlp_utilities_test.jl | 69 +++++++++++++++++++++++++++++- test_jump.jl | 98 +++++++++++++++++++++++++++++++++++++++++++ testing_barrier.jl | 19 ++++++++- 3 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 test_jump.jl diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 1acdd5cd..60c99e92 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -201,6 +201,65 @@ function create_jump_model_3(p_val = [-1.5]) return model, [x], [con1; con2], [p] end +function create_jump_model_4(p_val = [1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x <= p) + @constraint(model, con2, x <= 2) + @objective(model, Max, x) + + return model, [x], [con1; con2], [p] +end + +function create_jump_model_5(p_val = [1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x >= p) + @constraint(model, con2, x >= 2) + @objective(model, Max, -x) + + return model, [x], [con1; con2], [p] +end + +# Softmax model +h(y) = - sum(y .* log.(y)) +softmax(x) = exp.(x) / sum(exp.(x)) +function create_jump_model_6(p_a = collect(1.0:0.1:2.0)) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, x[i=1:length(p_a)] ∈ MOI.Parameter.(p_a)) + + # Variables + @variable(model, y[1:length(p_a)] >= 0.0) + + # Constraints + @constraint(model, con1, sum(y) == 1) + @constraint(model, con2[i=1:length(x)], y[i] <= 1) + + # Objective + @objective(model, Max, dot(x, y) + h(y)) + + return model, y, [con1; con2], x +end + DICT_PROBLEMS_Analytical = Dict( "geq no impact" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), @@ -210,6 +269,14 @@ DICT_PROBLEMS_Analytical = Dict( "geq bound impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; 0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; 0.1; -0.1], model_generator=create_jump_model_3), + "leq impact" => (p_a=[-2.1], Δp=[-0.2], Δs_a=[-0.2; 0.0; -0.2], model_generator=create_jump_model_3), + "leq no impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_4), + "leq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_4), + "leq impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_4), + "geq no impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_5), + "geq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_5), + "geq impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_5), + "softmax" => (p_a=collect(1.0:0.1:2.0), Δp=collect(-0.1:0.02:0.1), Δs_a=jacobian(softmax, p_a)[1] * Δp, model_generator=create_jump_model_6) ) function test_compute_derivatives_Analytical() @@ -220,7 +287,7 @@ function test_compute_derivatives_Analytical() # Compute derivatives (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) # Check sensitivities - @test all(isapprox.(Δs[1:length(Δs_a)], Δs_a; atol = 1e-6)) + @test all(isapprox.(Δs[1:length(Δs_a)], Δs_a; atol = 1e-4)) end end diff --git a/test_jump.jl b/test_jump.jl new file mode 100644 index 00000000..2b22d4c2 --- /dev/null +++ b/test_jump.jl @@ -0,0 +1,98 @@ +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, x) +@variable(model, y) + +@objective(model, Min, x + y) + +con1_ = @constraint(model, x >= 1) + +con2_ = @constraint(model, -y <= -2) + +JuMP.optimize!(model) + +dual(con1_) + +dual(con2_) + +####### + +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, x) +@variable(model, y) +@variable(model, s1) +@variable(model, s2) + + +@objective(model, Min, x + y) + +con1 = @constraint(model, x - 1 - s1 == 0) + +con2 = @constraint(model, -y + 2 - s2 == 0) + +con1s = @constraint(model, s1 >= 0) +con2s = @constraint(model, s2 <= 0) + +JuMP.optimize!(model) + +dual(con1) +dual(con2) +dual(con1s) +dual(con2s) + +####### + +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, x) +@variable(model, y) + +@objective(model, Max, x + y) + +con1_ = @constraint(model, -x >= -1) + +con2_ = @constraint(model, y <= 2) + +JuMP.optimize!(model) + +dual(con1_) + +dual(con2_) + +####### + +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, 0 <= x <= 1) +@variable(model, 0 <= y <= 1) + +@objective(model, Max, x - y) + +JuMP.optimize!(model) + +model + +dual.(LowerBoundRef(y)) + +dual.(UpperBoundRef(x)) + + +####### + +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, 0 <= x <= 1) +@variable(model, 0 <= y <= 1) + +@objective(model, Min, x - y) + +JuMP.optimize!(model) + +model.moi_backend.optimizer.model.inner.mult_x_L + +model.moi_backend.optimizer.model.inner.mult_x_U + +dual.(LowerBoundRef(x)) + +dual.(UpperBoundRef(y)) \ No newline at end of file diff --git a/testing_barrier.jl b/testing_barrier.jl index ec680a3d..ad87468d 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -28,14 +28,29 @@ test_compute_derivatives_Analytical() ################################################ # "pglib_opf_case5_pjm" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" "pglib_opf_case24_ieee_rts" -Random.seed!(1234) -@time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) +# Random.seed!(1234) +# @time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) +# casename = "pglib_opf_case300_ieee" # Random.seed!(1234) +# @time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1) # Δp=0.0 (no derivative): time=4.61s | obj= $474.18 # Δp=nothing (no restoration): time=452.65s | obj= $79886.16 # Δp=0.001 (with derivative): time=54.64s | obj= $474.18 +# casename = "pglib_opf_case14_ieee" +# Random.seed!(1234) +# @time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1, Δp=nothing) +# Δp=0.0 (no derivative): time=8.93s | obj= $0.00 +# Δp=nothing (no restoration): time=0.94s | obj= $0.00 +# Δp=0.001 (with derivative): time=0.58s | obj= $0.00 + + + +################################################ +# Load sensitivity +################################################ + Δs_primal, Δs_dual = sesitivity_load("pglib_opf_case24_ieee_rts") Δs_dual[21,1:24] Δs_dual[21,25:end] From 533d3a4643463950d584c1d003fb96c30d8d3107 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 4 Sep 2024 21:26:25 -0400 Subject: [PATCH 072/108] start fix sign --- nlp_utilities.jl | 66 +++++++++++++++++++++++++----------- nlp_utilities_test.jl | 29 +++++++++------- test_jump.jl | 78 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 139 insertions(+), 34 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index a0b469de..79494e54 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -46,18 +46,23 @@ function fill_off_diagonal(H) return ret end +sense_mult(x) = JuMP.objective_sense(owner_model(x)) == MOI.MIN_SENSE ? 1.0 : -1.0 +sense_mult(x::Vector) = sense_mult(x[1]) + """ compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) Compute the optimal Hessian of the Lagrangian. """ function compute_optimal_hessian(evaluator::MOI.Nonlinear.Evaluator, rows::Vector{ConstraintRef}, x::Vector{VariableRef}) - sense_multiplier = objective_sense(owner_model(x[1])) == MOI.MIN_SENSE ? -1.0 : 1.0 + sense_multiplier = objective_sense(owner_model(x[1])) == MOI.MIN_SENSE ? 1.0 : -1.0 hessian_sparsity = MOI.hessian_lagrangian_structure(evaluator) I = [i for (i, _) in hessian_sparsity] J = [j for (_, j) in hessian_sparsity] V = zeros(length(hessian_sparsity)) - MOI.eval_hessian_lagrangian(evaluator, V, value.(x), sense_multiplier, dual.(rows)) + # The signals are being sdjusted to match the Ipopt convention (inner.mult_g) + # but we don't know if we need to adjust the objective function multiplier + MOI.eval_hessian_lagrangian(evaluator, V, value.(x), 1.0, - sense_multiplier * dual.(rows)) H = SparseArrays.sparse(I, J, V, length(x), length(x)) return fill_off_diagonal(H) end @@ -74,7 +79,7 @@ function compute_optimal_jacobian(evaluator::MOI.Nonlinear.Evaluator, rows::Vect V = zeros(length(jacobian_sparsity)) MOI.eval_constraint_jacobian(evaluator, V, value.(x)) A = SparseArrays.sparse(I, J, V, length(rows), length(x)) - return A # Matrix(A) + return A end """ @@ -170,9 +175,10 @@ function get_slack_inequality(con::ConstraintRef) set_type = typeof(MOI.get(owner_model(con), MOI.ConstraintSet(), con)) obj = constraint_object(con) if set_type <: MOI.LessThan - # c(x) <= b --> slack = -c(x) + b | slack >= 0 + # c(x) <= b --> slack = c(x) - b | slack <= 0 return obj.func - obj.set.upper end + # c(x) >= b --> slack = c(x) - b | slack >= 0 return obj.func - obj.set.lower end @@ -182,6 +188,7 @@ end Compute the solution and bounds of the primal variables. """ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vector{C}) where {C<:ConstraintRef} + sense_multiplier = sense_mult(primal_vars) num_vars = length(primal_vars) leq_locations, geq_locations = find_inequealities(cons) ineq_locations = vcat(geq_locations, leq_locations) @@ -199,21 +206,31 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec V_L = spzeros(num_vars+num_ineq) X_L = spzeros(num_vars+num_ineq) for (i, j) in enumerate(has_low) - V_L[i] = dual.(LowerBoundRef(primal_vars[j])) + V_L[i] = dual.(LowerBoundRef(primal_vars[j])) * sense_multiplier + # + V_L[i] <= -1e-6 && @info "Dual of lower bound must be positive" i V_L[i] + # X_L[i] = JuMP.lower_bound(primal_vars[j]) end for (i, con) in enumerate(cons[geq_locations]) - V_L[num_vars+i] = dual.(con) + V_L[num_vars+i] = dual.(con) * (- sense_multiplier) + # + V_L[num_vars+i] <= -1e-6 && @info "Dual of geq constraint must be positive" i V_L[num_vars+i] end # value and dual of the upper bounds V_U = spzeros(num_vars+num_ineq) X_U = spzeros(num_vars+num_ineq) for (i, j) in enumerate(has_up) - V_U[i] = dual.(UpperBoundRef(primal_vars[j])) + V_U[i] = dual.(UpperBoundRef(primal_vars[j])) * (- sense_multiplier) + # + V_U[i] <= -1e-6 && @info "Dual of upper bound must be positive" i V_U[i] + # X_U[i] = JuMP.upper_bound(primal_vars[j]) end for (i, con) in enumerate(cons[leq_locations]) - V_U[num_vars+i] = dual.(con) + V_U[num_vars+i] = dual.(con) * (- sense_multiplier) + # + V_U[num_vars+i] <= -1e-6 && @info "Dual of leq constraint must be non-positive" i V_U[num_vars+i] end return X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, vcat(has_up, collect(num_vars+num_geq+1:num_vars+num_geq+num_leq)), vcat(has_low, collect(num_vars+1:num_vars+num_geq)) @@ -271,14 +288,21 @@ function build_M_N(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRe # Hessian of the lagrangian wrt the primal variables W = spzeros(num_vars + num_ineq, num_vars + num_ineq) W[1:num_vars, 1:num_vars] = hessian[1:num_vars, 1:num_vars] - # Jacobian of the constraints wrt the primal variables + # Jacobian of the constraints A = spzeros(num_cons, num_vars + num_ineq) + # A is the Jacobian of: c(x) = b and c(x) <= b and c(x) >= b, possibly all mixed up. + # Each of the will be re-written as: + # c(x) - b = 0 + # c(x) - b - su = 0, su <= 0 + # c(x) - b - sl = 0, sl >= 0 + # Jacobian of the constraints wrt the primal variables A[:, 1:num_vars] = jacobian[:, 1:num_vars] + # Jacobian of the constraints wrt the slack variables for (i,j) in enumerate(geq_locations) A[j, num_vars+i] = -1 end for (i,j) in enumerate(leq_locations) - A[j, num_vars+i] = 1 + A[j, num_vars+i] = -1 end # Partial second derivative of the lagrangian wrt primal solution and parameters ∇ₓₚL = spzeros(num_vars + num_ineq, num_parms) @@ -485,6 +509,7 @@ end function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, Δp; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 ) + sense_multiplier = sense_mult(primal_vars) num_cons = length(cons) num_var = length(primal_vars) # Solution and bounds @@ -493,29 +518,30 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) if isnothing(Δp) || iszero(Δp) - Λ = dual.(cons) + Λ = - dual.(cons) * sense_multiplier sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], ∂s * ones(size(∂s, 2))) return ∂s, sp end Δs = ∂s * Δp num_bounds = length(has_up) + length(has_low) - Λ = dual.(cons) + + Λ = - dual.(cons) * sense_multiplier num_geq = length(geq_locations) num_leq = length(leq_locations) - for i in 1:num_leq # slack of leq constraints - Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] - end - Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form + # for i in 1:num_leq # slack of leq constraints + # Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] + # end + # Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) if !isempty(r1) @warn "Relaxation needed" Δs = fix_and_relax(E, K, N, r1, Δp) - for i in 1:num_leq # slack of leq constraints - Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] - end - Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form + # for i in 1:num_leq # slack of leq constraints + # Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] + # end + # Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) end return Δs, sp diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 60c99e92..2c157586 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -83,7 +83,7 @@ function test_compute_optimal_hess_jacobian() full_hessian, full_jacobian = compute_optimal_hess_jac(evaluator, rows, [x; params]) hessian = full_hessian[1:num_var, 1:num_var] # Check Hessian - @test all(hessian .≈ analytic_hessian(value.(x), -1.0, dual.(cons), value.(params))) + @test all(hessian .≈ analytic_hessian(value.(x), 1.0, -dual.(cons), value.(params))) # TODO: Test hessial of parameters # Check Jacobian @test all(full_jacobian .≈ analytic_jacobian(value.(x), value.(params))) @@ -98,7 +98,7 @@ From sIpopt paper: https://optimization-online.org/2011/04/3008/ =# ################################################ -function create_nonlinear_jump_model_sipopt() +function create_nonlinear_jump_model_sipopt(ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @variable(model, p1 ∈ MOI.Parameter(4.5)) @@ -106,27 +106,32 @@ function create_nonlinear_jump_model_sipopt() @variable(model, x[i = 1:3] >= 0, start = -i) @constraint(model, g_1, 6 * x[1] + 3 * x[2] + 2 * x[3] - p1 == 0) @constraint(model, g_2, p2 * x[1] + x[2] - x[3] - 1 == 0) - @objective(model, Min, x[1]^2 + x[2]^2 + x[3]^2) + if ismin + @objective(model, Min, x[1]^2 + x[2]^2 + x[3]^2) + else + @objective(model, Max, -x[1]^2 - x[2]^2 - x[3]^2) + end return model, x, [g_1; g_2], [p1; p2] end function test_compute_derivatives() - @testset "Compute Derivatives No Inequalities" begin + @testset "Compute Derivatives No Inequalities: ismin=$ismin" for ismin in [true, false] # Model - model, primal_vars, cons, params = create_nonlinear_jump_model_sipopt() + sign_fix = ismin ? 1.0 : -1.0 + model, primal_vars, cons, params = create_nonlinear_jump_model_sipopt(ismin) optimize!(model) @assert is_solved_and_feasible(model) # Analytical solutions case b pb = [4.5, 1.0] - s_pb = [0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] - @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pb; atol = 1e-6)) + s_pb = [0.5, 0.5, 0.0, 0.0, - sign_fix * 1.0, 0.0, 0.0, sign_fix * 1.0] + @assert all(isapprox.([value.(primal_vars); - sign_fix * dual.(cons); sign_fix * dual.(LowerBoundRef.(primal_vars))], s_pb; atol = 1e-6)) # Analytical solutions case a pa = [5.0, 1.0] - s_pa = [0.6327, 0.3878, 0.0204, 0.1633, 0.2857, 0, 0, 0] + s_pa = [0.6327, 0.3878, 0.0204, - sign_fix * 0.1633, - sign_fix * 0.2857, 0, 0, 0] set_parameter_value.(params, pa) optimize!(model) @assert is_solved_and_feasible(model) - @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) + @assert all(isapprox.([value.(primal_vars); - sign_fix * dual.(cons); sign_fix * dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) # Compute derivatives without accounting for active set changes evaluator, rows = create_evaluator(model; x=[primal_vars; params]) X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) @@ -134,11 +139,11 @@ function test_compute_derivatives() # Check linear approx s_pb Δp = pb - pa s_pb_approx_violated = s_pa + ∂s * Δp - @test all(isapprox.([0.5765; 0.3775; -0.0459; 0.1327; 0.3571; 0.0; 0.0; 0.0], s_pb_approx_violated; atol = 1e-4)) + @test all(isapprox.([0.5765; 0.3775; -0.0459; - sign_fix * 0.1327; - sign_fix * 0.3571; 0.0; 0.0; 0.0], s_pb_approx_violated; atol = 1e-4)) # Account for active set changes Δs, s_pb_approx = compute_sensitivity(evaluator, rows, Δp; primal_vars, params) # s_pb_approx = s_pa .+ Δs - @test all(isapprox.([0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], s_pb_approx; atol = 1e-6)) + @test all(isapprox.([0.5, 0.5, 0.0, 0.0, - sign_fix * 1.0, 0.0, 0.0, 0.0], s_pb_approx; atol = 1e-6)) end end @@ -276,7 +281,7 @@ DICT_PROBLEMS_Analytical = Dict( "geq no impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_5), "geq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_5), "geq impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_5), - "softmax" => (p_a=collect(1.0:0.1:2.0), Δp=collect(-0.1:0.02:0.1), Δs_a=jacobian(softmax, p_a)[1] * Δp, model_generator=create_jump_model_6) + # "softmax" => (p_a=collect(1.0:0.1:2.0), Δp=collect(-0.1:0.02:0.1), Δs_a=jacobian(softmax, p_a)[1] * Δp, model_generator=create_jump_model_6) ) function test_compute_derivatives_Analytical() diff --git a/test_jump.jl b/test_jump.jl index 2b22d4c2..b9095a4c 100644 --- a/test_jump.jl +++ b/test_jump.jl @@ -11,6 +11,9 @@ con2_ = @constraint(model, -y <= -2) JuMP.optimize!(model) +model.moi_backend.optimizer.model.inner.mult_g +model.moi_backend.optimizer.model.inner.g + dual(con1_) dual(con2_) @@ -56,6 +59,9 @@ con2_ = @constraint(model, y <= 2) JuMP.optimize!(model) +model.moi_backend.optimizer.model.inner.mult_g +model.moi_backend.optimizer.model.inner.g + dual(con1_) dual(con2_) @@ -71,7 +77,8 @@ model = JuMP.Model(Ipopt.Optimizer) JuMP.optimize!(model) -model +model.moi_backend.optimizer.model.inner.mult_x_L +model.moi_backend.optimizer.model.inner.mult_x_U dual.(LowerBoundRef(y)) @@ -95,4 +102,71 @@ model.moi_backend.optimizer.model.inner.mult_x_U dual.(LowerBoundRef(x)) -dual.(UpperBoundRef(y)) \ No newline at end of file +dual.(UpperBoundRef(y)) + +####### + +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, x) +@variable(model, y <= 2) +@variable(model, z >= 3) +@variable(model, w) +@variable(model, u) + +@objective(model, Min, x - y + z + w - u) + +con1 = @constraint(model, x - 1 == 0) + +con1s = @constraint(model, w - 4 >= 0) +con2s = @constraint(model, u - 5 <= 0) + +JuMP.optimize!(model) + +JuMP.termination_status(model) + +model.moi_backend.optimizer.model.inner.mult_x_L +model.moi_backend.optimizer.model.inner.mult_x_U + +model.moi_backend.optimizer.model.inner.mult_g +model.moi_backend.optimizer.model.inner.g + +dual.(UpperBoundRef(y)) +dual.(LowerBoundRef(z)) +dual(con1) +dual(con1s) +dual(con2s) + + +####### Max + +model = JuMP.Model(Ipopt.Optimizer) + +@variable(model, x) +@variable(model, y <= 2) +@variable(model, z >= 3) +@variable(model, w) +@variable(model, u) + +@objective(model, Max, - x + y - z - w + u) + +con1 = @constraint(model, x - 1 == 0) + +con1s = @constraint(model, w - 4 >= 0) +con2s = @constraint(model, u - 5 <= 0) + +JuMP.optimize!(model) + +JuMP.termination_status(model) + +model.moi_backend.optimizer.model.inner.mult_x_L +model.moi_backend.optimizer.model.inner.mult_x_U + +model.moi_backend.optimizer.model.inner.mult_g +model.moi_backend.optimizer.model.inner.g + +dual.(UpperBoundRef(y)) +dual.(LowerBoundRef(z)) +dual(con1) +dual(con1s) +dual(con2s) \ No newline at end of file From 28c40b4fa24b2f5b7e626b0cfe22d68af1b3124f Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 5 Sep 2024 15:24:00 -0400 Subject: [PATCH 073/108] update code --- nlp_utilities.jl | 68 +++++++++++++++++++++++------- nlp_utilities_test.jl | 98 ++++++++++++++++++++++++++++++++----------- test_jump.jl | 2 +- testing_barrier.jl | 1 - 4 files changed, 127 insertions(+), 42 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 79494e54..b027f022 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -208,14 +208,24 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec for (i, j) in enumerate(has_low) V_L[i] = dual.(LowerBoundRef(primal_vars[j])) * sense_multiplier # - V_L[i] <= -1e-6 && @info "Dual of lower bound must be positive" i V_L[i] + if sense_multiplier == 1.0 + V_L[i] <= -1e-6 && @info "Dual of lower bound must be positive" i V_L[i] + else + V_L[i] >= 1e-6 && @info "Dual of lower bound must be negative" i V_L[i] + end # X_L[i] = JuMP.lower_bound(primal_vars[j]) end for (i, con) in enumerate(cons[geq_locations]) - V_L[num_vars+i] = dual.(con) * (- sense_multiplier) + # By convention jump dual will allways be positive for geq constraints + # but for ipopt it will be positive if min problem and negative if max problem + V_L[num_vars+i] = dual.(con) * (sense_multiplier) # - V_L[num_vars+i] <= -1e-6 && @info "Dual of geq constraint must be positive" i V_L[num_vars+i] + if sense_multiplier == 1.0 + V_L[num_vars+i] <= -1e-6 && @info "Dual of geq constraint must be positive" i V_L[num_vars+i] + else + V_L[num_vars+i] >= 1e-6 && @info "Dual of geq constraint must be negative" i V_L[num_vars+i] + end end # value and dual of the upper bounds V_U = spzeros(num_vars+num_ineq) @@ -223,16 +233,25 @@ function compute_solution_and_bounds(primal_vars::Vector{VariableRef}, cons::Vec for (i, j) in enumerate(has_up) V_U[i] = dual.(UpperBoundRef(primal_vars[j])) * (- sense_multiplier) # - V_U[i] <= -1e-6 && @info "Dual of upper bound must be positive" i V_U[i] + if sense_multiplier == 1.0 + V_U[i] <= -1e-6 && @info "Dual of upper bound must be positive" i V_U[i] + else + V_U[i] >= 1e-6 && @info "Dual of upper bound must be negative" i V_U[i] + end # X_U[i] = JuMP.upper_bound(primal_vars[j]) end for (i, con) in enumerate(cons[leq_locations]) + # By convention jump dual will allways be negative for leq constraints + # but for ipopt it will be positive if min problem and negative if max problem V_U[num_vars+i] = dual.(con) * (- sense_multiplier) # - V_U[num_vars+i] <= -1e-6 && @info "Dual of leq constraint must be non-positive" i V_U[num_vars+i] + if sense_multiplier == 1.0 + V_U[num_vars+i] <= -1e-6 && @info "Dual of leq constraint must be positive" i V_U[num_vars+i] + else + V_U[num_vars+i] >= 1e-6 && @info "Dual of leq constraint must be negative" i V_U[num_vars+i] + end end - return X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, vcat(has_up, collect(num_vars+num_geq+1:num_vars+num_geq+num_leq)), vcat(has_low, collect(num_vars+1:num_vars+num_geq)) end @@ -465,7 +484,7 @@ end Find the bound violations of the primal-dual solution first order approximation based on the (unrelaxed) sensitivity estimation. """ -function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) +function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol, ismin) num_w = length(X) num_low = length(has_low) num_up = length(has_up) @@ -478,10 +497,18 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t push!(_E, i) push!(r1, X[i] - X_L[i]) end - if sp[num_w+num_cons+j] < -tol - println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) - push!(_E, num_w+num_cons+j) - push!(r1, V_L[i]) + if ismin + if sp[num_w+num_cons+j] < -tol + println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) + push!(_E, num_w+num_cons+j) + push!(r1, V_L[i]) + end + else + if sp[num_w+num_cons+j] > tol + println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) + push!(_E, num_w+num_cons+j) + push!(r1, V_L[i]) + end end end for (j, i) in enumerate(has_up) @@ -490,10 +517,18 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t push!(_E, i) push!(r1, X_U[i] - X[i]) end - if sp[num_w+num_cons+num_low+j] > tol - println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) - push!(_E, num_w+num_cons+num_low+j) - push!(r1, V_U[i]) + if ismin + if sp[num_w+num_cons+num_low+j] < -tol + println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) + push!(_E, num_w+num_cons+num_low+j) + push!(r1, V_U[i]) + end + else + if sp[num_w+num_cons+num_low+j] > tol + println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) + push!(_E, num_w+num_cons+num_low+j) + push!(r1, V_U[i]) + end end end @@ -509,6 +544,7 @@ end function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{ConstraintRef}, Δp; primal_vars=all_primal_vars(model), params=all_params(model), tol=1e-6 ) + ismin = sense_mult(primal_vars) == 1.0 sense_multiplier = sense_mult(primal_vars) num_cons = length(cons) num_var = length(primal_vars) @@ -534,7 +570,7 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution - E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol, ismin) if !isempty(r1) @warn "Relaxation needed" Δs = fix_and_relax(E, K, N, r1, Δp) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index 2c157586..e7863171 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -14,7 +14,7 @@ https://github.com/jump-dev/JuMP.jl/blob/301d46e81cb66c74c6e22cd89fb89ced740f157 =# ################################################ -function create_nonlinear_jump_model() +function create_nonlinear_jump_model(;ismin=true) model = Model(Ipopt.Optimizer) set_silent(model) @variable(model, p ∈ MOI.Parameter(1.0)) @@ -23,7 +23,12 @@ function create_nonlinear_jump_model() @variable(model, x[i = 1:2], start = -i) @constraint(model, g_1, x[1]^2 <= p) @constraint(model, g_2, p * (x[1] + x[2])^2 <= p2) - @objective(model, Min, (1 - x[1])^2 + p3 * (x[2] - x[1]^2)^2) + if ismin + @objective(model, Min, (1 - x[1])^2 + p3 * (x[2] - x[1]^2)^2) + else + @objective(model, Max, -(1 - x[1])^2 - p3 * (x[2] - x[1]^2)^2) + end + return model, x, [g_1; g_2], [p; p2; p3] end @@ -98,7 +103,7 @@ From sIpopt paper: https://optimization-online.org/2011/04/3008/ =# ################################################ -function create_nonlinear_jump_model_sipopt(ismin = true) +function create_nonlinear_jump_model_sipopt(;ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @variable(model, p1 ∈ MOI.Parameter(4.5)) @@ -118,7 +123,7 @@ function test_compute_derivatives() @testset "Compute Derivatives No Inequalities: ismin=$ismin" for ismin in [true, false] # Model sign_fix = ismin ? 1.0 : -1.0 - model, primal_vars, cons, params = create_nonlinear_jump_model_sipopt(ismin) + model, primal_vars, cons, params = create_nonlinear_jump_model_sipopt(;ismin=ismin) optimize!(model) @assert is_solved_and_feasible(model) # Analytical solutions case b @@ -265,21 +270,41 @@ function create_jump_model_6(p_a = collect(1.0:0.1:2.0)) return model, y, [con1; con2], x end +# f(x, p) = 0 +# x = g(p) +# ∂x/∂p = ∂g/∂p + +function create_jump_model_7(p_val = [1.5], g = sin) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x * g(p) == 1) + @objective(model, Min, 0) + + return model, [x], [con1], [p] +end DICT_PROBLEMS_Analytical = Dict( "geq no impact" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), - "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_1), - "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), - "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1], model_generator=create_jump_model_2), + # "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_1), + "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; -0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), + # "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1], model_generator=create_jump_model_2), "geq bound impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; 0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), - "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; 0.1; -0.1], model_generator=create_jump_model_3), + # "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; 0.1; -0.1], model_generator=create_jump_model_3), "leq impact" => (p_a=[-2.1], Δp=[-0.2], Δs_a=[-0.2; 0.0; -0.2], model_generator=create_jump_model_3), "leq no impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_4), - "leq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_4), + # "leq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_4), "leq impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_4), "geq no impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_5), - "geq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_5), + # "geq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_5), "geq impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_5), # "softmax" => (p_a=collect(1.0:0.1:2.0), Δp=collect(-0.1:0.02:0.1), Δs_a=jacobian(softmax, p_a)[1] * Δp, model_generator=create_jump_model_6) ) @@ -302,7 +327,7 @@ end =# ################################################ -function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]) +function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]; ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @@ -317,12 +342,16 @@ function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]) @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint @constraint(model, con2, x + y == p[1]) @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end return model, [x; y], [con1; con2; con3], p end -function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]) +function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]; ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @@ -337,11 +366,16 @@ function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]) @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint @constraint(model, con2, x + y == p[1]) @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end + return model, [x; y], [con1; con2; con3], p end -function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]) +function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]; ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @@ -356,11 +390,15 @@ function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]) @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint @constraint(model, con2, x + y == p[1]) @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end return model, [x; y], [con1; con2; con3], p end -function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]) +function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]; ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @@ -376,12 +414,16 @@ function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]) @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint @constraint(model, con2, x + y == p[1]) @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end return model, [x; y], [con1; con2; con3], p end -function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]) +function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]; ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @@ -398,12 +440,16 @@ function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]) @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint @constraint(model, con2, x + y == p[1]) @constraint(model, con3, p[2] * x >= 0.1) - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end return model, [x; y], [con0; con1; con2; con3], p end -function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]) +function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]; ismin = true) model = Model(Ipopt.Optimizer) set_silent(model) @@ -420,7 +466,11 @@ function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]) @constraint(model, con1, x[2] - 0.0001 * x[1]^2 - 0.2 * z^2 - 0.3 * w^2 >= p[1] + 1) @constraint(model, con2, x[1] + 0.001 * x[2]^2 + 0.5 * w^2 + 0.4 * z^2 <= 10 * p[1] + 2) @constraint(model, con3, z^2 + w^2 == 13) - @objective(model, Min, x[2] - x[1] + z - w) + if ismin + @objective(model, Min, x[2] - x[1] + z - w) + else + @objective(model, Max, -x[2] + x[1] - z + w) + end return model, [x; z; w], [con2; con3], p end @@ -516,9 +566,9 @@ DICT_PROBLEMS = Dict( function test_compute_derivatives_Finite_Diff() # @testset "Compute Derivatives: $problem_name" - for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS + for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS, ismin in [true, false] # OPT Problem - model, primal_vars, cons, params = model_generator() + model, primal_vars, cons, params = model_generator(;ismin=ismin) eval_model_jump(model, primal_vars, cons, params, p_a) println("$problem_name: ", model) diff --git a/test_jump.jl b/test_jump.jl index b9095a4c..a4f35e9a 100644 --- a/test_jump.jl +++ b/test_jump.jl @@ -151,7 +151,7 @@ model = JuMP.Model(Ipopt.Optimizer) @objective(model, Max, - x + y - z - w + u) con1 = @constraint(model, x - 1 == 0) - +# s = w - 4 -> s >= 0 λ con1s = @constraint(model, w - 4 >= 0) con2s = @constraint(model, u - 5 <= 0) diff --git a/testing_barrier.jl b/testing_barrier.jl index ad87468d..28288a8e 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -14,7 +14,6 @@ include("nlp_utilities.jl") include("nlp_utilities_test.jl") include("opf.jl") - test_compute_optimal_hess_jacobian() test_compute_derivatives() From 478e7dbc265a6690d58f2375c2f99a1a0040c0a8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 11 Sep 2024 17:30:29 -0400 Subject: [PATCH 074/108] separate tests --- nlp_utilities.jl | 83 +++++----- nlp_utilities_test.jl | 361 ++++-------------------------------------- problem_list.jl | 340 +++++++++++++++++++++++++++++++++++++++ testing_barrier.jl | 12 +- 4 files changed, 425 insertions(+), 371 deletions(-) create mode 100644 problem_list.jl diff --git a/nlp_utilities.jl b/nlp_utilities.jl index b027f022..e4ee1954 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -425,6 +425,18 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # ∂s = - (K \ N) # Sensitivity ldiv!(∂s, K, N) ∂s = - ∂s + + ## Adjust signs based on JuMP convention + sense_multiplier = sense_mult(primal_vars) + num_lower = length(has_low) + num_w = length(_X) + num_cons = length(cons) + # Duals + ∂s[num_w+1:num_w+num_cons, :] *= -sense_multiplier + # Dual bounds lower + ∂s[num_w+num_cons+1:num_w+num_cons+num_lower, :] *= sense_multiplier + # Dual bounds upper + ∂s[num_w+num_cons+num_lower+1:end, :] *= -sense_multiplier return ∂s, K, N end @@ -434,7 +446,7 @@ end Fix the violations and relax complementary slackness. """ -function fix_and_relax(E, K, N, r1, Δp) +function fix_and_relax(E, K, N, r1, Δp, num_w, num_cons, num_lower, sense_multiplier) rs = (N * Δp)[:,:] # C = −E' inv(K) E # C = - E' * (K \ E) @@ -454,7 +466,15 @@ function fix_and_relax(E, K, N, r1, Δp) # ∆s = K \ (- (rs + E * ∆ν¯)) aux = - (rs + E * ∆ν¯)[:,:] ∆s = zeros(size(K, 1), size(aux, 2)) - ldiv!(∆s, K, aux) + ldiv!(∆s, K, aux) + + ## Adjust signs based on JuMP convention + # Duals + ∆s[num_w+1:num_w+num_cons, :] *= -sense_multiplier + # Dual bounds lower + ∆s[num_w+num_cons+1:num_w+num_cons+num_lower, :] *= sense_multiplier + # Dual bounds upper + ∆s[num_w+num_cons+num_lower+1:end, :] *= -sense_multiplier return ∆s end @@ -484,7 +504,7 @@ end Find the bound violations of the primal-dual solution first order approximation based on the (unrelaxed) sensitivity estimation. """ -function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol, ismin) +function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) #, ismin) num_w = length(X) num_low = length(has_low) num_up = length(has_up) @@ -497,19 +517,19 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t push!(_E, i) push!(r1, X[i] - X_L[i]) end - if ismin + # if ismin if sp[num_w+num_cons+j] < -tol println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) push!(_E, num_w+num_cons+j) push!(r1, V_L[i]) end - else - if sp[num_w+num_cons+j] > tol - println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) - push!(_E, num_w+num_cons+j) - push!(r1, V_L[i]) - end - end + # else + # if sp[num_w+num_cons+j] > tol + # println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) + # push!(_E, num_w+num_cons+j) + # push!(r1, V_L[i]) + # end + # end end for (j, i) in enumerate(has_up) if sp[i] > X_U[i] + tol @@ -517,19 +537,19 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t push!(_E, i) push!(r1, X_U[i] - X[i]) end - if ismin + # if ismin if sp[num_w+num_cons+num_low+j] < -tol println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) push!(_E, num_w+num_cons+num_low+j) push!(r1, V_U[i]) end - else - if sp[num_w+num_cons+num_low+j] > tol - println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) - push!(_E, num_w+num_cons+num_low+j) - push!(r1, V_U[i]) - end - end + # else + # if sp[num_w+num_cons+num_low+j] > tol + # println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) + # push!(_E, num_w+num_cons+num_low+j) + # push!(r1, V_U[i]) + # end + # end end E = spzeros(num_w + num_cons + num_low + num_up, length(_E)) @@ -553,32 +573,19 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Compute derivatives # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) + Λ = dual.(cons) if isnothing(Δp) || iszero(Δp) - Λ = - dual.(cons) * sense_multiplier - sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], ∂s * ones(size(∂s, 2))) + sp = approximate_solution(X, Λ, V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), ∂s * ones(size(∂s, 2))) return ∂s, sp end Δs = ∂s * Δp - num_bounds = length(has_up) + length(has_low) - - Λ = - dual.(cons) * sense_multiplier - num_geq = length(geq_locations) - num_leq = length(leq_locations) - # for i in 1:num_leq # slack of leq constraints - # Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] - # end - # Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form - sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) + sp = approximate_solution(X, Λ, V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), Δs) # Linearly appoximated solution - E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol, ismin) + E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) if !isempty(r1) @warn "Relaxation needed" - Δs = fix_and_relax(E, K, N, r1, Δp) - # for i in 1:num_leq # slack of leq constraints - # Δs[num_var+num_geq+i] = - Δs[num_var+num_geq+i] - # end - # Δs[end-num_bounds+1:end] = Δs[end-num_bounds+1:end] .* -1.0 # Correcting the sign of the bounds duals for the standard form - sp = approximate_solution(X, Λ, V_L[has_low], V_U[has_up], Δs) + Δs = fix_and_relax(E, K, N, r1, Δp, length(X), num_cons, length(has_low), sense_multiplier) + sp = approximate_solution(X, Λ, V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), Δs) end return Δs, sp end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index e7863171..df743217 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -5,6 +5,8 @@ using FiniteDiff include("nlp_utilities.jl") +include("problem_list.jl") + ################################################ #= # Test JuMP Hessian and Jacobian @@ -14,24 +16,6 @@ https://github.com/jump-dev/JuMP.jl/blob/301d46e81cb66c74c6e22cd89fb89ced740f157 =# ################################################ -function create_nonlinear_jump_model(;ismin=true) - model = Model(Ipopt.Optimizer) - set_silent(model) - @variable(model, p ∈ MOI.Parameter(1.0)) - @variable(model, p2 ∈ MOI.Parameter(2.0)) - @variable(model, p3 ∈ MOI.Parameter(100.0)) - @variable(model, x[i = 1:2], start = -i) - @constraint(model, g_1, x[1]^2 <= p) - @constraint(model, g_2, p * (x[1] + x[2])^2 <= p2) - if ismin - @objective(model, Min, (1 - x[1])^2 + p3 * (x[2] - x[1]^2)^2) - else - @objective(model, Max, -(1 - x[1])^2 - p3 * (x[2] - x[1]^2)^2) - end - - return model, x, [g_1; g_2], [p; p2; p3] -end - function analytic_hessian(x, σ, μ, p) g_1_H = [2.0 0.0; 0.0 0.0] g_2_H = p[1] * [2.0 2.0; 2.0 2.0] @@ -103,40 +87,24 @@ From sIpopt paper: https://optimization-online.org/2011/04/3008/ =# ################################################ -function create_nonlinear_jump_model_sipopt(;ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - @variable(model, p1 ∈ MOI.Parameter(4.5)) - @variable(model, p2 ∈ MOI.Parameter(1.0)) - @variable(model, x[i = 1:3] >= 0, start = -i) - @constraint(model, g_1, 6 * x[1] + 3 * x[2] + 2 * x[3] - p1 == 0) - @constraint(model, g_2, p2 * x[1] + x[2] - x[3] - 1 == 0) - if ismin - @objective(model, Min, x[1]^2 + x[2]^2 + x[3]^2) - else - @objective(model, Max, -x[1]^2 - x[2]^2 - x[3]^2) - end - return model, x, [g_1; g_2], [p1; p2] -end - function test_compute_derivatives() @testset "Compute Derivatives No Inequalities: ismin=$ismin" for ismin in [true, false] # Model - sign_fix = ismin ? 1.0 : -1.0 + # sign_fix = ismin ? 1.0 : -1.0 model, primal_vars, cons, params = create_nonlinear_jump_model_sipopt(;ismin=ismin) optimize!(model) @assert is_solved_and_feasible(model) # Analytical solutions case b pb = [4.5, 1.0] - s_pb = [0.5, 0.5, 0.0, 0.0, - sign_fix * 1.0, 0.0, 0.0, sign_fix * 1.0] - @assert all(isapprox.([value.(primal_vars); - sign_fix * dual.(cons); sign_fix * dual.(LowerBoundRef.(primal_vars))], s_pb; atol = 1e-6)) + s_pb = [0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] + @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pb; atol = 1e-6)) # Analytical solutions case a pa = [5.0, 1.0] - s_pa = [0.6327, 0.3878, 0.0204, - sign_fix * 0.1633, - sign_fix * 0.2857, 0, 0, 0] + s_pa = [0.6327, 0.3878, 0.0204, 0.1633, 0.2857, 0, 0, 0] set_parameter_value.(params, pa) optimize!(model) @assert is_solved_and_feasible(model) - @assert all(isapprox.([value.(primal_vars); - sign_fix * dual.(cons); sign_fix * dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) + @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) # Compute derivatives without accounting for active set changes evaluator, rows = create_evaluator(model; x=[primal_vars; params]) X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) @@ -144,11 +112,11 @@ function test_compute_derivatives() # Check linear approx s_pb Δp = pb - pa s_pb_approx_violated = s_pa + ∂s * Δp - @test all(isapprox.([0.5765; 0.3775; -0.0459; - sign_fix * 0.1327; - sign_fix * 0.3571; 0.0; 0.0; 0.0], s_pb_approx_violated; atol = 1e-4)) + @test all(isapprox.([0.5765; 0.3775; -0.0459; 0.1327; 0.3571; 0.0; 0.0; 0.0], s_pb_approx_violated; atol = 1e-4)) # Account for active set changes Δs, s_pb_approx = compute_sensitivity(evaluator, rows, Δp; primal_vars, params) # s_pb_approx = s_pa .+ Δs - @test all(isapprox.([0.5, 0.5, 0.0, 0.0, - sign_fix * 1.0, 0.0, 0.0, 0.0], s_pb_approx; atol = 1e-6)) + @test all(isapprox.([0.5, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], s_pb_approx; atol = 1e-6)) end end @@ -158,159 +126,34 @@ end =# ################################################ -function create_jump_model_1(p_val = [1.5]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p ∈ MOI.Parameter(p_val[1])) - - # Variables - @variable(model, x) - - # Constraints - @constraint(model, con1, x >= p) - @constraint(model, con2, x >= 2) - @objective(model, Min, x^2) - - return model, [x], [con1; con2], [p] -end - -function create_jump_model_2(p_val = [1.5]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p ∈ MOI.Parameter(p_val[1])) - - # Variables - @variable(model, x >= 2.0) - - # Constraints - @constraint(model, con1, x >= p) - @objective(model, Min, x^2) - - return model, [x], [con1], [p] -end - -function create_jump_model_3(p_val = [-1.5]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p ∈ MOI.Parameter(p_val[1])) - - # Variables - @variable(model, x) - - # Constraints - @constraint(model, con1, x <= p) - @constraint(model, con2, x <= -2) - @objective(model, Min, -x) - - return model, [x], [con1; con2], [p] -end - -function create_jump_model_4(p_val = [1.5]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p ∈ MOI.Parameter(p_val[1])) - - # Variables - @variable(model, x) - - # Constraints - @constraint(model, con1, x <= p) - @constraint(model, con2, x <= 2) - @objective(model, Max, x) - - return model, [x], [con1; con2], [p] -end - -function create_jump_model_5(p_val = [1.5]) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p ∈ MOI.Parameter(p_val[1])) - - # Variables - @variable(model, x) - - # Constraints - @constraint(model, con1, x >= p) - @constraint(model, con2, x >= 2) - @objective(model, Max, -x) - - return model, [x], [con1; con2], [p] -end - -# Softmax model -h(y) = - sum(y .* log.(y)) -softmax(x) = exp.(x) / sum(exp.(x)) -function create_jump_model_6(p_a = collect(1.0:0.1:2.0)) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, x[i=1:length(p_a)] ∈ MOI.Parameter.(p_a)) - - # Variables - @variable(model, y[1:length(p_a)] >= 0.0) - - # Constraints - @constraint(model, con1, sum(y) == 1) - @constraint(model, con2[i=1:length(x)], y[i] <= 1) - - # Objective - @objective(model, Max, dot(x, y) + h(y)) - - return model, y, [con1; con2], x -end # f(x, p) = 0 # x = g(p) # ∂x/∂p = ∂g/∂p -function create_jump_model_7(p_val = [1.5], g = sin) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p ∈ MOI.Parameter(p_val[1])) - - # Variables - @variable(model, x) - - # Constraints - @constraint(model, con1, x * g(p) == 1) - @objective(model, Min, 0) - - return model, [x], [con1], [p] -end - -DICT_PROBLEMS_Analytical = Dict( +DICT_PROBLEMS_Analytical_no_cc = Dict( "geq no impact" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), - # "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_1), "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; -0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), - # "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1], model_generator=create_jump_model_2), "geq bound impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; 0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), - # "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; 0.1; -0.1], model_generator=create_jump_model_3), "leq impact" => (p_a=[-2.1], Δp=[-0.2], Δs_a=[-0.2; 0.0; -0.2], model_generator=create_jump_model_3), "leq no impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_4), - # "leq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_4), "leq impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_4), "geq no impact max" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0], model_generator=create_jump_model_5), - # "geq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_5), "geq impact max" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2], model_generator=create_jump_model_5), # "softmax" => (p_a=collect(1.0:0.1:2.0), Δp=collect(-0.1:0.02:0.1), Δs_a=jacobian(softmax, p_a)[1] * Δp, model_generator=create_jump_model_6) ) -function test_compute_derivatives_Analytical() - @testset "Compute Derivatives Analytical: $problem_name" for (problem_name, (p_a, Δp, Δs_a, model_generator)) in DICT_PROBLEMS_Analytical +DICT_PROBLEMS_Analytical_cc = Dict( + "geq active constraint change" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_1), + "geq active bound change" => (p_a=[2.1], Δp=[-0.2], Δs_a=[-0.1; 0.1], model_generator=create_jump_model_2), + "leq active constraint change" => (p_a=[-1.9], Δp=[-0.2], Δs_a=[-0.1; 0.1; -0.1], model_generator=create_jump_model_3), + "leq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_4), + "geq active constraint change max" => (p_a=[1.9], Δp=[0.2], Δs_a=[0.1; -0.1; 0.1], model_generator=create_jump_model_5), +) + +function test_compute_derivatives_Analytical(DICT_PROBLEMS) + @testset "Compute Derivatives Analytical: $problem_name" for (problem_name, (p_a, Δp, Δs_a, model_generator)) in DICT_PROBLEMS # OPT Problem model, primal_vars, cons, params = model_generator() eval_model_jump(model, primal_vars, cons, params, p_a) @@ -327,154 +170,6 @@ end =# ################################################ -function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]; ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x) - @variable(model, y) - - # Constraints - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - if ismin - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective - else - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective - end - - return model, [x; y], [con1; con2; con3], p -end - -function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]; ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x <= 10) - @variable(model, y) - - # Constraints - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - if ismin - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective - else - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective - end - - return model, [x; y], [con1; con2; con3], p -end - -function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]; ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x <= 10) - @variable(model, y) - - # Constraints - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - if ismin - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective - else - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective - end - return model, [x; y], [con1; con2; con3], p -end - -function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]; ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x) - @variable(model, y) - - # Constraints - @constraint(model, con0, x == p[1] - 0.5) - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - if ismin - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective - else - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective - end - - return model, [x; y], [con1; con2; con3], p -end - -function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]; ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x) - @variable(model, y) - - # Constraints - fix(x, 0.5) - con0 = JuMP.FixRef(x) - @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint - @constraint(model, con2, x + y == p[1]) - @constraint(model, con3, p[2] * x >= 0.1) - if ismin - @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective - else - @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective - end - - return model, [x; y], [con0; con1; con2; con3], p -end - -function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]; ismin = true) - model = Model(Ipopt.Optimizer) - set_silent(model) - - # Parameters - @variable(model, p[i=1:2] ∈ MOI.Parameter.(p_val)) - - # Variables - @variable(model, x[i=1:2]) - @variable(model, z) # >= 2.0) - @variable(model, w) # <= 3.0) - # @variable(model, f[1:2]) - - # Constraints - @constraint(model, con1, x[2] - 0.0001 * x[1]^2 - 0.2 * z^2 - 0.3 * w^2 >= p[1] + 1) - @constraint(model, con2, x[1] + 0.001 * x[2]^2 + 0.5 * w^2 + 0.4 * z^2 <= 10 * p[1] + 2) - @constraint(model, con3, z^2 + w^2 == 13) - if ismin - @objective(model, Min, x[2] - x[1] + z - w) - else - @objective(model, Max, -x[2] + x[1] - z + w) - end - - return model, [x; z; w], [con2; con3], p -end - function eval_model_jump(model, primal_vars, cons, params, p_val) set_parameter_value.(params, p_val) optimize!(model) @@ -542,10 +237,8 @@ function print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, leq_locations, ge end end -DICT_PROBLEMS = Dict( - "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[-0.5; 0.5; 0.1], model_generator=create_nonlinear_jump_model), +DICT_PROBLEMS_no_cc = Dict( "QP_sIpopt" => (p_a=[4.5; 1.0], Δp=[0.001; 0.0], model_generator=create_nonlinear_jump_model_sipopt), - "QP_sIpopt" => (p_a=[5.0; 1.0], Δp=[-0.5; 0.0], model_generator=create_nonlinear_jump_model_sipopt), "NLP_1" => (p_a=[3.0; 2.0; 200], Δp=[0.001; 0.0; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_2" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.001; 0.0], model_generator=create_nonlinear_jump_model_1), "NLP_1_3" => (p_a=[3.0; 2.0; 200], Δp=[0.0; 0.0; 0.001], model_generator=create_nonlinear_jump_model_1), @@ -564,7 +257,13 @@ DICT_PROBLEMS = Dict( "NLP_6" => (p_a=[100.0; 200.0], Δp=[0.2; 0.5], model_generator=create_nonlinear_jump_model_6), ) -function test_compute_derivatives_Finite_Diff() + +DICT_PROBLEMS_cc = Dict( + "QP_JuMP" => (p_a=[1.0; 2.0; 100.0], Δp=[-0.5; 0.5; 0.1], model_generator=create_nonlinear_jump_model), + "QP_sIpopt2" => (p_a=[5.0; 1.0], Δp=[-0.5; 0.0], model_generator=create_nonlinear_jump_model_sipopt), +) + +function test_compute_derivatives_Finite_Diff(DICT_PROBLEMS, iscc=false) # @testset "Compute Derivatives: $problem_name" for (problem_name, (p_a, Δp, model_generator)) in DICT_PROBLEMS, ismin in [true, false] # OPT Problem @@ -582,9 +281,9 @@ function test_compute_derivatives_Finite_Diff() Δs_fd = ∂s_fd * Δp # actual solution sp = stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) - num_important = length(primal_vars) # Check sensitivities - if all(isapprox.(Δs, Δs_fd; rtol = 1e-3, atol=14-6)) || all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; rtol = 1e-3, atol=14-6)) + num_important = length(primal_vars) + length(cons) + if all(isapprox.(Δs, Δs_fd; rtol = 1e-5, atol=1e-6)) || (iscc && all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; rtol = 1e-5, atol=1e-6))) println("All sensitivities are correct") else @show Δp diff --git a/problem_list.jl b/problem_list.jl new file mode 100644 index 00000000..ef551a32 --- /dev/null +++ b/problem_list.jl @@ -0,0 +1,340 @@ +using JuMP +using Ipopt + +################################################ +#= +From JuMP Tutorial for Querying Hessians: +https://github.com/jump-dev/JuMP.jl/blob/301d46e81cb66c74c6e22cd89fb89ced740f157b/docs/src/tutorials/nonlinear/querying_hessians.jl#L67-L72 +=# +################################################ +function create_nonlinear_jump_model(;ismin=true) + model = Model(Ipopt.Optimizer) + set_silent(model) + @variable(model, p ∈ MOI.Parameter(1.0)) + @variable(model, p2 ∈ MOI.Parameter(2.0)) + @variable(model, p3 ∈ MOI.Parameter(100.0)) + @variable(model, x[i = 1:2], start = -i) + @constraint(model, g_1, x[1]^2 <= p) + @constraint(model, g_2, p * (x[1] + x[2])^2 <= p2) + if ismin + @objective(model, Min, (1 - x[1])^2 + p3 * (x[2] - x[1]^2)^2) + else + @objective(model, Max, -(1 - x[1])^2 - p3 * (x[2] - x[1]^2)^2) + end + + return model, x, [g_1; g_2], [p; p2; p3] +end + + +################################################ +#= +From sIpopt paper: https://optimization-online.org/2011/04/3008/ +=# +################################################ + +function create_nonlinear_jump_model_sipopt(;ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + @variable(model, p1 ∈ MOI.Parameter(4.5)) + @variable(model, p2 ∈ MOI.Parameter(1.0)) + @variable(model, x[i = 1:3] >= 0, start = -i) + @constraint(model, g_1, 6 * x[1] + 3 * x[2] + 2 * x[3] - p1 == 0) + @constraint(model, g_2, p2 * x[1] + x[2] - x[3] - 1 == 0) + if ismin + @objective(model, Min, x[1]^2 + x[2]^2 + x[3]^2) + else + @objective(model, Max, -x[1]^2 - x[2]^2 - x[3]^2) + end + return model, x, [g_1; g_2], [p1; p2] +end + +################################################ +#= +Simple Problems +=# +################################################ + + +function create_jump_model_1(p_val = [1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x >= p) + @constraint(model, con2, x >= 2) + @objective(model, Min, x^2) + + return model, [x], [con1; con2], [p] +end + +function create_jump_model_2(p_val = [1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x >= 2.0) + + # Constraints + @constraint(model, con1, x >= p) + @objective(model, Min, x^2) + + return model, [x], [con1], [p] +end + +function create_jump_model_3(p_val = [-1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x <= p) + @constraint(model, con2, x <= -2) + @objective(model, Min, -x) + + return model, [x], [con1; con2], [p] +end + +function create_jump_model_4(p_val = [1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x <= p) + @constraint(model, con2, x <= 2) + @objective(model, Max, x) + + return model, [x], [con1; con2], [p] +end + +function create_jump_model_5(p_val = [1.5]) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x >= p) + @constraint(model, con2, x >= 2) + @objective(model, Max, -x) + + return model, [x], [con1; con2], [p] +end + +# Softmax model +h(y) = - sum(y .* log.(y)) +softmax(x) = exp.(x) / sum(exp.(x)) +function create_jump_model_6(p_a = collect(1.0:0.1:2.0)) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, x[i=1:length(p_a)] ∈ MOI.Parameter.(p_a)) + + # Variables + @variable(model, y[1:length(p_a)] >= 0.0) + + # Constraints + @constraint(model, con1, sum(y) == 1) + @constraint(model, con2[i=1:length(x)], y[i] <= 1) + + # Objective + @objective(model, Max, dot(x, y) + h(y)) + + return model, y, [con1; con2], x +end + +function create_jump_model_7(p_val = [1.5], g = sin) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p ∈ MOI.Parameter(p_val[1])) + + # Variables + @variable(model, x) + + # Constraints + @constraint(model, con1, x * g(p) == 1) + @objective(model, Min, 0) + + return model, [x], [con1], [p] +end + +################################################ +#= +Non Linear Problems +=# +################################################ + + +function create_nonlinear_jump_model_1(p_val = [1.0; 2.0; 100]; ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end + + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_2(p_val = [3.0; 2.0; 10]; ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x <= 10) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end + + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_3(p_val = [3.0; 2.0; 10]; ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x <= 10) + @variable(model, y) + + # Constraints + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_4(p_val = [1.0; 2.0; 100]; ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x) + @variable(model, y) + + # Constraints + @constraint(model, con0, x == p[1] - 0.5) + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end + + return model, [x; y], [con1; con2; con3], p +end + +function create_nonlinear_jump_model_5(p_val = [1.0; 2.0; 100]; ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:3] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x) + @variable(model, y) + + # Constraints + fix(x, 0.5) + con0 = JuMP.FixRef(x) + @constraint(model, con1, y >= p[1]*sin(x)) # NLP Constraint + @constraint(model, con2, x + y == p[1]) + @constraint(model, con3, p[2] * x >= 0.1) + if ismin + @objective(model, Min, (1 - x)^2 + p[3] * (y - x^2)^2) # NLP Objective + else + @objective(model, Max, -(1 - x)^2 - p[3] * (y - x^2)^2) # NLP Objective + end + + return model, [x; y], [con0; con1; con2; con3], p +end + +function create_nonlinear_jump_model_6(p_val = [100.0; 200.0]; ismin = true) + model = Model(Ipopt.Optimizer) + set_silent(model) + + # Parameters + @variable(model, p[i=1:2] ∈ MOI.Parameter.(p_val)) + + # Variables + @variable(model, x[i=1:2]) + @variable(model, z) # >= 2.0) + @variable(model, w) # <= 3.0) + # @variable(model, f[1:2]) + + # Constraints + @constraint(model, con1, x[2] - 0.0001 * x[1]^2 - 0.2 * z^2 - 0.3 * w^2 >= p[1] + 1) + @constraint(model, con2, x[1] + 0.001 * x[2]^2 + 0.5 * w^2 + 0.4 * z^2 <= 10 * p[1] + 2) + @constraint(model, con3, z^2 + w^2 == 13) + if ismin + @objective(model, Min, x[2] - x[1] + z - w) + else + @objective(model, Max, -x[2] + x[1] - z + w) + end + + return model, [x; z; w], [con2; con3], p +end diff --git a/testing_barrier.jl b/testing_barrier.jl index 28288a8e..1cbf639b 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -14,13 +14,21 @@ include("nlp_utilities.jl") include("nlp_utilities_test.jl") include("opf.jl") +# No Fix + test_compute_optimal_hess_jacobian() +test_compute_derivatives_Finite_Diff(DICT_PROBLEMS_no_cc, false) + +test_compute_derivatives_Analytical(DICT_PROBLEMS_Analytical_no_cc) + +# Fix and Relax + test_compute_derivatives() -test_compute_derivatives_Finite_Diff() +test_compute_derivatives_Finite_Diff(DICT_PROBLEMS_cc, true) -test_compute_derivatives_Analytical() +test_compute_derivatives_Analytical(DICT_PROBLEMS_Analytical_cc) ################################################ # Strategic bidding test From 4eebfdab4e6dddb4342c56bd7044f7a4fe18a104 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 11 Sep 2024 18:22:11 -0400 Subject: [PATCH 075/108] update code --- nlp_utilities.jl | 83 ++++++++++++++++++++----------------------- nlp_utilities_test.jl | 5 ++- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index e4ee1954..12176f55 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -425,18 +425,6 @@ function compute_derivatives_no_relax(evaluator::MOI.Nonlinear.Evaluator, cons:: # ∂s = - (K \ N) # Sensitivity ldiv!(∂s, K, N) ∂s = - ∂s - - ## Adjust signs based on JuMP convention - sense_multiplier = sense_mult(primal_vars) - num_lower = length(has_low) - num_w = length(_X) - num_cons = length(cons) - # Duals - ∂s[num_w+1:num_w+num_cons, :] *= -sense_multiplier - # Dual bounds lower - ∂s[num_w+num_cons+1:num_w+num_cons+num_lower, :] *= sense_multiplier - # Dual bounds upper - ∂s[num_w+num_cons+num_lower+1:end, :] *= -sense_multiplier return ∂s, K, N end @@ -446,7 +434,7 @@ end Fix the violations and relax complementary slackness. """ -function fix_and_relax(E, K, N, r1, Δp, num_w, num_cons, num_lower, sense_multiplier) +function fix_and_relax(E, K, N, r1, Δp) rs = (N * Δp)[:,:] # C = −E' inv(K) E # C = - E' * (K \ E) @@ -468,14 +456,6 @@ function fix_and_relax(E, K, N, r1, Δp, num_w, num_cons, num_lower, sense_multi ∆s = zeros(size(K, 1), size(aux, 2)) ldiv!(∆s, K, aux) - ## Adjust signs based on JuMP convention - # Duals - ∆s[num_w+1:num_w+num_cons, :] *= -sense_multiplier - # Dual bounds lower - ∆s[num_w+num_cons+1:num_w+num_cons+num_lower, :] *= sense_multiplier - # Dual bounds upper - ∆s[num_w+num_cons+num_lower+1:end, :] *= -sense_multiplier - return ∆s end @@ -504,7 +484,7 @@ end Find the bound violations of the primal-dual solution first order approximation based on the (unrelaxed) sensitivity estimation. """ -function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) #, ismin) +function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol, ismin) num_w = length(X) num_low = length(has_low) num_up = length(has_up) @@ -517,19 +497,19 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t push!(_E, i) push!(r1, X[i] - X_L[i]) end - # if ismin + if ismin if sp[num_w+num_cons+j] < -tol println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) push!(_E, num_w+num_cons+j) push!(r1, V_L[i]) end - # else - # if sp[num_w+num_cons+j] > tol - # println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) - # push!(_E, num_w+num_cons+j) - # push!(r1, V_L[i]) - # end - # end + else + if sp[num_w+num_cons+j] > tol + println("Violation LB Dual: ", i, " ", sp[num_w+num_cons+j], " ", V_L[i]) + push!(_E, num_w+num_cons+j) + push!(r1, V_L[i]) + end + end end for (j, i) in enumerate(has_up) if sp[i] > X_U[i] + tol @@ -537,19 +517,19 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t push!(_E, i) push!(r1, X_U[i] - X[i]) end - # if ismin + if ismin if sp[num_w+num_cons+num_low+j] < -tol println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) push!(_E, num_w+num_cons+num_low+j) push!(r1, V_U[i]) end - # else - # if sp[num_w+num_cons+num_low+j] > tol - # println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) - # push!(_E, num_w+num_cons+num_low+j) - # push!(r1, V_U[i]) - # end - # end + else + if sp[num_w+num_cons+num_low+j] > tol + println("Violation UB Dual: ", i, " ", sp[num_w+num_cons+num_low+j], " ", V_U[i]) + push!(_E, num_w+num_cons+num_low+j) + push!(r1, V_U[i]) + end + end end E = spzeros(num_w + num_cons + num_low + num_up, length(_E)) @@ -571,22 +551,37 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co # Solution and bounds X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, cons) # Compute derivatives + num_w = length(X) + num_lower = length(has_low) # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) - Λ = dual.(cons) if isnothing(Δp) || iszero(Δp) - sp = approximate_solution(X, Λ, V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), ∂s * ones(size(∂s, 2))) + ## Adjust signs based on JuMP convention + # Duals + ∂s[num_w+1:num_w+num_cons, :] *= -sense_multiplier + # Dual bounds lower + ∂s[num_w+num_cons+1:num_w+num_cons+num_lower, :] *= sense_multiplier + # Dual bounds upper + ∂s[num_w+num_cons+num_lower+1:end, :] *= -sense_multiplier + sp = approximate_solution(X, dual.(cons), V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), ∂s * ones(size(∂s, 2))) return ∂s, sp end Δs = ∂s * Δp - sp = approximate_solution(X, Λ, V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), Δs) + sp = approximate_solution(X, - sense_multiplier * dual.(cons), V_L[has_low], V_U[has_up], Δs) # Linearly appoximated solution - E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol) + E, r1 = find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, tol, ismin) if !isempty(r1) @warn "Relaxation needed" - Δs = fix_and_relax(E, K, N, r1, Δp, length(X), num_cons, length(has_low), sense_multiplier) - sp = approximate_solution(X, Λ, V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), Δs) + Δs = fix_and_relax(E, K, N, r1, Δp) end + ## Adjust signs based on JuMP convention + # Duals + Δs[num_w+1:num_w+num_cons, :] *= -sense_multiplier + # Dual bounds lower + Δs[num_w+num_cons+1:num_w+num_cons+num_lower, :] *= sense_multiplier + # Dual bounds upper + Δs[num_w+num_cons+num_lower+1:end, :] *= -sense_multiplier + sp = approximate_solution(X, dual.(cons), V_L[has_low] .* (sense_multiplier), V_U[has_up].* (-sense_multiplier), Δs) return Δs, sp end diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index df743217..e9df2416 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -107,8 +107,7 @@ function test_compute_derivatives() @assert all(isapprox.([value.(primal_vars); dual.(cons); dual.(LowerBoundRef.(primal_vars))], s_pa; atol = 1e-4)) # Compute derivatives without accounting for active set changes evaluator, rows = create_evaluator(model; x=[primal_vars; params]) - X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low = compute_solution_and_bounds(primal_vars, rows) - ∂s, K, N = compute_derivatives_no_relax(evaluator, rows, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) + ∂s, s_pb_approx = compute_sensitivity(evaluator, rows, nothing; primal_vars, params) # Check linear approx s_pb Δp = pb - pa s_pb_approx_violated = s_pa + ∂s * Δp @@ -133,7 +132,7 @@ end DICT_PROBLEMS_Analytical_no_cc = Dict( "geq no impact" => (p_a=[1.5], Δp=[0.2], Δs_a=[0.0; -0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_1), - "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; -0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), + "geq impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.2; 0.4; 0.0; 0.4; 0.0], model_generator=create_jump_model_1), "geq bound impact" => (p_a=[2.1], Δp=[0.2], Δs_a=[0.2; 0.0; 0.4; 0.0; 0.4], model_generator=create_jump_model_2), "leq no impact" => (p_a=[-1.5], Δp=[-0.2], Δs_a=[0.0; 0.2; 0.0; 0.0; 0.0; 0.0; 0.0], model_generator=create_jump_model_3), "leq impact" => (p_a=[-2.1], Δp=[-0.2], Δs_a=[-0.2; 0.0; -0.2], model_generator=create_jump_model_3), From 0c0f63bde8996af3f8f0f8106b161665d77a16bd Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 12 Sep 2024 10:09:28 -0400 Subject: [PATCH 076/108] update debug --- nlp_utilities_test.jl | 14 +++++++++----- testing_barrier.jl | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/nlp_utilities_test.jl b/nlp_utilities_test.jl index e9df2416..a76426d5 100644 --- a/nlp_utilities_test.jl +++ b/nlp_utilities_test.jl @@ -268,13 +268,13 @@ function test_compute_derivatives_Finite_Diff(DICT_PROBLEMS, iscc=false) # OPT Problem model, primal_vars, cons, params = model_generator(;ismin=ismin) eval_model_jump(model, primal_vars, cons, params, p_a) - println("$problem_name: ", model) # Compute derivatives # Δp = [0.001; 0.0; 0.0] p_b = p_a .+ Δp (Δs, sp_approx), evaluator, cons = compute_sensitivity(model, Δp; primal_vars, params) leq_locations, geq_locations = find_inequealities(cons) + sa = stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p_a)...) # Check derivatives using finite differences ∂s_fd = FiniteDiff.finite_difference_jacobian((p) -> stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p)...), p_a) Δs_fd = ∂s_fd * Δp @@ -282,13 +282,17 @@ function test_compute_derivatives_Finite_Diff(DICT_PROBLEMS, iscc=false) sp = stack_solution(cons, leq_locations, geq_locations, eval_model_jump(model, primal_vars, cons, params, p_b)...) # Check sensitivities num_important = length(primal_vars) + length(cons) - if all(isapprox.(Δs, Δs_fd; rtol = 1e-5, atol=1e-6)) || (iscc && all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; rtol = 1e-5, atol=1e-6))) + test_derivatives = all(isapprox.(Δs, Δs_fd; rtol = 1e-5, atol=1e-6)) + test_approx = all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; rtol = 1e-5, atol=1e-6)) + if test_derivatives || (iscc && test_approx) println("All sensitivities are correct") + elseif iscc && !test_approx + @show Δp + println("Fail Approximations") + print_wrong_sensitive(Δs, sp.-sa, primal_vars, cons, leq_locations, geq_locations) else - @show Δp + @show Δp print_wrong_sensitive(Δs, Δs_fd, primal_vars, cons, leq_locations, geq_locations) - # Check solution - println(all(isapprox.(sp[1:num_important], sp_approx[1:num_important]; atol = 1e-6))) end println("--------------------") end diff --git a/testing_barrier.jl b/testing_barrier.jl index 1cbf639b..75d66d68 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -38,9 +38,9 @@ test_compute_derivatives_Analytical(DICT_PROBLEMS_Analytical_cc) # Random.seed!(1234) # @time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) -# casename = "pglib_opf_case300_ieee" -# Random.seed!(1234) -# @time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1) +casename = "pglib_opf_case300_ieee" +Random.seed!(1234) +@time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1, Δp=nothing) # Δp=0.0 (no derivative): time=4.61s | obj= $474.18 # Δp=nothing (no restoration): time=452.65s | obj= $79886.16 # Δp=0.001 (with derivative): time=54.64s | obj= $474.18 From a043847edb87771d5dc6abc04debfeef325668a5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 12 Sep 2024 11:13:41 -0400 Subject: [PATCH 077/108] fix bug --- nlp_utilities.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 12176f55..16168539 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -515,7 +515,7 @@ function find_violations(X, sp, X_L, X_U, V_U, V_L, has_up, has_low, num_cons, t if sp[i] > X_U[i] + tol println("Violation UB Var: ", i, " ", sp[i], " ", X_U[i]) push!(_E, i) - push!(r1, X_U[i] - X[i]) + push!(r1, X[i] - X_U[i]) end if ismin if sp[num_w+num_cons+num_low+j] < -tol From a9c3c8d53ebcb7e58028793c97a3a41871b8874e Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 24 Sep 2024 17:33:35 -0400 Subject: [PATCH 078/108] update bidding --- opf.jl | 99 +++++++++++++++++++++++++++++++++++++++----- strategic_bidding.jl | 56 +++++++++++++++++++++++++ test_jump.jl | 3 ++ testing_barrier.jl | 15 ++++--- 4 files changed, 156 insertions(+), 17 deletions(-) create mode 100644 strategic_bidding.jl diff --git a/opf.jl b/opf.jl index 09a7ad3d..b61da848 100644 --- a/opf.jl +++ b/opf.jl @@ -192,8 +192,8 @@ function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipo ) end -function build_bidding_upper(num_bidding_nodes, pmax) - upper_model = Model(Ipopt.Optimizer) +function build_bidding_upper(num_bidding_nodes, pmax; solver=Ipopt.Optimizer) + upper_model = Model(solver) @variable(upper_model, 0 <= pg_bid[i=1:num_bidding_nodes] <= pmax) @variable(upper_model, pg[i=1:num_bidding_nodes]) @@ -246,14 +246,14 @@ function fdiff_derivatives(f::Function) return ∇f, ∇²f end -function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001) +function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=Ipopt.Optimizer, solver_lower=Ipopt.Optimizer) # test derivative of the dual of the demand equilibrium constraint - data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes) + data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) pmax = data["pmax"] primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) set_parameter_value.(data["bid"], 0.01) - optimize!(data["model"]) + JuMP.optimize!(data["model"]) evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) leq_locations, geq_locations = find_inequealities(cons) num_ineq = length(leq_locations) + length(geq_locations) @@ -266,17 +266,17 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe # Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) # set_parameter_value.(data["bid"], 0.5 .+ Δp) - # optimize!(data["model"]) + # JuMP.optimize!(data["model"]) # @test dual.(data["bidding_lmps"]) ≈ Δs[(num_primal + num_ineq) .+ bidding_lmps_index] # test bilevel strategic bidding - upper_model, lambda, pg_bid, pg = build_bidding_upper(num_bidding_nodes, pmax) + upper_model, lambda, pg_bid, pg = build_bidding_upper(num_bidding_nodes, pmax; solver=solver_upper) evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) function f(pg_bid_val...) set_parameter_value.(data["bid"], pg_bid_val) - optimize!(data["model"]) + JuMP.optimize!(data["model"]) if !is_solved_and_feasible(data["model"]) return nothing end @@ -286,7 +286,7 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe function ∇f(g::AbstractVector{T}, pg_bid_val...) where {T} if value.(data["bid"]) == pg_bid_val set_parameter_value.(data["bid"], pg_bid_val) - optimize!(data["model"]) + JuMP.optimize!(data["model"]) @assert is_solved_and_feasible(data["model"]) end @@ -319,7 +319,7 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe @constraint(upper_model, lambda[i] == op_lambda(pg_bid...)) end - optimize!(upper_model) + JuMP.optimize!(upper_model) println("Status: ", termination_status(upper_model)) println("Objective: ", objective_value(upper_model)) @@ -328,6 +328,83 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe println("Dispatch: ", value.(pg)) end +function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=:LD_MMA, solver_lower=Ipopt.Optimizer, max_eval=100) + data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) + pmax = data["pmax"] + primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] + num_bidding_nodes = length(data["bidding_generators_dispatch"]) + set_parameter_value.(data["bid"], 0.01) + JuMP.optimize!(data["model"]) + evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) + leq_locations, geq_locations = find_inequealities(cons) + num_ineq = length(leq_locations) + length(geq_locations) + num_primal = length(primal_vars) + bidding_lmps_index = [findall(x -> x == i, cons)[1] for i in data["bidding_lmps"]] + if !isnothing(Δp) + Δp = fill(Δp, num_bidding_nodes) + end + + evaluator, cons = create_evaluator(data["model"]; x=[primal_vars; data["bid"]]) + + function f(pg_bid_val...) + set_parameter_value.(data["bid"], pg_bid_val) + JuMP.optimize!(data["model"]) + if !is_solved_and_feasible(data["model"]) + return nothing + end + return [value.(data["bidding_generators_dispatch"]); -dual.(data["bidding_lmps"])] + end + + function ∇f(pg_bid_val...) where {T} + if value.(data["bid"]) == pg_bid_val + set_parameter_value.(data["bid"], pg_bid_val) + JuMP.optimize!(data["model"]) + @assert is_solved_and_feasible(data["model"]) + end + + Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) + if isnothing(Δp) + Δs = Δs * ones(size(Δs, 2)) + end + # for i in 1:num_bidding_nodes + # g[i] = Δs[i] + # end + # for (i, b_idx) in enumerate(bidding_lmps_index) + # g[i] = -Δs[num_primal + num_ineq + b_idx] + # end + # return g + return [Δs[1:num_bidding_nodes]; -(Δs[(num_primal + num_ineq) .+ bidding_lmps_index])] + end + + trace = Any[] + function my_objective_fn(pg_bid_val::Vector, grad::Vector) + pg, lmps = f(pg_bid_val...) + Δpg, Δlmps = ∇f(pg_bid_val...) + value = dot(lmps, pg) + if length(grad) > 0 + grad .= dot(Δlmps, pg) .+ dot(lmps, Δpg) + end + + push!(trace, copy(pg_bid_val) => value) + return value + end + + opt = NLopt.Opt(solver_upper, num_bidding_nodes) + NLopt.lower_bounds!(opt, fill(0.0, num_bidding_nodes)) + NLopt.upper_bounds!(opt, fill(pmax, num_bidding_nodes)) + NLopt.xtol_rel!(opt, 1e-4) + maxeval!(opt, max_eval) + NLopt.max_objective!(opt, my_objective_fn) + max_f, opt_x, ret = NLopt.optimize(opt, fill(0.001, num_bidding_nodes)) + num_evals = NLopt.numevals(opt) + println("Status: ", ret) + println("Objective: ", max_f) + println("Bids: ", opt_x[1:num_bidding_nodes]) + println("Duals: ", opt_x[num_bidding_nodes+1:end]) + println("Number of evaluations: ", num_evals) + return max_f, num_evals, trace +end + function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver=Ipopt.Optimizer) data = make_basic_network(pglib(case_name)) @@ -335,7 +412,7 @@ function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver= num_bus = length(ref[:bus]) demand_equilibrium = [demand_equilibrium[i] for i in 1:num_bus] - optimize!(model) + JuMP.optimize!(model) params = vcat(pload.data, qload.data) all_primal_variables = all_variables(model) diff --git a/strategic_bidding.jl b/strategic_bidding.jl new file mode 100644 index 00000000..2468992e --- /dev/null +++ b/strategic_bidding.jl @@ -0,0 +1,56 @@ +using JuMP +using SparseArrays +using LinearAlgebra +using Ipopt +using Random + +include("nlp_utilities.jl") +include("nlp_utilities_test.jl") +include("opf.jl") + +################################################ +# Strategic bidding test +################################################ + +using JuMP, NLopt +using DataFrames, CSV + +solver_upper = :LN_BOBYQA # :LD_MMA +max_eval = 100 +solver_lower = Ipopt.Optimizer +casename = "pglib_opf_case300_ieee" +Random.seed!(1234) + +start_time = time() +profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=0.0, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) +end_time = time() + +solvers_upper = [:LD_MMA, :LN_BOBYQA] +Δps = [0.0, 0.001, nothing] +seeds = [1234, 1235, 1236] + +results = DataFrame( + solver_upper = String[], + Δp = Union{Nothing, Float64}[], + seed = Int[], + profit = Float64[], + num_evals = Int[], + time = Float64[] +) + +for solver_upper in solvers_upper + for Δp in Δps + for seed in seeds + Random.seed!(seed) + start_time = time() + profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) + end_time = time() + push!(results, (solver_upper, Δp, seed, profit, num_evals, end_time - start_time)) + end + end +end + +using CSV +save_file = "results/strategic_bidding_nlopt_$(casename).csv" + +CSV.write(save_file, results) \ No newline at end of file diff --git a/test_jump.jl b/test_jump.jl index a4f35e9a..abc4e974 100644 --- a/test_jump.jl +++ b/test_jump.jl @@ -1,3 +1,6 @@ +using JuMP +using Ipopt + model = JuMP.Model(Ipopt.Optimizer) @variable(model, x) diff --git a/testing_barrier.jl b/testing_barrier.jl index 75d66d68..4006e0f1 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -34,16 +34,21 @@ test_compute_derivatives_Analytical(DICT_PROBLEMS_Analytical_cc) # Strategic bidding test ################################################ +using JuMP, NLopt + # "pglib_opf_case5_pjm" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" "pglib_opf_case24_ieee_rts" # Random.seed!(1234) # @time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) +solver_upper = :LD_MMA # Ipopt.Optimizer +solver_lower = Ipopt.Optimizer casename = "pglib_opf_case300_ieee" Random.seed!(1234) -@time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1, Δp=nothing) -# Δp=0.0 (no derivative): time=4.61s | obj= $474.18 -# Δp=nothing (no restoration): time=452.65s | obj= $79886.16 -# Δp=0.001 (with derivative): time=54.64s | obj= $474.18 +# @time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1, Δp=nothing, solver_lower=solver_lower, solver_upper=solver_upper) +@time trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=0.0, solver_lower=solver_lower, solver_upper=solver_upper) +# Δp=0.0 (no derivative): time=10.62s | obj= $161.94 +# Δp=nothing (no restoration): time=8.19s | obj= $510.66 +# Δp=0.001 (with derivative): time=10s | obj= $0 # casename = "pglib_opf_case14_ieee" # Random.seed!(1234) @@ -52,8 +57,6 @@ Random.seed!(1234) # Δp=nothing (no restoration): time=0.94s | obj= $0.00 # Δp=0.001 (with derivative): time=0.58s | obj= $0.00 - - ################################################ # Load sensitivity ################################################ From ed20b3c73b1c4ff38dedd103f8980ca0200dcb4b Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Wed, 25 Sep 2024 17:08:57 -0400 Subject: [PATCH 079/108] update comparison --- nlp_utilities.jl | 2 +- opf.jl | 31 +++---- ...c_bidding_nlopt_pglib_opf_case300_ieee.csv | 26 ++++++ strategic_bidding.jl | 93 ++++++++++++------- 4 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv diff --git a/nlp_utilities.jl b/nlp_utilities.jl index 16168539..78d2c0d4 100644 --- a/nlp_utilities.jl +++ b/nlp_utilities.jl @@ -555,7 +555,7 @@ function compute_sensitivity(evaluator::MOI.Nonlinear.Evaluator, cons::Vector{Co num_lower = length(has_low) # ∂s = [∂x; ∂λ; ∂ν_L; ∂ν_U] ∂s, K, N = compute_derivatives_no_relax(evaluator, cons, primal_vars, params, X, V_L, X_L, V_U, X_U, leq_locations, geq_locations, ineq_locations, has_up, has_low) - if isnothing(Δp) || iszero(Δp) + if isnothing(Δp) ## Adjust signs based on JuMP convention # Duals ∂s[num_w+1:num_w+num_cons, :] *= -sense_multiplier diff --git a/opf.jl b/opf.jl index b61da848..b48083e5 100644 --- a/opf.jl +++ b/opf.jl @@ -284,7 +284,7 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe end function ∇f(g::AbstractVector{T}, pg_bid_val...) where {T} - if value.(data["bid"]) == pg_bid_val + if !all(value.(data["bid"]) .== pg_bid_val) set_parameter_value.(data["bid"], pg_bid_val) JuMP.optimize!(data["model"]) @assert is_solved_and_feasible(data["model"]) @@ -349,37 +349,32 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no function f(pg_bid_val...) set_parameter_value.(data["bid"], pg_bid_val) JuMP.optimize!(data["model"]) - if !is_solved_and_feasible(data["model"]) - return nothing - end + @assert is_solved_and_feasible(data["model"]) + return [value.(data["bidding_generators_dispatch"]); -dual.(data["bidding_lmps"])] end - function ∇f(pg_bid_val...) where {T} - if value.(data["bid"]) == pg_bid_val - set_parameter_value.(data["bid"], pg_bid_val) - JuMP.optimize!(data["model"]) - @assert is_solved_and_feasible(data["model"]) - end + function ∇f(pg_bid_val...) + @assert all(value.(data["bid"]) .== pg_bid_val) Δs, sp = compute_sensitivity(evaluator, cons, Δp; primal_vars=primal_vars, params=data["bid"]) if isnothing(Δp) Δs = Δs * ones(size(Δs, 2)) end - # for i in 1:num_bidding_nodes - # g[i] = Δs[i] - # end - # for (i, b_idx) in enumerate(bidding_lmps_index) - # g[i] = -Δs[num_primal + num_ineq + b_idx] - # end - # return g + return [Δs[1:num_bidding_nodes]; -(Δs[(num_primal + num_ineq) .+ bidding_lmps_index])] end + if !isnothing(Δp) && iszero(Δp) + _∇f = (pg_bid_val...) -> zeros(num_bidding_nodes + length(bidding_lmps_index)) + else + _∇f = ∇f + end + trace = Any[] function my_objective_fn(pg_bid_val::Vector, grad::Vector) pg, lmps = f(pg_bid_val...) - Δpg, Δlmps = ∇f(pg_bid_val...) + Δpg, Δlmps = _∇f(pg_bid_val...) value = dot(lmps, pg) if length(grad) > 0 grad .= dot(Δlmps, pg) .+ dot(lmps, Δpg) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv new file mode 100644 index 00000000..7ca7ed05 --- /dev/null +++ b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv @@ -0,0 +1,26 @@ +solver_upper,solver_lower,Δp,seed,profit,num_evals,time +LD_MMA,Ipopt.Optimizer,0.0,1,1.0000198455835613e-6,2,4.231703042984009 +LD_MMA,Ipopt.Optimizer,0.0,2,1.000019833287e-6,2,2.4404358863830566 +LD_MMA,Ipopt.Optimizer,0.0,3,1.000019819264884e-6,2,2.1308090686798096 +LD_MMA,Ipopt.Optimizer,0.001,1,1.0000198455835613e-6,2,15.876381874084473 +LD_MMA,Ipopt.Optimizer,0.001,2,1.000019833287e-6,2,20.831727981567383 +LD_MMA,Ipopt.Optimizer,0.001,3,1.000019819264884e-6,2,12.927597045898438 +LD_MMA,Ipopt.Optimizer,nothing,1,122.80979095433254,46,95.62759113311768 +LD_MMA,Ipopt.Optimizer,nothing,2,5.361247345230248,57,115.93277406692505 +LD_MMA,Ipopt.Optimizer,nothing,3,8.94806616746816,34,71.92520213127136 +LN_BOBYQA,Ipopt.Optimizer,0.0,1,302.0187732946744,100,64.3822250366211 +LN_BOBYQA,Ipopt.Optimizer,0.0,2,6.108197398955952,100,69.61509203910828 +LN_BOBYQA,Ipopt.Optimizer,0.0,3,22.904266176734236,100,63.965275049209595 +LD_LBFGS,Ipopt.Optimizer,nothing,1,38.19299251701024,9,16.08238911628723 +LD_LBFGS,Ipopt.Optimizer,nothing,2,14.706836657761308,21,29.91307520866394 +LD_LBFGS,Ipopt.Optimizer,nothing,3,3.3367572220409927,9,12.287266969680786 +LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,1,38.19299251701024,9,12.877355813980103 +LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,2,14.706836657761308,33,47.2161078453064 +LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,3,3.3367572220409927,9,12.670855045318604 +LN_COBYLA,Ipopt.Optimizer,0.0,1,2.718615740130916e-5,61,41.15530300140381 +LN_COBYLA,Ipopt.Optimizer,0.0,2,0.00048269384484980624,100,66.10403800010681 +LN_COBYLA,Ipopt.Optimizer,0.0,3,0.0002700183347489131,100,66.326425075531 +LD_CCSAQ,Ipopt.Optimizer,nothing,1,131.513254935514,53,77.16368222236633 +LD_CCSAQ,Ipopt.Optimizer,nothing,2,14.139224753633222,54,75.80647897720337 +LD_CCSAQ,Ipopt.Optimizer,nothing,3,8.94710430047761,40,56.080732107162476 +LD_SLSQP,Ipopt.Optimizer,nothing,1,124.080646614382,47,63.36072301864624 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 2468992e..fd1ad224 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -1,4 +1,5 @@ -using JuMP +using JuMP, NLopt +using DataFrames, CSV using SparseArrays using LinearAlgebra using Ipopt @@ -12,45 +13,69 @@ include("opf.jl") # Strategic bidding test ################################################ -using JuMP, NLopt -using DataFrames, CSV - -solver_upper = :LN_BOBYQA # :LD_MMA +# Parameters max_eval = 100 solver_lower = Ipopt.Optimizer casename = "pglib_opf_case300_ieee" -Random.seed!(1234) - -start_time = time() -profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=0.0, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) -end_time = time() - -solvers_upper = [:LD_MMA, :LN_BOBYQA] -Δps = [0.0, 0.001, nothing] -seeds = [1234, 1235, 1236] - -results = DataFrame( - solver_upper = String[], - Δp = Union{Nothing, Float64}[], - seed = Int[], - profit = Float64[], - num_evals = Int[], - time = Float64[] +save_file = "results/strategic_bidding_nlopt_$(casename).csv" + +# #### test +# solver_upper = :LD_CCSAQ # :LD_MMA :LN_BOBYQA +# Random.seed!(1234) +# start_time = time() +# profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=nothing, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=2) +# end_time = time() +#### end test + +# Experiments +seeds = collect(1:3) + +experiements = Dict( + :LD_MMA => [nothing], # 0.001 + :LN_BOBYQA => [0.0], + :LD_CCSAQ => [nothing], + :LD_SLSQP => [nothing], + :LD_LBFGS => [nothing], + :LD_TNEWTON_PRECOND_RESTART => [nothing], + :LN_COBYLA => [0.0], ) -for solver_upper in solvers_upper - for Δp in Δps - for seed in seeds - Random.seed!(seed) - start_time = time() - profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) - end_time = time() - push!(results, (solver_upper, Δp, seed, profit, num_evals, end_time - start_time)) - end +# results = DataFrame( +# solver_upper = String[], +# solver_lower = String[], +# Δp = String[], +# seed = Int[], +# profit = Float64[], +# num_evals = Int[], +# time = Float64[] +# ) + +# Check already executed experiments +_experiments = [(string(solver_upper), string(solver_lower), string(Δp), seed) for (solver_upper, Δp_values) in experiements for Δp in Δp_values for seed in seeds] +if isfile(save_file) + old_results = CSV.read(save_file, DataFrame) + _experiments = setdiff(_experiments, [(string(row.solver_upper), string(row.solver_lower), string(row.Δp), row.seed) for row in eachrow(old_results)]) +else + open(save_file, "w") do f + write(f, "solver_upper,solver_lower,Δp,seed,profit,num_evals,time\n") end end -using CSV -save_file = "results/strategic_bidding_nlopt_$(casename).csv" +# Run experiments +for (_solver_upper, _, _Δp, seed) in _experiments + solver_upper = Symbol(_solver_upper) + Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) + Random.seed!(seed) + start_time = time() + profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) + end_time = time() + # push!(results, (string(solver_upper), string(solver_lower), string(Δp), seed, profit, num_evals, end_time - start_time)) + open(save_file, "a") do f + write(f, "$solver_upper,$solver_lower,$Δp,$seed,$profit,$num_evals,$(end_time - start_time)\n") + end +end -CSV.write(save_file, results) \ No newline at end of file +# Save append results +if isempty(results) + @info "No new results" +end From a5deb414009102bb7d36797caff6d1a894e42b87 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 26 Sep 2024 20:11:52 -0400 Subject: [PATCH 080/108] update results --- ...idding_nlopt_pglib_opf_case1354_pegase.csv | 22 +++++++++++++++++++ ...c_bidding_nlopt_pglib_opf_case300_ieee.csv | 2 ++ strategic_bidding.jl | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv new file mode 100644 index 00000000..e9713e1f --- /dev/null +++ b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv @@ -0,0 +1,22 @@ +solver_upper,solver_lower,Δp,seed,profit,num_evals,time +LD_MMA,Ipopt.Optimizer,nothing,1,6.552743704139989,35,607.0762448310852 +LD_MMA,Ipopt.Optimizer,nothing,2,24.65689441558742,11,189.1302628517151 +LD_MMA,Ipopt.Optimizer,nothing,3,12.79030929277882,32,657.2815849781036 +LN_BOBYQA,Ipopt.Optimizer,0.0,1,1.750027206217297e-6,100,573.1526970863342 +LN_BOBYQA,Ipopt.Optimizer,0.0,2,1.7500272043199948e-6,100,599.9318590164185 +LN_BOBYQA,Ipopt.Optimizer,0.0,3,1.7500271997137022e-6,100,519.2458550930023 +LD_LBFGS,Ipopt.Optimizer,nothing,1,6.43308144546134,10,192.49517393112183 +LD_LBFGS,Ipopt.Optimizer,nothing,2,25.377735769000456,22,455.94850397109985 +LD_LBFGS,Ipopt.Optimizer,nothing,3,11.89687311810197,11,215.26282000541687 +LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,1,6.43308144546134,10,186.01598191261292 +LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,2,25.377735786197082,35,586.9959080219269 +LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,3,11.89687311810197,11,222.30563807487488 +LN_COBYLA,Ipopt.Optimizer,0.0,1,3.062534620017495e-6,100,519.2816069126129 +LN_COBYLA,Ipopt.Optimizer,0.0,2,3.062534611897969e-6,100,560.6840751171112 +LN_COBYLA,Ipopt.Optimizer,0.0,3,3.06253461443539e-6,100,519.0000901222229 +LD_CCSAQ,Ipopt.Optimizer,nothing,1,6.510220786828136,38,660.5518319606781 +LD_CCSAQ,Ipopt.Optimizer,nothing,2,24.616026186976466,14,247.06148481369019 +LD_CCSAQ,Ipopt.Optimizer,nothing,3,12.48944315121175,32,712.5562012195587 +LD_SLSQP,Ipopt.Optimizer,nothing,1,11.045317208284606,52,963.7296869754791 +LD_SLSQP,Ipopt.Optimizer,nothing,2,24.90432329776601,34,641.5618929862976 +LD_SLSQP,Ipopt.Optimizer,nothing,3,21.62278469369729,65,1285.2417600154877 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv index 7ca7ed05..d538f799 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv @@ -24,3 +24,5 @@ LD_CCSAQ,Ipopt.Optimizer,nothing,1,131.513254935514,53,77.16368222236633 LD_CCSAQ,Ipopt.Optimizer,nothing,2,14.139224753633222,54,75.80647897720337 LD_CCSAQ,Ipopt.Optimizer,nothing,3,8.94710430047761,40,56.080732107162476 LD_SLSQP,Ipopt.Optimizer,nothing,1,124.080646614382,47,63.36072301864624 +LD_SLSQP,Ipopt.Optimizer,nothing,2,13.29071622697712,100,134.19766306877136 +LD_SLSQP,Ipopt.Optimizer,nothing,3,8.840982469339004,100,130.50193786621094 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index fd1ad224..dd9bbf15 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -16,11 +16,11 @@ include("opf.jl") # Parameters max_eval = 100 solver_lower = Ipopt.Optimizer -casename = "pglib_opf_case300_ieee" +casename = "pglib_opf_case1354_pegase"# "pglib_opf_case300_ieee" save_file = "results/strategic_bidding_nlopt_$(casename).csv" # #### test -# solver_upper = :LD_CCSAQ # :LD_MMA :LN_BOBYQA +# solver_upper = :LD_MMA # :LD_MMA :LN_BOBYQA # Random.seed!(1234) # start_time = time() # profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=nothing, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=2) From a7485201a96ad6508c059f73245ae02c53e0a2eb Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Tue, 8 Oct 2024 17:11:17 -0400 Subject: [PATCH 081/108] udapte --- opf.jl | 20 ++++++----- ...idding_nlopt_pglib_opf_case1354_pegase.csv | 22 ------------ ...c_bidding_nlopt_pglib_opf_case300_ieee.csv | 29 +-------------- strategic_bidding.jl | 36 +++++++++++++------ 4 files changed, 39 insertions(+), 68 deletions(-) delete mode 100644 results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv diff --git a/opf.jl b/opf.jl index b48083e5..28678a43 100644 --- a/opf.jl +++ b/opf.jl @@ -156,6 +156,7 @@ function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipo data["gen"]["$(bidding_gen_ids[i])"] = build_gen(node, bidding_gen_ids[i], pmax, qmax) end data = make_basic_network(data) + total_market = sum([data["gen"][g]["pmax"] for g in keys(data["gen"])]) model, ref, demand_equilibrium, p, q, pg, qg, va, vm, pload, qload = build_opf_model(data; solver=solver) @@ -189,6 +190,7 @@ function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipo "model" => model, "bid" => _pg_bid, "pmax" => pmax, + "total_market" => total_market ) end @@ -328,9 +330,11 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe println("Dispatch: ", value.(pg)) end -function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=:LD_MMA, solver_lower=Ipopt.Optimizer, max_eval=100) +function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=:LD_MMA, solver_lower=optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), + max_eval=100, pmax_multiplier=1.0 +) data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) - pmax = data["pmax"] + pmax = data["pmax"] * pmax_multiplier primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) set_parameter_value.(data["bid"], 0.01) @@ -351,7 +355,7 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no JuMP.optimize!(data["model"]) @assert is_solved_and_feasible(data["model"]) - return [value.(data["bidding_generators_dispatch"]); -dual.(data["bidding_lmps"])] + return value.(data["bidding_generators_dispatch"]), -dual.(data["bidding_lmps"]) end function ∇f(pg_bid_val...) @@ -362,11 +366,11 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no Δs = Δs * ones(size(Δs, 2)) end - return [Δs[1:num_bidding_nodes]; -(Δs[(num_primal + num_ineq) .+ bidding_lmps_index])] + return Δs[1:num_bidding_nodes], -(Δs[(num_primal + num_ineq) .+ bidding_lmps_index]) end if !isnothing(Δp) && iszero(Δp) - _∇f = (pg_bid_val...) -> zeros(num_bidding_nodes + length(bidding_lmps_index)) + _∇f = (pg_bid_val...) -> zeros(num_bidding_nodes), zeros(length(bidding_lmps_index)) else _∇f = ∇f end @@ -379,7 +383,7 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no if length(grad) > 0 grad .= dot(Δlmps, pg) .+ dot(lmps, Δpg) end - + # println("Objective: ", value) push!(trace, copy(pg_bid_val) => value) return value end @@ -390,14 +394,14 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no NLopt.xtol_rel!(opt, 1e-4) maxeval!(opt, max_eval) NLopt.max_objective!(opt, my_objective_fn) - max_f, opt_x, ret = NLopt.optimize(opt, fill(0.001, num_bidding_nodes)) + max_f, opt_x, ret = NLopt.optimize(opt, rand(num_bidding_nodes)) num_evals = NLopt.numevals(opt) println("Status: ", ret) println("Objective: ", max_f) println("Bids: ", opt_x[1:num_bidding_nodes]) println("Duals: ", opt_x[num_bidding_nodes+1:end]) println("Number of evaluations: ", num_evals) - return max_f, num_evals, trace + return max_f, num_evals, trace, (sum(trace[end][1]) / data["total_market"]) * 100 end function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver=Ipopt.Optimizer) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv deleted file mode 100644 index e9713e1f..00000000 --- a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv +++ /dev/null @@ -1,22 +0,0 @@ -solver_upper,solver_lower,Δp,seed,profit,num_evals,time -LD_MMA,Ipopt.Optimizer,nothing,1,6.552743704139989,35,607.0762448310852 -LD_MMA,Ipopt.Optimizer,nothing,2,24.65689441558742,11,189.1302628517151 -LD_MMA,Ipopt.Optimizer,nothing,3,12.79030929277882,32,657.2815849781036 -LN_BOBYQA,Ipopt.Optimizer,0.0,1,1.750027206217297e-6,100,573.1526970863342 -LN_BOBYQA,Ipopt.Optimizer,0.0,2,1.7500272043199948e-6,100,599.9318590164185 -LN_BOBYQA,Ipopt.Optimizer,0.0,3,1.7500271997137022e-6,100,519.2458550930023 -LD_LBFGS,Ipopt.Optimizer,nothing,1,6.43308144546134,10,192.49517393112183 -LD_LBFGS,Ipopt.Optimizer,nothing,2,25.377735769000456,22,455.94850397109985 -LD_LBFGS,Ipopt.Optimizer,nothing,3,11.89687311810197,11,215.26282000541687 -LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,1,6.43308144546134,10,186.01598191261292 -LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,2,25.377735786197082,35,586.9959080219269 -LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,3,11.89687311810197,11,222.30563807487488 -LN_COBYLA,Ipopt.Optimizer,0.0,1,3.062534620017495e-6,100,519.2816069126129 -LN_COBYLA,Ipopt.Optimizer,0.0,2,3.062534611897969e-6,100,560.6840751171112 -LN_COBYLA,Ipopt.Optimizer,0.0,3,3.06253461443539e-6,100,519.0000901222229 -LD_CCSAQ,Ipopt.Optimizer,nothing,1,6.510220786828136,38,660.5518319606781 -LD_CCSAQ,Ipopt.Optimizer,nothing,2,24.616026186976466,14,247.06148481369019 -LD_CCSAQ,Ipopt.Optimizer,nothing,3,12.48944315121175,32,712.5562012195587 -LD_SLSQP,Ipopt.Optimizer,nothing,1,11.045317208284606,52,963.7296869754791 -LD_SLSQP,Ipopt.Optimizer,nothing,2,24.90432329776601,34,641.5618929862976 -LD_SLSQP,Ipopt.Optimizer,nothing,3,21.62278469369729,65,1285.2417600154877 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv index d538f799..523cfb84 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv @@ -1,28 +1 @@ -solver_upper,solver_lower,Δp,seed,profit,num_evals,time -LD_MMA,Ipopt.Optimizer,0.0,1,1.0000198455835613e-6,2,4.231703042984009 -LD_MMA,Ipopt.Optimizer,0.0,2,1.000019833287e-6,2,2.4404358863830566 -LD_MMA,Ipopt.Optimizer,0.0,3,1.000019819264884e-6,2,2.1308090686798096 -LD_MMA,Ipopt.Optimizer,0.001,1,1.0000198455835613e-6,2,15.876381874084473 -LD_MMA,Ipopt.Optimizer,0.001,2,1.000019833287e-6,2,20.831727981567383 -LD_MMA,Ipopt.Optimizer,0.001,3,1.000019819264884e-6,2,12.927597045898438 -LD_MMA,Ipopt.Optimizer,nothing,1,122.80979095433254,46,95.62759113311768 -LD_MMA,Ipopt.Optimizer,nothing,2,5.361247345230248,57,115.93277406692505 -LD_MMA,Ipopt.Optimizer,nothing,3,8.94806616746816,34,71.92520213127136 -LN_BOBYQA,Ipopt.Optimizer,0.0,1,302.0187732946744,100,64.3822250366211 -LN_BOBYQA,Ipopt.Optimizer,0.0,2,6.108197398955952,100,69.61509203910828 -LN_BOBYQA,Ipopt.Optimizer,0.0,3,22.904266176734236,100,63.965275049209595 -LD_LBFGS,Ipopt.Optimizer,nothing,1,38.19299251701024,9,16.08238911628723 -LD_LBFGS,Ipopt.Optimizer,nothing,2,14.706836657761308,21,29.91307520866394 -LD_LBFGS,Ipopt.Optimizer,nothing,3,3.3367572220409927,9,12.287266969680786 -LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,1,38.19299251701024,9,12.877355813980103 -LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,2,14.706836657761308,33,47.2161078453064 -LD_TNEWTON_PRECOND_RESTART,Ipopt.Optimizer,nothing,3,3.3367572220409927,9,12.670855045318604 -LN_COBYLA,Ipopt.Optimizer,0.0,1,2.718615740130916e-5,61,41.15530300140381 -LN_COBYLA,Ipopt.Optimizer,0.0,2,0.00048269384484980624,100,66.10403800010681 -LN_COBYLA,Ipopt.Optimizer,0.0,3,0.0002700183347489131,100,66.326425075531 -LD_CCSAQ,Ipopt.Optimizer,nothing,1,131.513254935514,53,77.16368222236633 -LD_CCSAQ,Ipopt.Optimizer,nothing,2,14.139224753633222,54,75.80647897720337 -LD_CCSAQ,Ipopt.Optimizer,nothing,3,8.94710430047761,40,56.080732107162476 -LD_SLSQP,Ipopt.Optimizer,nothing,1,124.080646614382,47,63.36072301864624 -LD_SLSQP,Ipopt.Optimizer,nothing,2,13.29071622697712,100,134.19766306877136 -LD_SLSQP,Ipopt.Optimizer,nothing,3,8.840982469339004,100,130.50193786621094 +solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time diff --git a/strategic_bidding.jl b/strategic_bidding.jl index dd9bbf15..3bbb0f58 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -15,11 +15,27 @@ include("opf.jl") # Parameters max_eval = 100 -solver_lower = Ipopt.Optimizer -casename = "pglib_opf_case1354_pegase"# "pglib_opf_case300_ieee" +solver_lower, solver_lower_name = optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), "Ipopt" +casename = "pglib_opf_case300_ieee"# "pglib_opf_case300_ieee" save_file = "results/strategic_bidding_nlopt_$(casename).csv" -# #### test +# #### test Range Evaluation +# Random.seed!(1) +# data = build_bidding_opf_model(casename) +# # offer = rand(length(data["bidding_lmps"])) +# # offer = zeros(length(data["bidding_lmps"])); offer[1] = 1.0; offer[2] = 1.0; offer[3] = 1.0; offer[4] = 1.0 +# offer = ones(length(data["bidding_lmps"])) +# profits = [] +# start_time = time() +# for val = 0.0:0.1:12 +# set_parameter_value.(data["bid"], val * offer) +# JuMP.optimize!(data["model"]) +# push!(profits, dot(-dual.(data["bidding_lmps"]), value.(data["bidding_generators_dispatch"]))) +# end +# end_time = time() +#### end test + +# #### test SB # solver_upper = :LD_MMA # :LD_MMA :LN_BOBYQA # Random.seed!(1234) # start_time = time() @@ -28,7 +44,7 @@ save_file = "results/strategic_bidding_nlopt_$(casename).csv" #### end test # Experiments -seeds = collect(1:3) +seeds = collect(1:10) experiements = Dict( :LD_MMA => [nothing], # 0.001 @@ -51,13 +67,13 @@ experiements = Dict( # ) # Check already executed experiments -_experiments = [(string(solver_upper), string(solver_lower), string(Δp), seed) for (solver_upper, Δp_values) in experiements for Δp in Δp_values for seed in seeds] +_experiments = [(string(solver_upper), solver_lower_name, string(Δp), seed) for (solver_upper, Δp_values) in experiements for Δp in Δp_values for seed in seeds] if isfile(save_file) old_results = CSV.read(save_file, DataFrame) _experiments = setdiff(_experiments, [(string(row.solver_upper), string(row.solver_lower), string(row.Δp), row.seed) for row in eachrow(old_results)]) else open(save_file, "w") do f - write(f, "solver_upper,solver_lower,Δp,seed,profit,num_evals,time\n") + write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time\n") end end @@ -67,15 +83,15 @@ for (_solver_upper, _, _Δp, seed) in _experiments Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) Random.seed!(seed) start_time = time() - profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) + profit, num_evals, trace, market_share = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) end_time = time() - # push!(results, (string(solver_upper), string(solver_lower), string(Δp), seed, profit, num_evals, end_time - start_time)) + # push!(results, (string(solver_upper), solver_lower_name, string(Δp), seed, profit, num_evals, end_time - start_time)) open(save_file, "a") do f - write(f, "$solver_upper,$solver_lower,$Δp,$seed,$profit,$num_evals,$(end_time - start_time)\n") + write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time)\n") end end # Save append results -if isempty(results) +if isempty(_experiments) @info "No new results" end From 1803fbbbf57b460714f7d74705d7df3300b064b7 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Tue, 8 Oct 2024 22:26:17 -0400 Subject: [PATCH 082/108] fix typo --- opf.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opf.jl b/opf.jl index 28678a43..6cebca8f 100644 --- a/opf.jl +++ b/opf.jl @@ -370,7 +370,7 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no end if !isnothing(Δp) && iszero(Δp) - _∇f = (pg_bid_val...) -> zeros(num_bidding_nodes), zeros(length(bidding_lmps_index)) + _∇f = (pg_bid_val...) -> (zeros(num_bidding_nodes), zeros(length(bidding_lmps_index))) else _∇f = ∇f end From 7000113c68927c806eb4661ec903c46f11c32b54 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Wed, 9 Oct 2024 22:23:27 -0400 Subject: [PATCH 083/108] update --- opf.jl | 8 +- ...idding_nlopt_pglib_opf_case1354_pegase.csv | 32 ++++++++ ...c_bidding_nlopt_pglib_opf_case300_ieee.csv | 70 ++++++++++++++++++ ...c_bidding_nlopt_pglib_opf_case300_ieee.pdf | Bin 0 -> 18288 bytes strategic_bidding.jl | 67 +++++++++++++++-- 5 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf diff --git a/opf.jl b/opf.jl index 6cebca8f..e5422ba4 100644 --- a/opf.jl +++ b/opf.jl @@ -331,7 +331,7 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe end function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=:LD_MMA, solver_lower=optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), - max_eval=100, pmax_multiplier=1.0 + max_eval=100, pmax_multiplier=1.0, bool_trace=false ) data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) pmax = data["pmax"] * pmax_multiplier @@ -384,7 +384,9 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no grad .= dot(Δlmps, pg) .+ dot(lmps, Δpg) end # println("Objective: ", value) - push!(trace, copy(pg_bid_val) => value) + if bool_trace + push!(trace, sum(pg_bid_val) => value) + end return value end @@ -401,7 +403,7 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no println("Bids: ", opt_x[1:num_bidding_nodes]) println("Duals: ", opt_x[num_bidding_nodes+1:end]) println("Number of evaluations: ", num_evals) - return max_f, num_evals, trace, (sum(trace[end][1]) / data["total_market"]) * 100 + return max_f, num_evals, trace, (sum(opt_x[1:num_bidding_nodes]) / data["total_market"]) * 100, ret end function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver=Ipopt.Optimizer) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv new file mode 100644 index 00000000..c76fea46 --- /dev/null +++ b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv @@ -0,0 +1,32 @@ +solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status +LD_MMA,Ipopt,nothing,1,379521.42897568096,4.463315414691349,54,1026.4853658676147,1 +LD_MMA,Ipopt,nothing,2,407060.93605103396,4.542909921306311,58,1076.0979299545288,1 +LD_MMA,Ipopt,nothing,3,410312.7304017537,5.129981875986086,61,1219.9480831623077,1 +LD_MMA,Ipopt,nothing,4,382648.40172933176,4.570043129002875,58,1093.451698064804,1 +LD_MMA,Ipopt,nothing,5,400129.3840004562,4.435130100704832,55,1004.400083065033,1 +LD_MMA,Ipopt,nothing,6,398863.17915605806,4.711472480818748,54,1025.8297719955444,1 +LD_MMA,Ipopt,nothing,7,366467.5619779321,4.418414297264759,61,1150.896108865738,1 +LD_MMA,Ipopt,nothing,8,404302.97117886203,4.759032820654829,50,931.877956867218,1 +LD_MMA,Ipopt,nothing,9,412951.31729381235,4.416180195739012,58,1071.8616619110107,1 +LD_MMA,Ipopt,nothing,10,391522.83322145656,4.46357445793139,60,1151.4680500030518,1 +LN_BOBYQA,Ipopt,0.0,1,168820.5939024172,1.004908393034264,100,523.141793012619,1 +LN_BOBYQA,Ipopt,0.0,2,158509.44145516364,0.9567626049305376,100,544.5915019512177,1 +LN_BOBYQA,Ipopt,0.0,3,166297.78580567916,1.0253714817670256,100,532.0673048496246,1 +LN_BOBYQA,Ipopt,0.0,4,158518.2600053562,0.9668237599289118,100,484.60249185562134,1 +LN_BOBYQA,Ipopt,0.0,5,162552.88193519256,0.981494279687558,100,535.4164979457855,1 +LN_BOBYQA,Ipopt,0.0,6,158328.14431752235,0.9888221309137759,100,497.9929940700531,1 +LN_BOBYQA,Ipopt,0.0,7,163347.59302177207,0.991401346808828,100,530.5398418903351,1 +LN_BOBYQA,Ipopt,0.0,8,162521.7493274639,0.9920052077878728,100,671.4308149814606,1 +LN_BOBYQA,Ipopt,0.0,9,163639.85254358838,0.9906836031732348,100,507.2492690086365,1 +LN_BOBYQA,Ipopt,0.0,10,157648.53408377702,0.9426881226241853,100,520.3809530735016,1 +LD_LBFGS,Ipopt,nothing,1,379533.928182014,4.463652068653421,71,1367.5183429718018,1 +LD_LBFGS,Ipopt,nothing,2,407216.4369955502,4.540208357985074,62,1169.3655450344086,1 +LD_LBFGS,Ipopt,nothing,3,410318.18203851796,5.130083101101336,63,1199.300220966339,1 +LD_LBFGS,Ipopt,nothing,4,375902.4215017463,4.661620288652698,27,538.6267030239105,1 +LD_LBFGS,Ipopt,nothing,5,404906.1092889891,4.088859405877921,19,351.5564959049225,1 +LD_LBFGS,Ipopt,nothing,6,402324.6784042329,4.668206955671465,49,909.8845860958099,1 +LD_LBFGS,Ipopt,nothing,7,406897.83261238714,4.206412789263682,49,929.1883580684662,1 +LD_LBFGS,Ipopt,nothing,8,404483.36856513243,4.75736385353077,51,971.0387299060822,1 +LD_LBFGS,Ipopt,nothing,9,431654.9285262448,4.334209147267336,67,1284.628026008606,1 +LD_LBFGS,Ipopt,nothing,10,401019.60731385625,4.229152525971723,28,532.8773579597473,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,1,379543.70165973814,4.463943219982909,107,2173.238525867462,4 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv index 523cfb84..2b3f3234 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv @@ -1 +1,71 @@ solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time +LD_MMA,Ipopt,nothing,1,96110.71882678397,51.409912646352794,55,112.35776281356812 +LD_MMA,Ipopt,nothing,2,169611.93004530968,7.801358484116504,50,69.22345805168152 +LD_MMA,Ipopt,nothing,3,178444.28138203637,15.922922168322174,49,65.88223600387573 +LD_MMA,Ipopt,nothing,4,194336.04203766666,12.075242297979434,44,56.839174032211304 +LD_MMA,Ipopt,nothing,5,176613.2149870944,12.422180711866769,62,85.81530904769897 +LD_MMA,Ipopt,nothing,6,65458.26361763022,26.589452398348463,35,50.73106598854065 +LD_MMA,Ipopt,nothing,7,79498.15073959171,26.38580354505366,42,58.235145807266235 +LD_MMA,Ipopt,nothing,8,74496.46392915075,24.967103266479842,39,51.71993613243103 +LD_MMA,Ipopt,nothing,9,181934.25932745318,7.077167283620686,68,96.30612301826477 +LD_MMA,Ipopt,nothing,10,150551.556318804,15.517238331302416,44,61.61980485916138 +LN_BOBYQA,Ipopt,0.0,1,244232.89513945184,10.81443490086525,100,72.240394115448 +LN_BOBYQA,Ipopt,0.0,2,188387.21949619512,6.913013068576153,100,115.08367490768433 +LN_BOBYQA,Ipopt,0.0,3,193988.9162058156,7.025273557559125,100,65.74386215209961 +LN_BOBYQA,Ipopt,0.0,4,150513.5414421936,4.749046588752977,100,68.16472101211548 +LN_BOBYQA,Ipopt,0.0,5,237987.9450137697,8.291928779369465,100,70.9854621887207 +LN_BOBYQA,Ipopt,0.0,6,232213.14261507624,7.731145691394789,100,76.82888698577881 +LN_BOBYQA,Ipopt,0.0,7,237795.28401277255,10.070900059768746,100,73.00460886955261 +LN_BOBYQA,Ipopt,0.0,8,224848.85898168833,7.89813901386081,100,66.71672105789185 +LN_BOBYQA,Ipopt,0.0,9,274612.1997313932,11.68412522749941,100,67.7688729763031 +LN_BOBYQA,Ipopt,0.0,10,173580.3273015472,8.560154430382207,100,71.89584803581238 +LD_LBFGS,Ipopt,nothing,1,221645.11888298934,11.401696814027988,26,39.10578203201294 +LD_LBFGS,Ipopt,nothing,2,141262.71448922117,26.311399334821562,26,38.36118006706238 +LD_LBFGS,Ipopt,nothing,3,87363.0693632695,24.253863875152994,69,95.3198938369751 +LD_LBFGS,Ipopt,nothing,4,223731.48169915998,15.140592494373312,28,38.29574799537659 +LD_LBFGS,Ipopt,nothing,5,160116.8615679772,22.860132440024824,24,33.951844930648804 +LD_LBFGS,Ipopt,nothing,6,92192.29686791127,22.910628369499896,23,34.021559953689575 +LD_LBFGS,Ipopt,nothing,7,235055.40643308955,13.34543966105306,55,79.17287993431091 +LD_LBFGS,Ipopt,nothing,8,108555.6621307289,23.11464351812243,33,44.80105805397034 +LD_LBFGS,Ipopt,nothing,9,160994.8478759525,24.081755403931364,39,58.67839002609253 +LD_LBFGS,Ipopt,nothing,10,190046.33552746775,10.594052014825742,60,84.64722919464111 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,1,198035.28078277098,7.88719158718009,37,52.36943316459656 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,2,145472.39213218822,26.120541699500915,49,74.90275287628174 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,3,87359.72330924864,24.251828921465503,82,122.74614095687866 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,4,223801.14490554525,15.217660857646734,34,47.43060493469238 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,5,160116.8615679772,22.860132440024824,25,35.79092884063721 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,6,92192.29686791127,22.910628369499896,24,35.60436296463013 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,7,253347.71115981837,11.747118556625795,95,138.99833989143372 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,8,108543.39782339208,23.10815061751523,46,62.6436607837677 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,9,161039.6722133498,24.06518542269386,65,96.75429606437683 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,10,190069.3908097993,10.536935293217182,75,104.32000207901001 +LN_COBYLA,Ipopt,0.0,1,176010.19169836925,5.677305066259365,100,72.03100514411926 +LN_COBYLA,Ipopt,0.0,2,127374.33544940113,3.8448240810590555,100,64.18761801719666 +LN_COBYLA,Ipopt,0.0,3,176150.12057850952,5.482315426210346,100,65.934485912323 +LN_COBYLA,Ipopt,0.0,4,91727.20714677343,2.8158531169098886,100,68.77439904212952 +LN_COBYLA,Ipopt,0.0,5,198362.8753737887,6.237497498689416,100,72.6327908039093 +LN_COBYLA,Ipopt,0.0,6,102785.83811041675,2.9832594145233964,100,70.44147896766663 +LN_COBYLA,Ipopt,0.0,7,187911.54789161313,5.893495185002765,100,73.67294597625732 +LN_COBYLA,Ipopt,0.0,8,148226.30580434453,4.7518733303087295,100,66.37015104293823 +LN_COBYLA,Ipopt,0.0,9,106883.42897620534,3.1732717624926936,100,68.65271401405334 +LN_COBYLA,Ipopt,0.0,10,105033.57597358119,3.1867592685601807,100,68.18176698684692 +LD_CCSAQ,Ipopt,nothing,1,238249.22192024256,13.368774486955296,53,77.45764899253845 +LD_CCSAQ,Ipopt,nothing,2,152988.0994922613,19.838917943421166,51,73.36208820343018 +LD_CCSAQ,Ipopt,nothing,3,54459.19634512048,32.066785148062614,53,79.32646608352661 +LD_CCSAQ,Ipopt,nothing,4,223330.3099661669,14.979136317486603,59,77.34801506996155 +LD_CCSAQ,Ipopt,nothing,5,162860.81792287217,22.377943402548755,54,74.73434710502625 +LD_CCSAQ,Ipopt,nothing,6,238090.28459416158,16.0151547078734,56,77.43051195144653 +LD_CCSAQ,Ipopt,nothing,7,154809.254065423,16.97647571581712,55,78.2419638633728 +LD_CCSAQ,Ipopt,nothing,8,186665.13050085382,13.90320875043978,54,73.58215618133545 +LD_CCSAQ,Ipopt,nothing,9,103830.54778112366,29.50307082154892,46,69.35896801948547 +LD_CCSAQ,Ipopt,nothing,10,190069.37436692233,10.537467135211093,56,75.66284680366516 +LD_SLSQP,Ipopt,nothing,1,202122.28641528555,2.267153756014537,100,131.90146803855896 +LD_SLSQP,Ipopt,nothing,2,199331.8254757841,2.5237751272297952,100,138.61620903015137 +LD_SLSQP,Ipopt,nothing,3,223750.4938407642,2.2014218689308698,100,134.73497080802917 +LD_SLSQP,Ipopt,nothing,4,209293.7385906039,2.4208293676255077,100,129.0216929912567 +LD_SLSQP,Ipopt,nothing,5,203328.36023899372,1.4604130279370071,97,135.48368501663208 +LD_SLSQP,Ipopt,nothing,6,231355.38333033636,2.0340602661021707,100,140.8066110610962 +LD_SLSQP,Ipopt,nothing,7,242495.6487788387,4.432860622983777,100,136.01385188102722 +LD_SLSQP,Ipopt,nothing,8,187208.17397587682,1.6937783334110903,100,127.55766701698303 +LD_SLSQP,Ipopt,nothing,9,190462.07305198105,2.353980987448582,100,133.07019090652466 +LD_SLSQP,Ipopt,nothing,10,184874.85776357268,2.6584992062203487,100,135.63582706451416 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a4ba782e9309aa045ee07bc13ca2beffff5323b GIT binary patch literal 18288 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 %A8?Pv`*^m=ah_2;O?)V!OG53qZ`5iz>!%3_^V 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 Vv8V*>H$w{pQ$sFQRabvEE&wheI(7g6 literal 0 HcmV?d00001 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 3bbb0f58..25ca4c4b 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -4,6 +4,7 @@ using SparseArrays using LinearAlgebra using Ipopt using Random +using Logging include("nlp_utilities.jl") include("nlp_utilities_test.jl") @@ -16,7 +17,7 @@ include("opf.jl") # Parameters max_eval = 100 solver_lower, solver_lower_name = optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), "Ipopt" -casename = "pglib_opf_case300_ieee"# "pglib_opf_case300_ieee" +casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" save_file = "results/strategic_bidding_nlopt_$(casename).csv" # #### test Range Evaluation @@ -39,7 +40,7 @@ save_file = "results/strategic_bidding_nlopt_$(casename).csv" # solver_upper = :LD_MMA # :LD_MMA :LN_BOBYQA # Random.seed!(1234) # start_time = time() -# profit, num_evals, trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=nothing, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=2) +# profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=nothing, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=2) # end_time = time() #### end test @@ -56,6 +57,20 @@ experiements = Dict( :LN_COBYLA => [0.0], ) +res = Dict( + :FORCED_STOP => -5, + :ROUNDOFF_LIMITED => -4, + :OUT_OF_MEMORY => -3, + :INVALID_ARGS => -2, + :FAILURE => -1, + :SUCCESS => 1, + :STOPVAL_REACHED => 2, + :FTOL_REACHED => 3, + :XTOL_REACHED => 4, + :MAXEVAL_REACHED => 5, + :MAXTIME_REACHED => 6 +) + # results = DataFrame( # solver_upper = String[], # solver_lower = String[], @@ -73,7 +88,7 @@ if isfile(save_file) _experiments = setdiff(_experiments, [(string(row.solver_upper), string(row.solver_lower), string(row.Δp), row.seed) for row in eachrow(old_results)]) else open(save_file, "w") do f - write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time\n") + write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") end end @@ -83,11 +98,17 @@ for (_solver_upper, _, _Δp, seed) in _experiments Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) Random.seed!(seed) start_time = time() - profit, num_evals, trace, market_share = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) + profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) end_time = time() # push!(results, (string(solver_upper), solver_lower_name, string(Δp), seed, profit, num_evals, end_time - start_time)) - open(save_file, "a") do f - write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time)\n") + ret = res[ret] + if ret < 0 + @warn "Solver $(solver_upper) failed with seed $(seed)" + continue + else + open(save_file, "a") do f + write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") + end end end @@ -95,3 +116,37 @@ end if isempty(_experiments) @info "No new results" end + +# Plot results: One scatter point per solver_upper +# - Each solver_upper should be an interval with the mean and std of the results per seed +# - x-axis: market_share | y-axis: (profit / max_profit) * 100 +# - color: one per solver_upper +# - shape: one per Δp + +using Plots +using Statistics + +results = CSV.read(save_file, DataFrame) + +maximum_per_seed = [maximum(results.profit[results.seed .== seed]) for seed in seeds] + +results.gap = (maximum_per_seed[results.seed] - results.profit) * 100 ./ maximum_per_seed[results.seed] + +# use combined groupby +results_d = combine(groupby(results, [:solver_upper, :Δp]), :gap => mean, :market_share => mean, :gap => std, :num_evals => mean, :time => mean) + +# plot +markers = [] +for Δp in results_d.Δp + if Δp == "nothing" + push!(markers, :circle) + else + push!(markers, :diamond) + end +end +plt = scatter(results_d.market_share_mean, results_d.gap_mean, group=results_d.solver_upper, yerr=results_d.gap_std, + xlabel="Bid Volume (% of Market Share)", ylabel="Optimality Gap (%)", legend=:outertopright, marker=markers +) + +# save +savefig(plt, "results/strategic_bidding_nlopt_$(casename).pdf") \ No newline at end of file From 32032094f4592ab439ca1c975979f0fb64e309e4 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 10 Oct 2024 21:12:24 -0400 Subject: [PATCH 084/108] update --- ...idding_nlopt_pglib_opf_case1354_pegase.csv | 30 ++++++++++ ...idding_nlopt_pglib_opf_case1354_pegase.pdf | Bin 0 -> 18329 bytes ...idding_nlopt_pglib_opf_case2869_pegase.csv | 9 +++ ...c_bidding_nlopt_pglib_opf_case300_ieee.pdf | Bin 18288 -> 17702 bytes strategic_bidding.jl | 53 ++++++++++++++---- 5 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.pdf create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv index c76fea46..1f3abb1e 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv @@ -30,3 +30,33 @@ LD_LBFGS,Ipopt,nothing,8,404483.36856513243,4.75736385353077,51,971.038729906082 LD_LBFGS,Ipopt,nothing,9,431654.9285262448,4.334209147267336,67,1284.628026008606,1 LD_LBFGS,Ipopt,nothing,10,401019.60731385625,4.229152525971723,28,532.8773579597473,1 LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,1,379543.70165973814,4.463943219982909,107,2173.238525867462,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,6,402328.87271081784,4.668319190463052,85,1627.645066022873,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,8,406133.5530688092,4.3972176556105715,52,1025.2977900505066,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,9,431642.9372922031,4.333970990756671,50,1007.0831871032715,4 +LN_COBYLA,Ipopt,0.0,1,178359.74463298658,1.1200533758856948,100,593.1553659439087,5 +LN_COBYLA,Ipopt,0.0,2,212254.08379188183,1.4180001908515154,100,659.3406028747559,5 +LN_COBYLA,Ipopt,0.0,3,200066.04751296729,1.3332900353516668,100,595.0377469062805,5 +LN_COBYLA,Ipopt,0.0,4,218238.51484798797,1.4738619006311162,100,627.186784029007,5 +LN_COBYLA,Ipopt,0.0,5,203024.17273709437,1.3468078531035172,100,620.616494178772,5 +LN_COBYLA,Ipopt,0.0,6,191606.14917259244,1.2721033726111335,100,619.6530439853668,5 +LN_COBYLA,Ipopt,0.0,7,216634.18097193688,1.4533418695121343,100,620.3641991615295,5 +LN_COBYLA,Ipopt,0.0,8,220015.7631276374,1.4754972444825114,100,645.8744649887085,5 +LN_COBYLA,Ipopt,0.0,9,214629.68380120033,1.4855060084960066,100,580.2151620388031,5 +LN_COBYLA,Ipopt,0.0,10,189925.5983051968,1.2210286163994604,100,572.2593560218811,5 +LD_CCSAQ,Ipopt,nothing,1,286001.84954590956,6.691799689272085,44,869.4242498874664,4 +LD_CCSAQ,Ipopt,nothing,2,288247.7411749264,6.3875171811775004,35,649.8028280735016,4 +LD_CCSAQ,Ipopt,nothing,3,310628.608337936,6.372146554628574,58,1256.3902189731598,4 +LD_CCSAQ,Ipopt,nothing,5,298608.34558092774,6.8261547881138975,53,1064.9097120761871,4 +LD_CCSAQ,Ipopt,nothing,6,275603.01813745056,6.3531033574974884,55,1096.9445140361786,4 +LD_CCSAQ,Ipopt,nothing,7,289939.19647312385,6.440085995867354,37,724.0602381229401,4 +LD_CCSAQ,Ipopt,nothing,9,297084.5813973348,6.798650024586453,57,1227.8032429218292,4 +LD_SLSQP,Ipopt,nothing,1,376542.9974470474,3.2596009023573256,100,2095.886658191681,5 +LD_SLSQP,Ipopt,nothing,2,156706.26214407926,0.952168590574678,1,29.89198112487793,4 +LD_SLSQP,Ipopt,nothing,3,394748.4905896696,4.871532266285069,100,2073.313261985779,5 +LD_SLSQP,Ipopt,nothing,4,156773.4713477757,0.9610650569992588,1,29.20994520187378,4 +LD_SLSQP,Ipopt,nothing,5,161022.97906556918,0.9814364867894274,1,29.023494005203247,4 +LD_SLSQP,Ipopt,nothing,6,156720.88459197825,0.9791306609134534,1,28.679763793945312,4 +LD_SLSQP,Ipopt,nothing,7,161525.52344752845,0.983549520184631,1,29.40539813041687,4 +LD_SLSQP,Ipopt,nothing,8,394271.15137901035,4.110846793336604,100,2074.1711959838867,5 +LD_SLSQP,Ipopt,nothing,9,161725.68930725995,0.9896961020228838,1,30.21243405342102,4 +LD_SLSQP,Ipopt,nothing,10,385325.4647475914,3.744578744748129,100,2041.07581615448,5 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.pdf b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.pdf new file mode 100644 index 0000000000000000000000000000000000000000..43d2ebdf0e7942e38a7ea14e186ab939c976be81 GIT binary patch literal 18329 zcmZU)V{~Lu*EQO)(J?!=Dt6Mb?WE$4ZKq?~wr$(!*tTtUa?{WEyx)EAxaUWWTIb9) z*IIM$bN1RbPLatAi_$UDv%r$=?@i6ZG7{15aiM;j+=M?%2=3iprg|BL=ll>dP;!ZJvi{c<4GV*M9I`+qsI|1U>rBWn{! zQ$j{2SO!ruOGhJn1_31@2?=o{S79SVn_ou%+#DRg z5o$4rT7H%Ae>D4#*8f$CE(1Rvk@!DehWS4}kBWzuSac-aWNyZ?4c#v! zt|#<=?6FS5-R(j2&V4>-GwdAv)`kDu9G?5>fUd3k5gvh)jmfZ-{rMrsf9g5Wj+ZBL ztouQoh;?hR_6s~aN9pzKJRNZd)zR2p#%hk}ohvh8?QEe|R_q#vi=-o}?gaTGP1cbb zDf6LS;3hfryPPHLyCych`kbX&L)N|aKg$=IuZxb&!nV}DN1W8oB>Rdj;;+5Sq z%V^V%Ch@ePar~33?UCXv+s^2v%6%t)fk=uXM4}9eNu>QoDtUD zMXH{3F9%>G4mxI|T_L&4p6xxqG~F)Ll=hcaV~q2PQjnV(Ueo+~6?NFKF2u{(1b>3l z{ceOZE3%aNU27AVOn&W4J=HQ>r62YowNEB!(6pp?p#6|8TCY;@y^Q-}?necbA$Tg@ zOy)$mIHZ)`jmFOfe42~hF5HR9-8BnH_(e2uHU9*XO28KDc3b0$w0RheI<=|JCvbwh z1BN-BFF)11$r^i;Zt?zxbw7Dc{@K+WkZ2Y_&sL@C3Wc{#4Ft=B%=fNU=)HlkvIZKq zcUdAwH-yJp%;J1UtP4@CAudA@z(r0jpw{S_#W60LO?w!DpysMp#*JoktiGj3W~^X% zOu=A0rWm?`zzyq$Oo$E5xX>24x1Ju76(B%SUsX|6H^a<+jxl`jQ|o18KMV(}zVu6W z1=vOwN9>TM_3N29X`v7@z;)53SSfc2gf)Bj_(nmo=#jK8Q&NDj>5W;CJ3yZDR@N~> z24M#smDP~PrQAt4_m=nbRIa3qP0$3M7&*}AF-R|!4t;+kHgpcCkd%;)lkOKrNwo%N ziKU;8mN1^!QeAD>v5vPicV+$aS#F3AB+0=yOJK(YfW%ux#2t$Kj6cUaosvm%n+T^I zH9Rx=Hmp#mQi_C3(5-J*c;2dtG_FO6(2&9bbYpNs=I<@fJ9vIZOb#(=MY8qc;ZP^g zYoNOA*;fizYI`LrQkh7A`qcr=Ww{6eNu!zwrEZCZVjrQ4V47n{B*9jSARF~51#7GR zB@S!Fn&h6(RDwR0t|TtF6?+=^Owg4ib1G9zwO*?1jG5>t6%B(WHYDGXBn|l%YSATB z8i^7rK&Orho4j8<9{HeiXD$Gx+8qZASeza=|-n)7X-bQQj=| z+peZeDn*|9ZUq#LeImR!qmu{pR&iXioSfN8*3Ai*k9DUaH%2=0xg|Q{XlStzRW^yI zkXQ%B2k;<8Tn}3E451V&?YxbS3D4G+Fogpz9d`&ZV?i0+GfONQIU9$8eITVt8&Tww zOd+n5Vsj&rR@Se%&$ zXB9MGNkZa{4KpQQqU=caWYeiY4=xpQt1%nkax7mFnR`_rR3LCr&uP_tX?Yi}euLIb zo6a(#(}EmUH-m{>>m-P~7me35ap7xxpEtjyGLE;t5}cE4iwLvEztq!o`?fTtPk>;N zHf}&wbf^V(#vU{z>035Mt41|r%wkq6jnTJ7z;3;XC*b6;Nqi0paA|xJ>{oWn3vWvd zUqAq+AZk~7{%53xVD^JN{+o`@%g{~38R7~0qvhF1v%ZQWR z^y8{;DWrj;%FFtXn5h#KW?luok5i;vEd&Tx;Kd2cRmaHBlEUqcqzx%nE5k*YqP z9T^cK;BfG+MVzuJdoVFhbDDe9tRPJn{pIC+So_)B(=vM{yhEmJ>1%|6tM4|`X57;l zP>1~SIGYIClqaop>ycZYQP24E0!4)OVMC$jVgJ=DRH)LNH%$>B&EGoK(Spsgz-_7i z@w-;;-2+2V@Vzn&2HoPDaqc%;P1T7~!RIw8HP~fMw-G=ozKWY zUanX5ue8WVXOmL=aO7Seqq>Ng41K1^4eZ=?<>Pon)mNhd zX_0mMfF+fV56nHtGCv}57zd3NYWC+f#k$&x1cTMPgwfxu*23AUh9zMPrW1&mBo~vI zIqb0Aog^i7L3&j`MF6r@=gH#LyEGCpxWlDyEw$#(2zVCR7C!w&f3FvJ<9@Rh7)g>3 zEphPgIN&GZ0ILBh41S^XGRNhRRr+R?K4mF48lH+|b;Uc_6Ok?C8LjmwA$p2NjtS^? zM#v*<4XSDE=AtHBv0iD2>f54h-C&1~1~=D>^5IO2ikO6J$&jaJ8??)C;1QAF44o$` zOPHVxD3`>(h9RA%kK72 zT&}55J>0ZnCe1x0>C7|sMVL#QgdTmvc%J_4>l2%G6*)6B`~6_!`bxaT1Mtu)uwVde zUK4e}iin#eE0mdyTQGXtORDigA8CwDyo}}_mQGgoyVUP-k;yKC@RF`8{#4d88^sVG z-{_4fy{j!2pNT3PqO|iQ(>d(Zr%Yv)OZ9Rbqz-96IA2u5S;Odzu9I|Y+roVHd^HK*U=fU{CW&AauJ31i*l0wnp?ez;NA(4f3h-hI`&D z71O~DjXUoaTh5P*)=*_lZ-eUa?66QU)}_YgFZ#|UF=qz0!kb2(;VYif$<^}Y1#Puw z1^n`B#qbve&>u#Jt%Rgt@hqp;rS|AEQ^7)>^S&p0t?rB*AB+2vCrZ=^YEtSZcCe&v zs8}SbDU-DNr!4vnfZeg46Nmg;UzQm`5^Qcb54;8Nqh4#Nm=5~~x4GS0$$Hu4J5>uN zJ72|*qVt)L(wo=F(YCMQSz=CVZyx%Z&hpoJ5LJ1hPox#Q^4zgxxCVC1puVkn1SKvm z6qWss=LM(p(|bVO$&tS_Pj{SF#-DuUZ5ED(c2X!Z!s8Gj)3@2&Y={_N_Hs@wK(^hJ zLVhH>6=4S;Oq7zP6}}jlMa`a4O-HW}b;!$E8^XFO_%$%5PFP7RpuL$a@TtmZ|82^c zQ(I8D%vP;H$GZY8cNI)a{nLC37Ka8yI}sRo!8Y=(u_#Q-_XOWECZoq(S+K6xGF(K_ z@iDrLx3x6Y(v{95ewrs|J!N3%14am?VWL0EO-RI8es7nU?pta7956f6+I&4{a{GDM z| z_M(v@EB0FF0qKK8i<07CPpKox(9dCM$Q+78jtnJhH-`nSAeOf2F^O7%*V+Z+gOm|F z^q4{dQRoS6WEpl8d*g_e5e?6y+PD0Pm{9hu90B7HM^TT)X)4|?BZ@n|q6B$?=|z%z z7->NI%>vuj^|W+4!n=l?2fOQFddz9~@q@fF$xZYObe+2EGD`-U36}kvS3qq zav1iN0(H6CC{+s!{Vr|#eBM5$CCwh~yi2!{c|W{| zo*>$>6)^vfR~5$EEL!EmFgem!px+s5PX}o1QG(SpfD@F@62E^44Ij~uJN13Hbdbzh z`Lk-qv$vSHzI_c@V1%%iq};#EY-AOZ3@*n1plrzf3~nkDEJ+<+%#Gktf|ysoxN?)Y zQpTB36RYp^G~qaPiRn_JhnLGc`%GCBm zfQrQKlA_9>*l{LEMV=~=q*VaWIJCFICW0?)O~)zSB$+3xM8Pj^*B7cs@ruLJUUFA9 z1eGz_Bq18~QhM?Yfh@J=tPIymUn>hD4UJkL0-Oa24;~HROkFr$tT(vSfCoRcYG0kx zlmeO|qe>yBgMFFR(_t{wKRG|uJAq{eI3{7v`7s;AaXy9nGhDslJO3f_z#>8B2D5C+ZA|_@k za=J{aU9K{0_fyVdnk_8}o0a7Bx5eC%yV3TKKBig7V3NoNIQc1fZ^c<*q(~MJ_E~7N zf)&oenuvyQ@tIl_ZrbF2rrC<)#5@wt^OoM%IdN255-R~sBZ^N*Zk*Xj7QriS<4zLx z*`RYV=2=Kb8}?cHqT-C7s@P95b+e7Guxv3K;%D&`PbgD0lO2&P0yt(Q&=n5(^)0c+ z2T4{1rJL3TbR&&2F`}y_>mAqiX@P4*@_dk0+q8qfNKXLb;p!yo;<(<*{Sgh{J-3#p zf);Bd8+`9FnPx?AotjER8nw&@n_Hk>drA%EEbeQ@#g)`%#kt=eT7CIw@OPMI$tQKj z#nWS8zdj(C6N8p%H}$s7jZ3k_rEKTEQb4Re5l!>KH4ET-H0H?A6_i3%n1niJG9riZ zMg_+6$t6=UU!g*m46e)Y(rk1{m#=0P05Viera*DQE+w$dOxk%1p%3I6{(`|73#HN4 zpQ+FJ8EODfm+;s9QssyA$o7$2VgrMwK#nRC{Z!OoP1ruw3Lp$2`rW`S1jI2Sx^O#0 zVp~{w>P^`-0F+KzfT2t+0Op#J7NCA~^cO~ij~2kMLC@Ib$r)pi6~CeeA_6HZND(I* zYRvD3JP^Xc!ul|M%@3qPahvICy%oa0tS!-R7fFEeXj2|C+u@7Ff?%B%>uk!ZcRlR4 z{qy#Av<=RY&jiJm)P(Pw%yIzJ^kb4`ly}~Als%j&%@UEL;6eNgah$xmN*Wq$Z65D0 zDWT7`#stD>U45ab%6G17KZx4iT+6N#y0H7kt<%2_u}|eR=Pl^kcBRG}VdU?9=vIoM zAQv?w(7!Wf{qs6bkbkGj=3%rPf7wx1f=B*{H_0daXr-KP(4f23bFC~rZVmO<^=dl= zpoE^0A<-GI!*2{wlQw1*c1j0-J{M|Uc+m-1^KQol?zE^a{=|$`2miS$eGd7asEe4g zrKqtNsO-%P^p+bG3om)MuX!)E4Gh!V3GN))1Z0L#8vZSB!_vpp56bP~X2Qd;D%KlS z&KmI;&x0h>IRMWprR5+LSGIMYzFT&yq%?o6v=8Xi5Ov+)W2o=J=_l9Byp@M$%ABLADiawL4)E8 zcJSS%K(uP=m8TS3C>bG;tz~2E6mbi0sE39ZrVx8T8#lPE)0{vpBX5v)+v!%Uh4OCs zfMg)CrFn1xj8^XL&z%AK(iSH&1gB`FDj=29^Fohd*e13DR!ma`8bsYLkq>mHK^u65 z2|^I#hgVKR5L|1@CGUpV8-B}~oma}0@$ja;Aj8A#>07g5Uy`p zp3m7Y+W9OmLDk90IGkjD&NZ?3&JGw}1ZJZnaGmElS!`N|*!;@NddZx~8xN;$2&Zh; z*9?I!*JpjZI3-U|JUSQF;e(e`t&$n?l)uO!VJicIw;82ah052dcO;^~ycVY!qt%FM zc`+zZWYx4|v_j)7OjBo#^6u{pIl<5en?etxByR>%%#YI{T~u@Ti#I@SSG&FZn;STE zxs`EFn{!v?j&x(}=r>zX{b>S#trL zhrI-*pacxAnwL~e&|5TOhpQm>`VvpJi=U|ua~0i=Xbq|FtfPYD3#ov(k{)Zd-r|Dx z_>pH=5#*6Z_YovGXvelE9Br6o0=)A8Q;4*Gsi%Nx4mJ@Nc-%c?Z^<+y27nN9C~C9? z!}>ZesaAp9$IP@jH&Im18{R&8Z@R4a4}b>MM*$w3ATQ*-*6w!$Ag-3njqe30w<6xLW6SqgA^(IiVD1LtG-10tZd#^N4)n+z$~25zyi ze|d7=4`%HufIOc1TZ23*HV+&sp%a9)*~T6EH`f&=dc>93H~@x&{~Hx1J9AZoPYP)5 z_!h5QK;c}VpA0EC4rwSn2NvlN-$7}j028gB6{>3)jBHp$`R3@<$}1qXEqV*|7F}ZT z&zyV*GSc)uycU3XvVY&Lps1+Ck~PR{S?DkBENXDrSX`E0g5QuqnUo-oT0)FS(;CNY zT-CK4q8y&QQ<^$X2alQ$=n%P6dJ5 z9H1>|0R4ywrPD!p3GLC<5_aYBQO=W$ka-tt7UM@lKvrPNb$y5q&X6VN$t8nWSMNO~ zDYs^EIs^W|O{zL@X)c%1ll1!Sg)t{IFKZvSV4uv>q2>1W^o68i(fw@?D&?ffmhDFl zUsDT!-*>|J3M_uB#-yf|<1vG1e)-Bboh=R1DzGW0m>Ay-!5#UoQSvFz)ILzz%|heo znS6fv$ukYJ;N<54Cpg2{zj<+LyJ$0!THb4`tFo6aZ`dt{KIJx3yAD&a3$atB!lx8` zl*QbH-Bm=}i?b!U_h0v(^yHWh%IrI;w2J;p@2Saw zsw6)Hpbezsdomc95r;HJIg0gIfBnMuO?B3U;cJ)iI&)`DiGvO zQZTLlLMbn-)HCh?t&=eIf+n-DQ?WIyu+f?s;D?5j!nr13Aj0y5dbta8p&0D}%UIP^ zJ{??DcGi#M*E`4M#C4GW^Sg}iLWf_5>z^;_zh+MPmIEA1l*4|MtB);?rV#4vBjB^1 zZDhN;RWFDNG^X|D1V98?d@BM8VV9;5yC%PxMTR9HJwA`hOCXENAp^o7_kV{$gWsLd zvOw;RrJG^-1kI?RVjbH^X6T@Xyh!|)ra+3^Ks*jSW#U1z3C_BxXuVy3ED;LsBJ>z3 zTxFgr^UDm)Q#O%^d;FBYjD0VB{xm5n2to>k!_Iys(6(+jYuC0K%!akMASWzdCVPF@ zfDVUJaK+pgWQ109)$g9c=todq?x@b`$)LauvPA&GBl#ZJtpS>;X5qyxHmwvI0NW`v zsHoV8?GorD20m8$?Ay-WtzVmS*2JzM?>7;pZ)H=rP}IuqwlGhGsO8aBkc2MdTr%c> z9^4oHb^T7_IJO8Z@d&xYmG9<+MI*T0TZ*vGDETL8g5H_`_chXc*YXKpOoow|^)XDf zo19VKf?H2Q1y`!b|MC5|rtH296wmGJah^Q^2iSt7=;nt4k>B!r**zC9>{FU?E9zQw zOn*>m{;N&JV!Gq$tzZBKesNR;d~a zv#TgU#*^Z74SwHdNZP#%_}1mSx&aP4&is~HG=w4Ydft=v>{Q&RwY zYtL-6%oLdplQn^OAyRz&Gm!Hf)-1@=V8+bvO=~a{}Ek7ecqNs&^lx-T_l3$ z=2od6>wJjf#9)`@8X_qk8>X_yJ)3M^%B>if9ZDj;8?QAWj1k3fQO5hz`^|aWjVfTB z02QBjN`2bCT6j`At4w>y{a=8)qVNAv1!AWlRp*NB}EG?f5>5|AyNp@$ZqPLNy9iLZUpZmF=mzI*B2eXziCX!F!}-?-rcu0ypFQP%o9I)o_E#}S zG3fWWSj?Z&@Qv|9We^sh)DjQIS_*OXd40S{x_c>v1tDo;P@z%|d{vu4s;R-9%CMPi zIEJPofm|nfmFOy__8H{5+00P$8PO(>mUk z?WY*4J}z8JNu#zn@p_wPs~%E#j(0gNQ2^$UlEM%Xh8lS1tOHJqdVmWXaz7rog+{Ns z9Ua2f17_D@Jhuyax!R2;efbhHT0mXo?9dT=+}e}#0|06RvEkGxdLU^;;abby!6ZEJ zVWiD1pZd_b-~<70J$pPd9Z-Ai8(ozW;(_NWq$cU`MuV^D22X(sK1ODmXPqUt9$&qK(25br+^xWK)Aq@C58>B@DKCz`u^9861qp6P)p8xjpM!rG5|t0qf734Hx`IUBfY!YY z+p2VpMU!lQFv|J=wltbsCJ}*R|4ezFc6G*JT^#*e>ATKv%wp$bTi4S|Gk_u2lOZ?J zsaKi@_Ry}d_kHz)kp>2lKtV`BCng!LaCnYVl_$Mm@Pt4skQ1CBOOBRnqSx?IY%oiu zJ$!5Gce2i^KV3K;h?V{8En^!T`ZMH^?Kh1uI31-OfXvCYWt*r48i@`;sm}5+QZTd^ zqC%2*VlA}oH*tLmVni3M2=E~t+v;uLT6w(50dBVY1qdjh`Iw~8a$hDAm){&Pr$T_>C;CyW zvu2b9D7NzAw`FDG6Rpi}VF>J1vkKfE4}(@WM;pb823fJXZGycD!~gSEE?I6#nhCY= z=ZA`bvLYY#+B*@Uj@lBpBJU5DeEQy?ZM{2`gDKGLv101&st*lLr4DjK!D~yj@4z}d z*(IA1s(?QL@XzQ~L2`QJZL4`^AtN%n+Z&!;N=z|ER8v@7*%=b`>1w|*FYe-_;tmDT>1qjCY?ixqnaws{l?8Ld_{SZN&K74 zmfZ{w5PWXM{UaHBvVxXm!`apHkOSK#;+Z4DB)M{YIt~w*e zACZ7~N|yEJ`lwYn2WLjF2>3si_}>@A zN=fdmx4N8`rBS>Jxov@;1*mMEL9%;~C37#V{A@@=p0ykc$174SgIf<3OSZS5^~9Da ztJ|Tj5QtSopcPQR-)&Reiv3!N@u>@-uc3qr7Zcem8?S-j;l#O;hV(af*UyEM0kQJm zXYM!j<>0e!17*ceWQ#4I!SR`Dj^Bd_;85(Tq!vwJgg<>KrT#_XLA2`i@Qg%$kIdfprEOP%VZGTFb-pMiMwzhaumgjHE};IeOv(kMG36=V1A zX3?;79R`!eyS9KMqNm4q>|4H;;M$Wt!hByAy?q$38qh7$XrbMT^ImJz8LXn*J_N$} zHO1>qs`DhAe{*`?3+~@vaw7*NPw65H`&V%Wzg!ap9OoUJNtDJQ7HMG_p zgVlB+ZZY-lc{sX_lLRn6fna!AuI?SEF@^H}kC(r&r%rb{0v+KF@C z4-PNnZIBr#?@FcoOBwO{b#0RPt{itkvU7wWF*3KV4HX#tSgpIrBPUY5Ss!e0RxCm& zmCJpW53YZi|M9ysDg6^J>dg{Z<@dP8N#CjphQdUA)ubVG(oOVaLn0$OfjMc8Z7;-k3n4+MvhdW4hkdu&74E80eEhnhi#50V+%N-rKpSR&=4?(d}IL}3S zTZH{-Rt>xxV0KI5zLuoRIt0j`JNxAi7TfMG@acdy;mF-^s#3VNs?$qsPWfGelJG&vyu-^`(PsDY+>uFUi zb47HFs>C4ze3sel*RR7t{aET|p7F zuQ)~(1g_hCVVQK4X+<4LCXfqsYx24+T_na@8z)YbjNW$m*499T<>%D17KMOK6FL`OAR~n z8gE6juyhkRn>v96FB*HorJ3aqW8>jTvKpp|G_+L-E;k*bgyxQmONyMcW-y)p7!}!* z6aIa3&QL>(ZC=qr2ti(q#1D)}t#P$am-Zq?SMz`B64HP|H2bu1bI_wFrVAQaT?>iL zH1@%i7C+oFy^;e(6>k=)DzwrcQU(yQXtaKc%g03929;D-Bt;_ z4vEOeL2I03{DE}c!RkTyLy??9hkj-lEPG8*TjmU-lD(4F%%HizLZN|aWsH?sF==`? zJ_XZx<10p6wASo7I770j)@*sWwWs%Z9R0G_YLeq9>@Sk>7gTyrj-9Me2`Mgl&9cS` zQUA&kP0mMO3+I%R!t5H&!4uwHn+6nGzbIKt_$=Xbuq8`Ur9_ORNMRr`l=z9FBzi=Q zjUgS&hN!yhd505~e}S}Y2QV)hif@Z;5gf>|3ZimrWkiPuMn+PTE9=V<#KAmRu{+ob z7U^7MESjLGvk?YN=pCh2L%p6n!p(r8%MvoETjV2sW=pL}j8L79mo@e`Qe;kCj*TKE zz>4)WVi)DZ_@;@?Q42v>k$f?JhsFxnB##U~oe6mPqi-B@8Wg!|2OW9>UR+x^D?vsXILAqaC~*3VIN$CQ)6Di&qz_7Wme z(Ozy9(KS*08K{Owpfhz5T9WU!s7(Jd<7YrjpmVmE`Cz?7Rx^fp$xB5QGFmj*Sm1!>01?Di`TH&kGBjbHRGnH@&iMV_N~*=?sqIame3@mq~^$<;iy6kH^9_PC&sH}Nq3xJDI_EqzH~ z#+y@u$^X5ADgpaxO6fZ}-fh1Xkyqp^$vRCDH5J^RPIH94?9V%^UtuqKQYwM5RE(yp zhxuo${y@YCzWwIP(bf8H4Tnm(BJmZ?mPSx9HdRu{92XS?n)1Z9`3 zgpy93NjTeX-tf<=NmEs=FUV2>eXZAHDOw)cwn;x2+@qIIy!u~hrs0|&lEbwa8v59Z zB1O+JfK{+y6mRtGko)MTo_jX>r=ixIkb$fY?Qu9|TA8uz?U$a`<>suFTKby}bsr%c zuQUqnb=F%Dkhf&3p0H{eG%*j!g)MCl6av-up-m6XZO21L%@y77Uca?|b6#VGe}%6( z5A^u3(fGZ&x|~bXuG%CPOpFs9oq7sqVOz+&c|h{0T6Z>-S&Ib5* zt^r??gOk@R*Ey2U=x1)3-BTuK&W5RlX>i^#Dr?A{p`#}odF(ZgG?rerbs5J}_m^{# z(okHrA~ne4UfEfMyPWANroJ7#m#Nz=@5al&Dz#ZJ9)$hA%FAH;riSWge*MPs-4o`*8Ez~6EZ@rA9A{tLizWT|%Au;v@wHVJ6_vJbh)YwA&^}+XfhEdwI>ro$u#TnP zS_4!O@9~h3V#&U+=+#G@H_}IK93Gi*L5M*!z>#g{>2Tb>%kM0Q>p($n$#6IXE6L2o z%DzNo;%HRqnAYMDCjihLf{>Y5+z_qZ=bZ+EYpa=sd|k4NVHXBNC3nRqjqbiTj*kp4 zsZw78eB+ittU}j3q4EMxv8s8{yUH!ZOIOzDYd@h(w2TSJgHjmar@CGnmidFwTDadr zskGWr@a?10rhq%W?4Z~JYzUp!7|4Umhr~M9;+EVHUw=ELnEen-;mCTnw{3c- zLBM^4dem5Ib(8XNsKBd8b5u0-8p7SncZUB`v8C=dW5aj$Vjem5Dqg&j_E5`g+CKF< zQb{-Ao_VNV&nG)ZKIty@g>_gQ?p>HL>HOh8(-FmUTw~nHGnO?r7jCgQysMl4u|hUL zYNVh2V8`3!<3h*E=C#ON7B^*a)NkT-4kb#^WVk4HSz)ngQy_z;Kk3emi59Iv(B7=A z_<3ZwxR#pl_{aT9Y4TOwHR(4mW1O1w$8}wd@!!6Thlt>M6W*)6w~nUxpL;0}&a$SL z^8ykV^*Y62EN3=%JEM18uBKZ3ADJIpjaWELYZOjT`clGs?i+$4m*T^oS3P&+_;7Y( z=jseg?Yu;j7m|bZ*kU@D=o|-^+RU%1)c<-N+Oe`34c#I{M2`!5_!3CF*DA>9EGrZF9(}Va+lwZc~LaL;MA04JR zn@xWb`tQj%i(IhQL7bJ|P*o#Di=RR=H`%4H)ZHHD?Xax56#dsyAem~@r!bIqJ2DJ& ziHDgwv6U{xCs;^Q*k27(^1hmw^%A;x?(DS!Z5bL=SJp~t8w2*vd{E+cyUPB9D2(g@ z-?ta0&i-tn(&o=)>{v$wv8(45jYdkJRaO8>&pgq-qz&e8UysafPO00*jDgJ%1p=rza2#+s)+k2L{)F@Yh8D6l=iR8?Fj zo0GV0NgCcVqxb^tg*3ym`tozdRJ_<699c2DM5w$u#(0z$+J0~06UY_y&HV?-=2kLU zsm(L43Z*A5yXs~M0|aDR4H2}92qXj9of$I^R(lM8)hUPGm9`u9^s%AB;jagKf1PTo z!naJPf?djiSTz)pgh18SzTGf4_Njt%R<5(R+OufWx2tD&_+e2Cd(FFhR|K>!p49D; z7vv^xbFYJk!3ip;AN}{QE}K$~i?M$MsqxRb8C=*im}aWF0j-LggY}dik&~}{Cc^W_ zG6M>_Zq=5NuVEx+TI+CE_q|JlKdx~e8u2xIaF5exx&}J0WurH1*xxfXa$Wkh8PwI? zf0l-@ykJWqwH*D9gW$!bys+CLRmz{ysI?t##nTs3kB+N&{IL?$k#b0RrXFJm&ZOxU zj>jvTDrVjRlzWaBN6ahZ8!zcDz%=hlE>gui7CbP$u&N1kRUQ`AufX>qt zMzeI^|Nh$E&%$7*9{VF@(sRo5ox!Vcs8Ijs^`DbSI4JlQl7&xhM+@>0 z2Z73$ecNMqTvM{CtZhqAhD*9*H3w1UFOhY>L`jnqb;wFG;pq56O6%PJ!vHm3_K9QN z5xduqkPfGXo%J{8+_c$)zvZ?=3LC?ydPZZ5B3Qb(T*j|ibGlOKdU1rtrwX2X^Q2|` z5vE`ztmci51}V!j19XG2hgIb1xi?w^TS3wL zPvAZQ5X)P7>dafZ_}?B)SM3zQccN?h;_`pmC;Z|Md#XLPBS5^`n;Qjf6CR z(UEpO_e~MT*8|Rpzv!;PxNZXsAC*928xcoJR|6-13itx(otWSb^a* z?J7%?+AEdLz=k$=^OC{J^Tj{U>E(fz;YArK!!Tc%4wa)cxZ%JA?S85jE-eS=a*#3W ziS;uFqoo^qDY!>4oqS3X$ZcOY5RrUIHF)5Ph|Cu$oJ1%PYm9!i#LDQ&w0}nD^1^Gr z-=x%#syTcrur*TF(ln~_JTEYxrX30;yHDO(O^2#IoXMm#`KWZL-rup&jGq#S

_Gj%kL@}bH`z{%M@B53yC>! za@vCQ1_Kf28?Une+jkkb8WX+OT!>|nZW5;!U;w_hl*!)D-#o~da10r#YLFU-wbJ>C z%hyGyz=N^F9@X-l(eL5MpQ=#JkJXIoBrci6iCgAVqqc0|kj>WZz z9iNx0!z$LmC{BBgH`$BL)D{eV^{;lNg9(F6S4G=34cT>XY5wk&v~m|rgsiaeh?AfS zV$oLbo~ZjmI#PM27#jf9_g81wSs?u{zo{Q3tLqxsV>g?%6o#6AKY>kr1>Yd2YjGmU6f#?XY=oheK|i`i`b_=B=BLuYLaGVk6{pQNqK@*Z$t07O>l3R6ODOvMdY0&-g+!(obG#)}# z5DO1@`vUUL28*lkHL8xbVXotF=v0w#=TUCZ{Vo)!rJKwCc)OwONtQOPgC3a>p*Qe{^U_##|8~+frZKPLQK|13Pd(ssGNQoQP$D|ab0`k&r;O(tWy=!Nx{-AoW`(uGBPyQa%x_8FsS1_Kfvd>AV zY#JOp&(%#z)jL0&|6et^hkyzaX74bHGgDZ*w8WiB$FL)2dai5%_h{D{k5lDn*<6nB z{0)ta5V|go zy~}%f*|xjsO;7J~x&sZ(G2}mNes8tB#v%flI02jLm`(lfkB++)-jM~@RHR+@rzPW& z$FA3>!MCqN4N8y{{Stt{nr2B+N+jdFCsH02%@dG~7O}tcE)bEXGx9_xt3) zdzRTeH?PY^A@PVoN#VRpm;MwS?6s}-yW+{*I zBpiPbmS2x1!*fS(VdCy_|C-@cnbsSRTR=!`J}50}(D$_7S|Ti11rybi+h)=Fknh5w z!xO6JdDTj8j2*l=RlI&<7a)l=_r7z=kLJFz%;{OB@$O_Rrp zU|%4yIet$|z^`;Z(s#8R>_|6J)cM!G{E|N0StvqtJXd58kYB&XlY;$!$~e=YrjIa; zJKP2;wE;!WR42$0$~Bk(3Kj^K0y=~v+>lZt6a)z&i5!Yx7*Iiua0(HXK;<0d2pZ%F znG}Oy0D&k8HXsTl3Lye=N&hgd)6oz6<=N-MyF2r~JG(pYPhSHc01D;>*A%JY-=MhW zw90o0hI14{`6q$Jd)pmrI5+>(ipBzQ<{FwG1k$%EckQi8522*x1cGAt8$%*gE+q(L z#9GSVwT}y=P=R0NvT zbX(f)_UvnyM&PI$b=zBAp2t|5e<%`)z-jfD*`m#YJ2kQ|%BbCPN!VPatX}2PwM+Y5 zlwW%mD?wk$u;bPsj;l+fKMYlg@77{B}xzMJ$qC9%M!VTQodOa>YDbrY|GJ8`7 zS;}xepHgb0Sw*$2n-Nx$+J3Fl9P=Tm@Fa6q8->yF%kP&taC3!gMnqWUeaLPmn;<2sLFM`Vt)v;kzVQJwViR3 z9<)T0v>3-E2fg7j_CkXe*N?i*qe)Xd0c->q9G*_;0ao}{BmM|omDFqyMhw%W%Qk6i zlW4o=c!d$i_k98U&|$W1++@f0H5!92wck#>F}N?Ow|yR@w8W+L@1yJZ)B2psk^_i> zKsu46=&by#OFSnFluldqJg4k@hgO9HxM(cQi}+Fg@3ej)70$rGI9Z9U9QRFk^@}tw zN%@wC@|e=`OX!rA-!`l>DpA`jwI{;BZg#Z-feL$uKP(CX-X)3d!j%WL>OA#`wffgp5fN?*oy^GrDizfR6dJlovkx{>>0W+3y+AQ`oT%m3M&ts?c7okDmcIB_^NGS7Gqc(-FyE$Lu=N>;~$+lB@Jfa$*#QbSjKSo=+ZdZ$1@In z*+h(-ZR&%khf1b4N^4y)%Dk|TeIa)`P#$#|p}efFg1WZ4;waOGTTFP;q3c4o*?IA` zisb$a-8lR7rg4&Sc!NN%?o_f#H~sl2n?~(KgAdyv9Of&Z1p+|l{HNh@n8@gG+#W^8Gcqw82-y@B8;ghR!fyb! ijY|wR7P5=l*~6#A<1sk=-rCF&X4V!UC=_|l8T1eK!ziWz literal 0 HcmV?d00001 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv new file mode 100644 index 00000000..97ff4d4e --- /dev/null +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -0,0 +1,9 @@ +solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status +LD_MMA,Ipopt,nothing,1,763095.0244814962,5.473044706849988,58,5223.248332023621,4 +LD_MMA,Ipopt,nothing,2,647615.6240026074,5.510982189555721,51,4742.689513921738,4 +LD_MMA,Ipopt,nothing,3,785222.7713789152,5.496784911947918,52,4898.633311986923,4 +LD_MMA,Ipopt,nothing,4,696144.0876179516,5.693404697323059,48,5097.842547178268,4 +LD_MMA,Ipopt,nothing,5,777237.1560935642,5.465085022928578,31,3426.131083011627,4 +LD_MMA,Ipopt,nothing,7,709407.5791562888,5.551524568785184,51,4731.625789880753,4 +LD_MMA,Ipopt,nothing,8,829592.6036594185,5.503763274506792,54,5420.210821151733,4 +LD_MMA,Ipopt,nothing,9,740503.4336833901,5.494251454803217,53,4863.309112071991,4 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf index 8a4ba782e9309aa045ee07bc13ca2beffff5323b..f166879cc6085ac3be6be81ed11a52d0535d2ab2 100644 GIT binary patch delta 16970 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%0Ds@9Dju#Jcl>H9bbqSgm8Uc!2XT^=-^WoMpM@|> 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 C>xIDp delta 17582 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 Date: Thu, 10 Oct 2024 21:18:46 -0400 Subject: [PATCH 085/108] update --- strategic_bidding.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/strategic_bidding.jl b/strategic_bidding.jl index e3ecfd03..1ae146ed 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -18,7 +18,8 @@ include("opf.jl") max_eval = 100 solver_lower, solver_lower_name = optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), "Ipopt" casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" -save_file = "results/strategic_bidding_nlopt_$(casename).csv" +save_file_name = "results/strategic_bidding_nlopt_$(casename)" +save_file = save_file_name * ".csv" # #### test Range Evaluation # Random.seed!(1) @@ -93,7 +94,7 @@ else end for thread_id in 1:Threads.nthreads() - open(save_file * "_$thread_id", "w") do f + open(save_file_name * "_$thread_id" * ".csv", "w") do f write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") end end @@ -114,7 +115,7 @@ function run_experiment(_solver_upper, _Δp, seed, id) @warn "Solver $(solver_upper) failed with seed $(seed)" continue else - open(save_file * "_$thread_id", "a") do f + open(save_file_name * "_$id" * ".csv", "a") do f write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") end end @@ -126,12 +127,12 @@ end # Run experiments on multiple threads Threads.@threads for (_solver_upper, _solver_lower, _Δp, seed) in _experiments - run_experiment(_solver_upper, _Δp, seed) + run_experiment(_solver_upper, _Δp, seed, Threads.threadid()) end # Merge results for thread_id in 1:Threads.nthreads() - open(save_file * "_$thread_id", "r") do f + open(save_file_name * "_$thread_id" * ".csv", "r") do f lines = readlines(f) open(save_file, "a") do f for line in lines From bc87a86b241b26647355a1a24c6c83000c5edd51 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 10 Oct 2024 21:25:13 -0400 Subject: [PATCH 086/108] update --- strategic_bidding.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 1ae146ed..61da7475 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -113,7 +113,7 @@ function run_experiment(_solver_upper, _Δp, seed, id) ret = res[ret] if ret < 0 @warn "Solver $(solver_upper) failed with seed $(seed)" - continue + return nothing else open(save_file_name * "_$id" * ".csv", "a") do f write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") @@ -121,8 +121,9 @@ function run_experiment(_solver_upper, _Δp, seed, id) end catch e @warn "Solver $(solver_upper) failed with seed $(seed)" - continue + return nothing end + return nothing end # Run experiments on multiple threads From 68e7d49fe5aaf90223801a70a1a35c0cd8a837a8 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 10 Oct 2024 22:09:36 -0400 Subject: [PATCH 087/108] update --- strategic_bidding.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 61da7475..ea9b7286 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -107,7 +107,7 @@ function run_experiment(_solver_upper, _Δp, seed, id) try Random.seed!(seed) start_time = time() - profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_lower=solver_lower, solver_upper=solver_upper, max_eval=max_eval) + profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_upper=solver_upper, max_eval=max_eval) end_time = time() # push!(results, (string(solver_upper), solver_lower_name, string(Δp), seed, profit, num_evals, end_time - start_time)) ret = res[ret] From 0eba4003d4442b6e21a202ce8bb453783cb04d72 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 10 Oct 2024 22:29:06 -0400 Subject: [PATCH 088/108] try distributed --- strategic_bidding.jl | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/strategic_bidding.jl b/strategic_bidding.jl index ea9b7286..ab16e523 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -1,14 +1,18 @@ -using JuMP, NLopt -using DataFrames, CSV -using SparseArrays -using LinearAlgebra -using Ipopt -using Random -using Logging - -include("nlp_utilities.jl") -include("nlp_utilities_test.jl") -include("opf.jl") +using Distributed + +# addprocs() + +@everywhere using JuMP, NLopt +@everywhere using DataFrames, CSV +@everywhere using SparseArrays +@everywhere using LinearAlgebra +@everywhere using Ipopt +@everywhere using Random +@everywhere using Logging + +@everywhere include("nlp_utilities.jl") +@everywhere include("nlp_utilities_test.jl") +@everywhere include("opf.jl") ################################################ # Strategic bidding test @@ -16,7 +20,7 @@ include("opf.jl") # Parameters max_eval = 100 -solver_lower, solver_lower_name = optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), "Ipopt" +solver_lower_name = "Ipopt" casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" save_file_name = "results/strategic_bidding_nlopt_$(casename)" save_file = save_file_name * ".csv" @@ -93,7 +97,7 @@ else # end end -for thread_id in 1:Threads.nthreads() +for thread_id in 1:nprocs() open(save_file_name * "_$thread_id" * ".csv", "w") do f write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") end @@ -101,7 +105,7 @@ end # Run experiments # for (_solver_upper, _, _Δp, seed) in _experiments -function run_experiment(_solver_upper, _Δp, seed, id) +@everywhere function run_experiment(_solver_upper, _Δp, seed, id) solver_upper = Symbol(_solver_upper) Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) try @@ -127,12 +131,14 @@ function run_experiment(_solver_upper, _Δp, seed, id) end # Run experiments on multiple threads -Threads.@threads for (_solver_upper, _solver_lower, _Δp, seed) in _experiments - run_experiment(_solver_upper, _Δp, seed, Threads.threadid()) +@distributed for (_solver_upper, _solver_lower, _Δp, seed) in _experiments + id = myid() + @info "Running $(_solver_upper) $(_Δp) $seed on thread $id" + run_experiment(_solver_upper, _Δp, seed, id) end # Merge results -for thread_id in 1:Threads.nthreads() +for thread_id in 1:nprocs() open(save_file_name * "_$thread_id" * ".csv", "r") do f lines = readlines(f) open(save_file, "a") do f From 5d98548363cce9c761a39589b815453d20ed5b65 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Fri, 11 Oct 2024 13:52:11 -0400 Subject: [PATCH 089/108] update --- ...idding_nlopt_pglib_opf_case2869_pegase.csv | 12 +++++++++ strategic_bidding.jl | 27 ++++++++++--------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 97ff4d4e..c264a007 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -7,3 +7,15 @@ LD_MMA,Ipopt,nothing,5,777237.1560935642,5.465085022928578,31,3426.131083011627, LD_MMA,Ipopt,nothing,7,709407.5791562888,5.551524568785184,51,4731.625789880753,4 LD_MMA,Ipopt,nothing,8,829592.6036594185,5.503763274506792,54,5420.210821151733,4 LD_MMA,Ipopt,nothing,9,740503.4336833901,5.494251454803217,53,4863.309112071991,4 +LN_BOBYQA,Ipopt,0.0,2,385557.7831737955,1.0457149046685181,100,2048.12282204628,5 +LN_BOBYQA,Ipopt,0.0,3,384922.0106639474,1.0338236542586012,100,1741.6074459552765,5 +LN_BOBYQA,Ipopt,0.0,4,372987.8115548726,1.000955716353836,100,1879.1780288219452,5 +LN_BOBYQA,Ipopt,0.0,8,385599.3639342813,1.040553129624309,100,1853.8286619186401,5 +LN_BOBYQA,Ipopt,0.0,9,377596.75082265434,1.0280291628739298,100,1888.7991490364075,5 +LN_BOBYQA,Ipopt,0.0,10,364604.2245929031,0.9789858879518353,100,1799.6835939884186,5 +LN_COBYLA,Ipopt,0.0,2,455690.4622564906,1.2768479925360656,100,2056.48614692688,5 +LN_COBYLA,Ipopt,0.0,3,453681.9058440277,1.268292117138062,100,1788.0797748565674,5 +LN_COBYLA,Ipopt,0.0,4,445688.2818385775,1.243156638955702,100,1846.7739961147308,5 +LN_COBYLA,Ipopt,0.0,8,402582.56670932414,1.1039157409656242,100,1886.1198689937592,5 +LN_COBYLA,Ipopt,0.0,9,460929.08241540554,1.2936620440698983,100,1753.9974720478058,5 +LN_COBYLA,Ipopt,0.0,10,441021.88283406827,1.234313197882759,100,1746.5269379615784,5 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index ab16e523..96b7204d 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -19,11 +19,11 @@ using Distributed ################################################ # Parameters -max_eval = 100 -solver_lower_name = "Ipopt" -casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" -save_file_name = "results/strategic_bidding_nlopt_$(casename)" -save_file = save_file_name * ".csv" +@everywhere max_eval = 100 +@everywhere solver_lower_name = "Ipopt" +@everywhere casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" +@everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" +@everywhere save_file = save_file_name * ".csv" # #### test Range Evaluation # Random.seed!(1) @@ -62,7 +62,7 @@ experiements = Dict( :LN_COBYLA => [0.0], ) -res = Dict( +@everywhere res = Dict( :FORCED_STOP => -5, :ROUNDOFF_LIMITED => -4, :OUT_OF_MEMORY => -3, @@ -108,7 +108,7 @@ end @everywhere function run_experiment(_solver_upper, _Δp, seed, id) solver_upper = Symbol(_solver_upper) Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) - try + # try Random.seed!(seed) start_time = time() profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_upper=solver_upper, max_eval=max_eval) @@ -123,15 +123,15 @@ end write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") end end - catch e - @warn "Solver $(solver_upper) failed with seed $(seed)" - return nothing - end + # catch e + # @warn "Solver $(solver_upper) failed with seed $(seed)" + # return nothing + # end return nothing end # Run experiments on multiple threads -@distributed for (_solver_upper, _solver_lower, _Δp, seed) in _experiments +@sync @distributed for (_solver_upper, _solver_lower, _Δp, seed) in _experiments id = myid() @info "Running $(_solver_upper) $(_Δp) $seed on thread $id" run_experiment(_solver_upper, _Δp, seed, id) @@ -142,8 +142,9 @@ for thread_id in 1:nprocs() open(save_file_name * "_$thread_id" * ".csv", "r") do f lines = readlines(f) open(save_file, "a") do f - for line in lines + for line in lines[2:end] write(f, line) + write(f, "\n") end end end From 1661f3756d866e4942442f679ca0da7a660c826f Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Fri, 11 Oct 2024 15:03:01 -0400 Subject: [PATCH 090/108] update --- ...rategic_bidding_nlopt_pglib_opf_case2869_pegase.csv | 2 ++ strategic_bidding.jl | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index c264a007..59ee48a2 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -19,3 +19,5 @@ LN_COBYLA,Ipopt,0.0,4,445688.2818385775,1.243156638955702,100,1846.7739961147308 LN_COBYLA,Ipopt,0.0,8,402582.56670932414,1.1039157409656242,100,1886.1198689937592,5 LN_COBYLA,Ipopt,0.0,9,460929.08241540554,1.2936620440698983,100,1753.9974720478058,5 LN_COBYLA,Ipopt,0.0,10,441021.88283406827,1.234313197882759,100,1746.5269379615784,5 +LN_COBYLA,Ipopt,0.0,1,445078.1423079036,1.2348499223505311,100,1642.233705997467,5 +LD_SLSQP,Ipopt,nothing,5,361092.8467074701,0.9705346029574599,1,328.6287798881531,4 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 96b7204d..c4e2df0f 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -108,7 +108,7 @@ end @everywhere function run_experiment(_solver_upper, _Δp, seed, id) solver_upper = Symbol(_solver_upper) Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) - # try + try Random.seed!(seed) start_time = time() profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_upper=solver_upper, max_eval=max_eval) @@ -123,10 +123,10 @@ end write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") end end - # catch e - # @warn "Solver $(solver_upper) failed with seed $(seed)" - # return nothing - # end + catch e + @warn "Solver $(solver_upper) failed with seed $(seed)" + return nothing + end return nothing end From cd90ce491c2b0cfeed16a94a47ee838ff8425c92 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Fri, 11 Oct 2024 21:54:33 -0400 Subject: [PATCH 091/108] update --- ...idding_nlopt_pglib_opf_case2869_pegase.csv | 3 ++ strategic_bidding.jl | 52 +++++++++++++------ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 59ee48a2..538f9a49 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -21,3 +21,6 @@ LN_COBYLA,Ipopt,0.0,9,460929.08241540554,1.2936620440698983,100,1753.99747204780 LN_COBYLA,Ipopt,0.0,10,441021.88283406827,1.234313197882759,100,1746.5269379615784,5 LN_COBYLA,Ipopt,0.0,1,445078.1423079036,1.2348499223505311,100,1642.233705997467,5 LD_SLSQP,Ipopt,nothing,5,361092.8467074701,0.9705346029574599,1,328.6287798881531,4 +LD_CCSAQ,Ipopt,nothing,1,865777.6409374254,4.924741812075953,58,4722.888010025024,4 +LN_BOBYQA,Ipopt,0.0,6,377512.7432059537,1.0171404460731441,100,2210.409973859787,5 +LN_BOBYQA,Ipopt,0.0,7,372795.4990087037,1.0051094144550203,100,4360.791481018066,5 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index c4e2df0f..d59ba644 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -92,17 +92,17 @@ if isfile(save_file) old_results = CSV.read(save_file, DataFrame) _experiments = setdiff(_experiments, [(string(row.solver_upper), string(row.solver_lower), string(row.Δp), row.seed) for row in eachrow(old_results)]) else - # open(save_file, "w") do f - # write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") - # end -end - -for thread_id in 1:nprocs() - open(save_file_name * "_$thread_id" * ".csv", "w") do f + open(save_file, "w") do f write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") end end +# for thread_id in 1:nprocs() +# open(save_file_name * "_$thread_id" * ".csv", "w") do f +# write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") +# end +# end + # Run experiments # for (_solver_upper, _, _Δp, seed) in _experiments @everywhere function run_experiment(_solver_upper, _Δp, seed, id) @@ -119,30 +119,48 @@ end @warn "Solver $(solver_upper) failed with seed $(seed)" return nothing else - open(save_file_name * "_$id" * ".csv", "a") do f - write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") - end + # open(save_file_name * "_row" * "_$id" * ".csv", "w") do f + # write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") + # end + df = DataFrame( + solver_upper = [string(solver_upper)], + solver_lower = [solver_lower_name], + Δp = [string(Δp)], + seed = [seed], + profit = [profit], + market_share = [market_share], + num_evals = [num_evals], + time = [end_time - start_time], + status = [ret] + ) + save_file_id = save_file_name * "_row" * "_$id" * ".csv" + @async CSV.write(save_file_id, df) end catch e - @warn "Solver $(solver_upper) failed with seed $(seed)" + @warn "Solver $(solver_upper) failed with seed $(seed)" e return nothing end return nothing end # Run experiments on multiple threads -@sync @distributed for (_solver_upper, _solver_lower, _Δp, seed) in _experiments - id = myid() - @info "Running $(_solver_upper) $(_Δp) $seed on thread $id" +@sync @distributed for id in collect(1:length(_experiments)) + _solver_upper, _solver_lower, _Δp, seed = _experiments[id] + # id = uuid1() + @info "Running $(_solver_upper) $(_Δp) $seed on thread $(myid())" id run_experiment(_solver_upper, _Δp, seed, id) end # Merge results -for thread_id in 1:nprocs() - open(save_file_name * "_$thread_id" * ".csv", "r") do f +iter_files = readdir("results"; join=true) +# filter +iter_files = [file for file in iter_files if occursin(casename, file)] +iter_files = [file for file in iter_files if occursin("row", file)] +for file in iter_files + open(file, "r") do f lines = readlines(f) open(save_file, "a") do f - for line in lines[2:end] + for line in lines[1:end] write(f, line) write(f, "\n") end From ccbba5bf4e67873f83df845035802ada69976162 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Sat, 12 Oct 2024 12:21:32 -0400 Subject: [PATCH 092/108] update --- .../strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv | 6 ++++++ strategic_bidding.jl | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 538f9a49..c052b86e 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -24,3 +24,9 @@ LD_SLSQP,Ipopt,nothing,5,361092.8467074701,0.9705346029574599,1,328.628779888153 LD_CCSAQ,Ipopt,nothing,1,865777.6409374254,4.924741812075953,58,4722.888010025024,4 LN_BOBYQA,Ipopt,0.0,6,377512.7432059537,1.0171404460731441,100,2210.409973859787,5 LN_BOBYQA,Ipopt,0.0,7,372795.4990087037,1.0051094144550203,100,4360.791481018066,5 +LD_MMA,Ipopt,nothing,6,655217.2314237518,5.768599646056398,51,4790.7436101436615,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,4,946819.1086822012,4.2721327415139125,61,6084.313436985016,4 +LD_MMA,Ipopt,nothing,10,740397.4707648519,5.437243652642576,51,4644.582688808441,4 +LD_CCSAQ,Ipopt,nothing,2,958960.7569564288,4.9773509468261565,53,4771.052474975586,4 +LD_CCSAQ,Ipopt,nothing,3,881844.446174467,5.030087253722467,52,4612.008496999741,4 +LN_BOBYQA,Ipopt,0.0,1,370868.89833338564,0.99269379010979,100,1574.4580879211426,5 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index d59ba644..82ccdf76 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -160,7 +160,7 @@ for file in iter_files open(file, "r") do f lines = readlines(f) open(save_file, "a") do f - for line in lines[1:end] + for line in lines[2:end] write(f, line) write(f, "\n") end From 11a50559182d2c824bcbf4a2b41ece1062043435 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Mon, 14 Oct 2024 12:32:15 -0400 Subject: [PATCH 093/108] update --- ..._bidding_nlopt_pglib_opf_case2869_pegase.csv | 1 + ..._bidding_nlopt_pglib_opf_case2869_pegase.pdf | Bin 0 -> 16530 bytes 2 files changed, 1 insertion(+) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index c052b86e..83fc9aa0 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -30,3 +30,4 @@ LD_MMA,Ipopt,nothing,10,740397.4707648519,5.437243652642576,51,4644.582688808441 LD_CCSAQ,Ipopt,nothing,2,958960.7569564288,4.9773509468261565,53,4771.052474975586,4 LD_CCSAQ,Ipopt,nothing,3,881844.446174467,5.030087253722467,52,4612.008496999741,4 LN_BOBYQA,Ipopt,0.0,1,370868.89833338564,0.99269379010979,100,1574.4580879211426,5 +LD_CCSAQ,Ipopt,nothing,4,852544.2212812195,4.937471040422679,57,6256.860535144806,4 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4eb1ec621290e448473a32ad8974ce7325da0951 GIT binary patch literal 16530 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 literal 0 HcmV?d00001 From 3309208dd287e5d477ca6f9937429b1b40cfd021 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Mon, 14 Oct 2024 17:06:33 -0400 Subject: [PATCH 094/108] update --- .../strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 83fc9aa0..988397ea 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -31,3 +31,9 @@ LD_CCSAQ,Ipopt,nothing,2,958960.7569564288,4.9773509468261565,53,4771.0524749755 LD_CCSAQ,Ipopt,nothing,3,881844.446174467,5.030087253722467,52,4612.008496999741,4 LN_BOBYQA,Ipopt,0.0,1,370868.89833338564,0.99269379010979,100,1574.4580879211426,5 LD_CCSAQ,Ipopt,nothing,4,852544.2212812195,4.937471040422679,57,6256.860535144806,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,8,886900.2692065428,4.522389761538202,85,9017.482391119003,4 +LD_CCSAQ,Ipopt,nothing,5,876558.4320741579,4.9579754656702315,60,6208.630883932114,4 +LD_CCSAQ,Ipopt,nothing,6,882604.859439062,4.949006378588906,57,5059.946820020676,4 +LD_CCSAQ,Ipopt,nothing,7,865919.200103611,4.936893793045543,56,4742.679541110992,4 +LD_SLSQP,Ipopt,nothing,4,939773.5724978196,3.712319507250259,100,9312.706056833267,5 +LD_SLSQP,Ipopt,nothing,7,370846.8680343175,1.000010615588849,1,119.07087993621826,4 From 84585a523066bce2fc9ca4c28eaf26ce7b46335b Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Mon, 14 Oct 2024 21:31:07 -0400 Subject: [PATCH 095/108] update --- results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 988397ea..2bcf0a46 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -37,3 +37,4 @@ LD_CCSAQ,Ipopt,nothing,6,882604.859439062,4.949006378588906,57,5059.946820020676 LD_CCSAQ,Ipopt,nothing,7,865919.200103611,4.936893793045543,56,4742.679541110992,4 LD_SLSQP,Ipopt,nothing,4,939773.5724978196,3.712319507250259,100,9312.706056833267,5 LD_SLSQP,Ipopt,nothing,7,370846.8680343175,1.000010615588849,1,119.07087993621826,4 +LD_SLSQP,Ipopt,nothing,2,981751.7547293808,3.976173171005669,100,9234.672760009766,5 From 56655d38169dea47c71d075c7a050e1ef5a87a4d Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Tue, 15 Oct 2024 13:38:18 -0400 Subject: [PATCH 096/108] update --- results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv | 3 +++ strategic_bidding.jl | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 2bcf0a46..06bbb877 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -38,3 +38,6 @@ LD_CCSAQ,Ipopt,nothing,7,865919.200103611,4.936893793045543,56,4742.679541110992 LD_SLSQP,Ipopt,nothing,4,939773.5724978196,3.712319507250259,100,9312.706056833267,5 LD_SLSQP,Ipopt,nothing,7,370846.8680343175,1.000010615588849,1,119.07087993621826,4 LD_SLSQP,Ipopt,nothing,2,981751.7547293808,3.976173171005669,100,9234.672760009766,5 +LD_SLSQP,Ipopt,nothing,8,880621.0068699884,3.2708947211254404,100,8346.3530189991,5 +LD_SLSQP,Ipopt,nothing,9,962220.4989043615,4.253120976146774,100,8124.440047979355,5 +LD_SLSQP,Ipopt,nothing,10,915427.2589328055,4.072749203205618,100,8140.769929885864,5 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 82ccdf76..5da246da 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -144,7 +144,7 @@ end end # Run experiments on multiple threads -@sync @distributed for id in collect(1:length(_experiments)) +@sync @distributed for id in collect(length(_experiments):-1:1) _solver_upper, _solver_lower, _Δp, seed = _experiments[id] # id = uuid1() @info "Running $(_solver_upper) $(_Δp) $seed on thread $(myid())" id From 7fd09fddcaad7964952d04fbea8b333f5e802b9b Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Wed, 16 Oct 2024 15:05:52 -0400 Subject: [PATCH 097/108] update --- results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index 06bbb877..fc16ddab 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -41,3 +41,4 @@ LD_SLSQP,Ipopt,nothing,2,981751.7547293808,3.976173171005669,100,9234.6727600097 LD_SLSQP,Ipopt,nothing,8,880621.0068699884,3.2708947211254404,100,8346.3530189991,5 LD_SLSQP,Ipopt,nothing,9,962220.4989043615,4.253120976146774,100,8124.440047979355,5 LD_SLSQP,Ipopt,nothing,10,915427.2589328055,4.072749203205618,100,8140.769929885864,5 +LD_SLSQP,Ipopt,nothing,3,382965.20115181385,1.0285899909371885,1,109.31101393699646,4 \ No newline at end of file From 7ffd2f86be0f0bd4202b3554e61523fca044ad28 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 17 Oct 2024 10:47:15 -0400 Subject: [PATCH 098/108] update --- .gitignore | 2 + Project.toml | 1 + opf.jl | 19 ++++---- slurm.jl | 18 +++++++ strategic_bidding.jl | 111 ++++++++++++++++++++++--------------------- testing_barrier.jl | 34 ------------- 6 files changed, 88 insertions(+), 97 deletions(-) create mode 100644 slurm.jl diff --git a/.gitignore b/.gitignore index fc3a525f..2cc34922 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ Manifest.toml examples/.ipynb_checkpoints .DS_Store /Manifest.toml +*.sbatch +*.out diff --git a/Project.toml b/Project.toml index d73a81b4..c15e4468 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.4.2" [deps] BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +ClusterManagers = "34f1f09b-3a8b-5176-ab39-66d58a4d544e" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" diff --git a/opf.jl b/opf.jl index e5422ba4..58d04635 100644 --- a/opf.jl +++ b/opf.jl @@ -142,8 +142,7 @@ function build_opf_model(data; add_param_load=false, solver=Ipopt.Optimizer) return model, ref, demand_equilibrium, p, q, pg, qg, va, vm, pload, qload end -function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipopt.Optimizer) - data = make_basic_network(pglib(case_name)) +function build_bidding_opf_model(data; percen_bidding_nodes=0.1, solver=Ipopt.Optimizer) data["basic_network"] = false # add bidding generators num_bidding_nodes = ceil(Int, length(data["bus"]) * percen_bidding_nodes) @@ -171,7 +170,7 @@ function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipo println("") println("\033[1mSummary\033[0m") - println(" case........: $(case_name)") + # println(" case........: $(case_name)") println(" variables...: $(model_variables)") println(" constraints.: $(model_constraints)") @@ -183,7 +182,7 @@ function build_bidding_opf_model(case_name; percen_bidding_nodes=0.1, solver=Ipo remaining_vars = setdiff(all_primal_variables, bidding_generators_dispatch) remaining_vars = setdiff(remaining_vars, _pg_bid) return Dict( - "case" => case_name, + # "case" => case_name, "model_variables" => remaining_vars, "bidding_generators_dispatch" => bidding_generators_dispatch, "bidding_lmps" => [demand_equilibrium[i] for i in bidding_nodes], @@ -248,9 +247,9 @@ function fdiff_derivatives(f::Function) return ∇f, ∇²f end -function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=Ipopt.Optimizer, solver_lower=Ipopt.Optimizer) +function test_bilevel_ac_strategic_bidding(data; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=Ipopt.Optimizer, solver_lower=Ipopt.Optimizer) # test derivative of the dual of the demand equilibrium constraint - data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) + data = build_bidding_opf_model(data; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) pmax = data["pmax"] primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) @@ -330,10 +329,10 @@ function test_bilevel_ac_strategic_bidding(case_name="pglib_opf_case5_pjm.m"; pe println("Dispatch: ", value.(pg)) end -function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=:LD_MMA, solver_lower=optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), +function test_bidding_nlopt(data; percen_bidding_nodes=0.1, Δp=0.0001, solver_upper=:LD_MMA, solver_lower=optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0), max_eval=100, pmax_multiplier=1.0, bool_trace=false ) - data = build_bidding_opf_model(case_name; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) + data = build_bidding_opf_model(data; percen_bidding_nodes=percen_bidding_nodes, solver=solver_lower) pmax = data["pmax"] * pmax_multiplier primal_vars = [data["bidding_generators_dispatch"]; data["model_variables"]] num_bidding_nodes = length(data["bidding_generators_dispatch"]) @@ -406,8 +405,8 @@ function test_bidding_nlopt(case_name="pglib_opf_case5_pjm.m"; percen_bidding_no return max_f, num_evals, trace, (sum(opt_x[1:num_bidding_nodes]) / data["total_market"]) * 100, ret end -function sesitivity_load(case_name="pglib_opf_case5_pjm.m"; Δp=nothing, solver=Ipopt.Optimizer) - data = make_basic_network(pglib(case_name)) +function sesitivity_load(data; Δp=nothing, solver=Ipopt.Optimizer) + # data = make_basic_network(pglib(case_name)) model, ref, demand_equilibrium, p, q, pg, qg, va, vm, pload, qload = build_opf_model(data; solver=solver, add_param_load=true) num_bus = length(ref[:bus]) diff --git a/slurm.jl b/slurm.jl new file mode 100644 index 00000000..b0a20356 --- /dev/null +++ b/slurm.jl @@ -0,0 +1,18 @@ +# + +try + + using Distributed, ClusterManagers +catch + Pkg.add("ClusterManagers") + Pkg.checkout("ClusterManagers") +end + +using Distributed, ClusterManagers + +np = 50 # +addprocs(SlurmManager(np), job_file_loc = ARGS[1]) + +println("We are all connected and ready.") + +include(ARGS[2]) \ No newline at end of file diff --git a/strategic_bidding.jl b/strategic_bidding.jl index 5da246da..ffa9add3 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -1,7 +1,16 @@ using Distributed +# Add worker processes if needed # addprocs() +@everywhere pkg_path = @__DIR__ + +@everywhere import Pkg + +@everywhere Pkg.activate(pkg_path) + +@everywhere Pkg.instantiate() + @everywhere using JuMP, NLopt @everywhere using DataFrames, CSV @everywhere using SparseArrays @@ -25,6 +34,10 @@ using Distributed @everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" @everywhere save_file = save_file_name * ".csv" +@everywhere data + +data = make_basic_network(pglib(casename)) + # #### test Range Evaluation # Random.seed!(1) # data = build_bidding_opf_model(casename) @@ -53,7 +66,7 @@ using Distributed seeds = collect(1:10) experiements = Dict( - :LD_MMA => [nothing], # 0.001 + :LD_MMA => [nothing], :LN_BOBYQA => [0.0], :LD_CCSAQ => [nothing], :LD_SLSQP => [nothing], @@ -76,52 +89,43 @@ experiements = Dict( :MAXTIME_REACHED => 6 ) -# results = DataFrame( -# solver_upper = String[], -# solver_lower = String[], -# Δp = String[], -# seed = Int[], -# profit = Float64[], -# num_evals = Int[], -# time = Float64[] -# ) +# Prepare the list of experiments +_experiments = [ + (string(solver_upper), solver_lower_name, string(Δp), seed) + for (solver_upper, Δp_values) in experiements + for Δp in Δp_values + for seed in seeds +] # Check already executed experiments -_experiments = [(string(solver_upper), solver_lower_name, string(Δp), seed) for (solver_upper, Δp_values) in experiements for Δp in Δp_values for seed in seeds] if isfile(save_file) old_results = CSV.read(save_file, DataFrame) - _experiments = setdiff(_experiments, [(string(row.solver_upper), string(row.solver_lower), string(row.Δp), row.seed) for row in eachrow(old_results)]) + _experiments = setdiff(_experiments, [ + (string(row.solver_upper), string(row.solver_lower), string(row.Δp), row.seed) + for row in eachrow(old_results) + ]) else open(save_file, "w") do f write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") end end -# for thread_id in 1:nprocs() -# open(save_file_name * "_$thread_id" * ".csv", "w") do f -# write(f, "solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status\n") -# end -# end - -# Run experiments -# for (_solver_upper, _, _Δp, seed) in _experiments @everywhere function run_experiment(_solver_upper, _Δp, seed, id) + _data = deepcopy(data) solver_upper = Symbol(_solver_upper) Δp = _Δp == "nothing" ? nothing : parse(Float64, _Δp) try Random.seed!(seed) start_time = time() - profit, num_evals, trace, market_share, ret = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=Δp, solver_upper=solver_upper, max_eval=max_eval) + profit, num_evals, trace, market_share, ret = test_bidding_nlopt( + _data; percen_bidding_nodes=0.1, Δp=Δp, solver_upper=solver_upper, max_eval=max_eval + ) end_time = time() - # push!(results, (string(solver_upper), solver_lower_name, string(Δp), seed, profit, num_evals, end_time - start_time)) ret = res[ret] if ret < 0 @warn "Solver $(solver_upper) failed with seed $(seed)" return nothing else - # open(save_file_name * "_row" * "_$id" * ".csv", "w") do f - # write(f, "$solver_upper,$solver_lower_name,$Δp,$seed,$profit,$market_share,$num_evals,$(end_time - start_time),$ret\n") - # end df = DataFrame( solver_upper = [string(solver_upper)], solver_lower = [solver_lower_name], @@ -133,46 +137,47 @@ end time = [end_time - start_time], status = [ret] ) - save_file_id = save_file_name * "_row" * "_$id" * ".csv" - @async CSV.write(save_file_id, df) + return df end catch e @warn "Solver $(solver_upper) failed with seed $(seed)" e return nothing end - return nothing end -# Run experiments on multiple threads -@sync @distributed for id in collect(length(_experiments):-1:1) - _solver_upper, _solver_lower, _Δp, seed = _experiments[id] - # id = uuid1() - @info "Running $(_solver_upper) $(_Δp) $seed on thread $(myid())" id - run_experiment(_solver_upper, _Δp, seed, id) -end +# Run experiments using pmap +experiments_list = [ + (_solver_upper, _Δp, seed, id) + for (id, (_solver_upper, _, _Δp, seed)) in enumerate(_experiments) +] -# Merge results -iter_files = readdir("results"; join=true) -# filter -iter_files = [file for file in iter_files if occursin(casename, file)] -iter_files = [file for file in iter_files if occursin("row", file)] -for file in iter_files - open(file, "r") do f - lines = readlines(f) - open(save_file, "a") do f - for line in lines[2:end] - write(f, line) - write(f, "\n") - end - end - end -end +results = pmap( + experiment -> run_experiment(experiment...), + experiments_list +) -# Save append results -if isempty(_experiments) +# Filter out failed experiments +results = filter(!isnothing, results) + +# Combine results into a DataFrame +if !isempty(results) + all_results = vcat(results...) + # Append to existing results if any + if isfile(save_file) + old_results = CSV.read(save_file, DataFrame) + combined_results = vcat(old_results, all_results) + else + combined_results = all_results + end + # Save combined results + CSV.write(save_file, combined_results) +else @info "No new results" end +# Proceed with plotting or further analysis as needed + + # Plot results: One scatter point per solver_upper # - Each solver_upper should be an interval with the mean and std of the results per seed # - x-axis: market_share | y-axis: (profit / max_profit) * 100 diff --git a/testing_barrier.jl b/testing_barrier.jl index 4006e0f1..a6a7bcd8 100644 --- a/testing_barrier.jl +++ b/testing_barrier.jl @@ -30,40 +30,6 @@ test_compute_derivatives_Finite_Diff(DICT_PROBLEMS_cc, true) test_compute_derivatives_Analytical(DICT_PROBLEMS_Analytical_cc) -################################################ -# Strategic bidding test -################################################ - -using JuMP, NLopt - -# "pglib_opf_case5_pjm" "pglib_opf_case14_ieee" "pglib_opf_case30_ieee" "pglib_opf_case57_ieee" "pglib_opf_case118_ieee" "pglib_opf_case300_ieee" "pglib_opf_case24_ieee_rts" -# Random.seed!(1234) -# @time test_bilevel_ac_strategic_bidding("pglib_opf_case24_ieee_rts"; percen_bidding_nodes=0.1) - -solver_upper = :LD_MMA # Ipopt.Optimizer -solver_lower = Ipopt.Optimizer -casename = "pglib_opf_case300_ieee" -Random.seed!(1234) -# @time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1, Δp=nothing, solver_lower=solver_lower, solver_upper=solver_upper) -@time trace = test_bidding_nlopt(casename; percen_bidding_nodes=0.1, Δp=0.0, solver_lower=solver_lower, solver_upper=solver_upper) -# Δp=0.0 (no derivative): time=10.62s | obj= $161.94 -# Δp=nothing (no restoration): time=8.19s | obj= $510.66 -# Δp=0.001 (with derivative): time=10s | obj= $0 - -# casename = "pglib_opf_case14_ieee" -# Random.seed!(1234) -# @time test_bilevel_ac_strategic_bidding(casename; percen_bidding_nodes=0.1, Δp=nothing) -# Δp=0.0 (no derivative): time=8.93s | obj= $0.00 -# Δp=nothing (no restoration): time=0.94s | obj= $0.00 -# Δp=0.001 (with derivative): time=0.58s | obj= $0.00 - -################################################ -# Load sensitivity -################################################ - -Δs_primal, Δs_dual = sesitivity_load("pglib_opf_case24_ieee_rts") -Δs_dual[21,1:24] -Δs_dual[21,25:end] ################################################ # Moonlander test From b0aca2fa48cad13b47e102cd3c661d1e6a21e8a8 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 17 Oct 2024 16:22:19 -0400 Subject: [PATCH 099/108] update --- ...idding_nlopt_pglib_opf_case2869_pegase.csv | 7 ++++++- ...idding_nlopt_pglib_opf_case2869_pegase.pdf | Bin 16530 -> 17034 bytes strategic_bidding.jl | 4 +--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index fc16ddab..e557a2c7 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -41,4 +41,9 @@ LD_SLSQP,Ipopt,nothing,2,981751.7547293808,3.976173171005669,100,9234.6727600097 LD_SLSQP,Ipopt,nothing,8,880621.0068699884,3.2708947211254404,100,8346.3530189991,5 LD_SLSQP,Ipopt,nothing,9,962220.4989043615,4.253120976146774,100,8124.440047979355,5 LD_SLSQP,Ipopt,nothing,10,915427.2589328055,4.072749203205618,100,8140.769929885864,5 -LD_SLSQP,Ipopt,nothing,3,382965.20115181385,1.0285899909371885,1,109.31101393699646,4 \ No newline at end of file +LD_SLSQP,Ipopt,nothing,3,382965.20115181385,1.0285899909371885,1,109.31101393699646,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,3,894864.6797276961,4.501147755337157,44,3928.2089171409607,4 +LD_CCSAQ,Ipopt,nothing,8,843574.6079864681,4.987640110007977,47,3896.163267850876,4 +LD_CCSAQ,Ipopt,nothing,9,893301.5427800094,4.964824798862503,53,4455.742611169815,4 +LD_CCSAQ,Ipopt,nothing,10,870338.9921688911,4.91134686506627,60,4816.567409992218,4 +LD_SLSQP,Ipopt,nothing,1,963555.3278867417,4.030547695525361,100,8082.192088127136,5 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf index 4eb1ec621290e448473a32ad8974ce7325da0951..e17c4e22347390f33718889878ac53e1d47053db 100644 GIT binary patch delta 16303 zcmX}RV{|556D&HhZB3GijfrjBwrwX*Y}@w4#uHC$+qSJU?|1Jxe|xR&-fOSgwX3=l z13)4}K;i`<*_nPbv9Z>RV}pXTG5uyqG6p3DRQry{l6Bf00=s_=yT>p6CiM*n$X)&E zF>@lBH}ohYXBl{#Ym>Rrs|__+9ewb!Y#5E$&y( z|NUyu|FtjfC3^}1==$cE6SI$fm|l=_ zrrk|XT6Y;eM|WJ2J6Qu<$JTljN5dd}^WPu(45x?R+3=rnNd@2Dn49`vQPH@06b#3C zfUjXfU*Ht}Au>b(;I&cZZdAcmQ0xYVYxtHcPB!f3`7}9E{~VSpP<{sI)AjNQ<%2Wl zLggp!t9s6BS%lB5vTb+G8y-vdRoOP6^v;ywlRl;P{iLb#kO&52IPUg*(da$W&IcZ_^pyT5W`6JE(MZOE)Qz3Wo=x^@KSlqudI#h?G` zWirR2cO{|!b$M9Zqurd{2!ma#J@v_ID~4a$lzsCZ$H*sFdzhUz8rnR|%lQXzy#06x zW?Y$PdH5*70BOcvm#%he*~|cs|5(DdA8E<9yl!pHfR_!pZu;_4n-U;^TvG9L)|_t* zymLZ%5lDFtZ+htH2oTbn8iJHD_cZ^EwPTs?;rHWEao?LC@M1Ji#5tK7h|++>`;&!V z-a$8)89HKcYwoR%ZZ9i1qo)q&EM}c{cXDi5HA-1KpM`IUZAvO>^ejtjU3tKmbb+mC z)Gl?0^QKx%TUU7Tuwo<_c;Q^-(C!oIZSr*JcZNSS)g ztD${;XSp%r0(xGUK5!cP;-EZ?`=E$f5y6mC4K|6aOa^eyzn=&mytT}Y;jNhv<(bla zenoKRnLw4sm*p72HYx&A`r}(T(`K+5EaywpWE)C$7yI1w7!cpA`RFZ^*(vHGv}4e-_R{%AYlJ^5MwO)MiDgHfYZN^# z&4gOK&cOZ-+iPIjz8OE(sotUcQDLHmB|PI#KcdNyGd&HvpZo&o9rE?QaL7y4LZ#~u zdzjo+3|2&UU`c8ZO}8_-u|go7g9c5OzLD4rF&0qOPvXuz1O$f^uN1RcSbGd~_x4CHwz$jA+#Q_UKo3W4^t>bdf98DhenG_h6;I^sH&lcLHF~NShNC+J@V6OUA zof&D7V+d41BP0h5G|V|EdfNL#N%53*CXDKoAz?6e9H2)M5)VW-`|qF;8hvQfQ4YvO z91vAXYxFYQuYxACPc|-EJ>Ef&j7udfkrqX&yYJ3VoUJKPKD`e6n((l;$W5N=oXD=% zl|))(7z7l$QIjLq*??s8m-(_(7s*v$nDn~%zZPz<2I~Nd1r`l4wOy*kiU*lBp#gx6 zHc#i^l5tONtVh<74bC-CS!J6Dxid9-79j^iE|J!|$M?EL4kgGogWP$idNmBz6h06FT_fS zWhs8vJ;al0HFNB51>NwiU=>t;V89DvIcb)#T+oQ624PChb?C=9MLK`kCFi`;aXhJ$ z)A0vey^HW(F5y-$Grjzb4Qr?gHz1dKsW{MG?gHQ}hJaUDv~tGFVI|JIiP*a^uwR^H zW3slk#e_8N*t;E4s8XI`V1$Wh$ZC=Edk3ACHn^wDrG(|IT4i$dI%3MHCI-anIC4N~ zn;@s?zBpb+TmxMbjJ%KK)hE@{>9=Ji{K?J7w^`oHGi`A2i#7(D)zXg%A&{M>s!ZvS zn^r(t4SF4h=LlnkZslFF((f`$-;?WS!bT%4tDy-ya;JPt>EPbL^MNGHM}=<(UGjmD z59J~cSyz1UW!zGJhExzE zR2kX24Xbh{B-Dalu;u7sb<)Xn9A$Qpl1S>JK}WZZodpP9UdIEgdoZ+ zM#q;!XB9_gm-)3qX*qHe@pJ*+22%h>7ElB3)K|oiT&iE|ha<((#s#HuK_`njP?( zmHLI9J2xSt$?Q2bGf&3WYm;#G9B&aHI>=~;yIt4HQ}D0@{tVQWx5 zfrveJ{KnCE5g(UhiSPJF*xJW#^&r{zTKwJR0`FAqO?xagV@fsKO#nuR%g)?y2SjOW z2$jL)>!YN~cwU5Q8@uFm2eTOJ%S}mKXGioZx!LB|o9CCQAwh)ibHoA36h=w^(-(U}Wcg;1RNrrJKR1z5r z%vGq;h^f+oB<+GOW3@Lq)s|>fW3t*nw$bFBS7u|zQvzK2k@jh#S*EAH!ue~6OYqxH z8lR1Alm;F#?`uf+IXOru_5(08YE2X7$_WBFCKsd_e^{AfmUc)rhhN-vNU(HrWRj^0 zm*n3~z-swB@3gu-V>&wC3J63qQpk)tsaoFy@&PMVrEwy+&of#5s1w+B$in0v0{G4q zPUYZG3Bm*9(jJ;$jt{i$mnb1>E*nA1$dWo`f;Ql+_QATbnu8F$0{~L*lZK@xg58bm ziuNRy1a{F4EO}eU?&fe{IYGtdS}I2}7uclIUpu*sriK-q;ejw@%JGwCxLUF3_r)@T zhE*rSU0c)5+3hZU5>#vRT%|<7o~HXh!1|=d_^D%u8DRcLa*Wp+G}|B? zu1;?hMKB!3>coiRd9USQ##Fx!BtFzKckzREu9>F@LXSHy%qX}sM34&{_kCLAYPbeA z&-hXIBsgN0s3N1`^={9_Jc#&^fH*9^QU*A8ambzPsB{sh4xm}hQJZD zHu{*btuj4}Dj>}IGgrINJhX#yo>@M7J-&_*PDysi^e11Zj1qL)EgAGX>T3gDdg#&| z>g1eHeRjx}bg=-x6P|OwF)f~QZxm{A%&3i>ab)32kJ=@P)!A9=p-y+@l-qZcarKS| zR`?!_r5UsS0u(6~szxSUR3kzpyn=Nl5Ci*uo`He_84x2S!In;^K`Ltgt<-!R+DFX> z?B60~K`O5n@FcTX#!8Pg$lb8dRx%-HrKg_*jpyQ?d|W*&9YQ#R4+Qvx)lkZxShzRw z^Gq0Ef=v??`Y_~1{x8WLX@45N|$l6iF2E5n3wpt8sMjJP~{@01)Mu>DV9gDOg-#@1G9F? zCZVDn;!gUZ9^rI6tXT~q$j^n!qsN*xZG`aCqj){aEOi}@ zC~8Js9&Lo!%e6+-<4Bf{RW5VA-Pxq*Akd*FeUG?uE=8!nHYIP|FdCD@F zmH^tsjG@!=1hx`3Q#B9C!BMgMxmtOnM(Nuz$lXBa+{}GR; ztle}WrnjdM!8uusG=dhOVARbEyMQ)g|i4eK9s(%SV^Li}^(dBMV>2rQIfdWGoQ{y^a(n zc&;vsGA*h=v!Wz9v_s063Zb)Ks$zp`@6~^)>bYJ)yN|bhTvUP{%O~yBi3!%Z128_Q zkZ251Vh3ZsG5SL7x+dV@cNYJ_zf2D0)Xot+KG>kkHeYqOOF|O^)!`G)W|v7Fqix~T z?vd1g=wxlz;y*DTx@13dwd({jiB6BDs(LpS$=^cDL&L3A+%AQf2KQz&av1n^vFp^1 z@qKV}YkLE^N_rC1jrFsvLzqhI3vdf2!vmxacU|k|J{7Wb%D$+u>rOIDHSDpy8Pl0e!gQYHUCM86c0YGU@hd*OMWoSzy!XY}`r5i4xC>OQuh;R%ty`goe-%9nL z+GLrSq~~QKI^>h0xe2=#(bab-6`-l${%VDpySCfRb0>%uc)|&fXnYXv< zS?n??t9$om%#?97Yjm=o$DHmRNVJB)Fkka;Yhi1F96+=c-O5W)tw;HcUP(i(%RlFyqx$Qvg9=2&ZDW1qfDMT(Kb3*#rVO9$^cbNN zv9-Uq(m0llFI?W`;VacmdvQRhw^2)^6x*KkAs>T?pEPv_2HzOG#Trp0vGZYc>Z zL>T=N+8l*6!Zxh-j1J9}D)7Y^ZI&Anzr&WDNMw6sScC|@X>_yog$g1lkHK6r8Y01a zU}{A3%Ept?07Ed~3r3fvdO3D_rLLE9O4v0ujph+CAkW3oP1c>fCD4ltO`l+JX2O}Y z)+ZXuy*QW?{f_pn2AIiA8ZfM6b|wY8)?;k1$$_fsu;#4a>ZK6I5Cfjz)`Ak4(cM81 z#^_oajAHNc5J5_<5XKwr5Y7#oEf9@}oEZ~=C=PI9fOyOa+^emcLhSiISU3Zp8vbZzI~+iRHc25F$?`BilW3aYJ23Ll~hRn6?leaCD664#*E;fCIvwHaIY z^6>dMUiPzwSPkz-pOvil{LmQc$?#?G=vFg7MG7{VP-fI>ginIUDDFi`3+gIasL+i> zy+=|3pv_()78|a=J+i|NE`Pj3;Q0~d}h0GETGB*~C$+V5|G`M}Y#iqAs$m^{=ZL&e{8;*>n341MKW z6cTEF0>++V)cp_@KRz8l*+KEB@(-7~58Yi@jAo>yjpLQ9QZy9RD!^y{srB>nRDu7v z`VTyT)Mp#DY7Hr`4_Ju;oRCcS0%7uoE%?^NfYe)*KU@uc8h^gK1+7AE>^m2_3`kg z{A@0=)-nnF7?0a4wBx`VU7MHx+Y1gLg;?W~DzuqsxIC8i{iZpntEihJK5KN>@SlW| zUctESvz1XJbOq%jg5XpmAq=ax;8O5J?2EeN%qy(m(m8)dS)_Zobuxi=;kF*F{70pt&B5>c%Z!)?#PnVL@e-e|n35ZOL6 z*cAtJ%q81hh=QaEQkyn-+x|0~tqO8lD=(d?zmq2->m_E_WSu461hCTxrH2>K4JFb# zFvFVYIgLr+OlkJcr%onsqWI*EKay*MTqLlP9#+Hn1tma*Bw?m005C$Ch$^|lH-$b^ zWfql)%WokrhVr2<#L3C|X*{GOl8^YQh(M2iMEiz2Pl|9e@hA`1up@M`fVoh@c%G+9 zs!Y&XVBn7EYqtFC!VYlhx*a6o2UdvPmOEY~l)RV8UuH&k$E%0kJOx~fegX%7GwVJ% znvQTAtWxLQM`fCRKr}%lz$Q5b`TIm#zKDX~+!MjkVv&faf(-yq%e4(%ZyS-JS4KFr zqk6t4?j~Q!0R$bE7;HBOsZ#^R&5Lmz$xdl;oj|Kc1a^R61s|Gp_9p1=p3g+Y!Y()b z@Ti#I)$R?7RfxKuqpve~&zUh%8b(Q_9krr=fS)c5@|4d4TiQ0q|6Y0L%>w#48R$70rC=^)=X0O*r)kq&RS!Q7s zuY3Mn^&o-`WrNTqY_z&WfoRj^c?Nbc>`xvk)+q_l>t_#3NK>CCB>wPu--inS4451O zTNuW~+K1yfqyRF%B6ve`hx$(A2*rFVdQd6f8xcK3^wxW5_U*E)mii3u$Y4&MCu;ae zF1#!yPS;<*vM@-@2aKVSGK?MjM3$a8RO?URtfLWlPa#`f#8vKL92W`f5{K_W;6%p0 zUZeuX>yM*9Wx>x@asQngXoL;mt6QCwLNcu#_Z!C8J8xdBgjgu7a_Wzk~aKq+m><& zwt^3TAI+8LK9&PCN|uRw(z&b|i)w`^V!S$J_FBSvcm@hH5hP5%qRTGrYVms!(R$F4 zAA9%8Om18>z-aJlB?pk>Fl7wwc(f#<=7*Y=6$G8j@YoYsnn4L8rb$w;t5&PEmXM|;%mY7r3Pi&xE1ABr7wBBhI0hFj^HaxbaT{rt>uIc z{Zj^_>0_HY+jSZ|N}xB1?xj#_w+#c12f= zwiYg#Fed?+XungWaXtQrHPwG+T!^T_RG0`=s&2AskLrQE97g0e@Wkct9)vg(SE8~8 zi8NNKM6u434~BQnWoJ^FV^M45Kb%93H~YyWK;w6fS3B}R6F))yl3(NX^q}_W?4yo4 z4>=0>Ro4a&3?F5&5KNfl`L#&JWpRNY1}_UJ@)3TH5k*TJ7*E1Rp%E}rYX8dztw-b5 zf=kj-ktxLSYXRrX@~%@bj2iA>8{7bf{csQ-G1;TV=O+7hCMMaby3}B{1nWRL(no6h zbv1^nQgWvXIYROf^_|G0dL_w_(Ia2({lE;HA1Efnx5i6gw}&Zxx#8M}GkDUCyBV@xh{ zR90Q^&`^T#Y)@gg$rzdhpBBTaqT~W77$SxVWtKE+x@7KrsnwT-q7FY1LqYzt2=W0l z`PBR*(Y+9?NW%kdk%)NDA(?jG+SvZWEjaRbB-yrpjyr{AEWoaOTHaew*(e{UZAt++MS-njjp(B^Q@WkkogvJHqReq=^q7zqPjoL!*oGqlu`Sj76TJu;bSH(``ly z@`RSSvOCAphM?6YJ=0JD^iZCLst!G*@$N;ItrjFPDm@H3Q-U;TRYsrnDu(vW-P}1bpA!#$P{_Li|wx18J3sT*5itvq*LI zc(t6{*_N{?N@DPB;t!(TWz_zI9XDIq>i#2|#cE`(J1D2pXDE1VSNU%QoFXX>5Ll7X zf{{c`PMVqrB`|%mz2H6suX^}w$!~OQ=+>HFSQmyU3-&2w!8iTzoBB{#>fl|>N0&FXj|q!# zSz=IbOO=83ds?{Ncnmo}L_2TS+P!uiw_0LKXjQzD6=#+$lkN4zflL?fnM!qDRBdAM zQTx&A96ENuQqkE_Or=JgH176~-A_($P4QQmJIx7@@C07O?gE+=w-+?=V1zL?7c#K+ z&>9nB2#@n>GpHIGPuy#i<}qdYg|KLNk}PsayYl5GVH9*3FE&6wct_%9Kr1rjObR$( z;Pj~TKd`pd6raD<(7=R#L<471*n(?JWNt7Cq<8@b5c`b$hG%>bT5z^Kb*{0bx}$;8 z@a(*3`wXWZX2~V7iUDbWK}2GgJ+Yy{WPsL{gziAqa_tZ^Q4#Z;wI`^| zLJ8Z;uL%zPb1tREsv|BOCD9XDLrSU(6O^y0&J0Ut1KOktn7y7tAS>wm!IY%hPrpK{ z1>kw`KAp9H8J4OAf)*YI-O%kUl01b!P!SS32B#FHhf&SLQ+J(rss0SqJKA#?kd=rp;qb9-lmQT_OVNYfOUIY$Rf z@6v<0s-0Qe(||NRu0*;hd=S4EP)J|_iFe9k@ugW>HqM+(u&ZQzsOUSc`uINQ>LWv#*Af>Y@j2sN?K6HB`Y*fY9bYtRKmmz zbnPH6R)CaLG?3-Xny#~51#~q$DJc4wH7XvUIWpbo=#Wd?Xq_`;U6wJl?tMVlP5`-G zTHSS;fNX8s_c;`Ua5;`e&Q%C``NEKaQw%Sh_c`PD6`L9IA1Z-1s?c-hSs%+~9fmxWeaD04zZTQWeVyGCq2q zuiJgV_o4igv22d_;mV8;5)YjCcHT8e6r4j@GQ$gw)PbS$8-zUD!?9QW%)-f>GVYPyMyu&O$jX~&T=`MsAF?jaNz<}jS3wf*+({aTIu@T5A>SsVa1nad*rEhMM z#XN>u1&Q5>O@~MiV75nW(g>;l`R%IihAMLV6ulF~g>2zCRDK2pO7KJ-Uo$&iS-*jg zXT8^@S9$VF^tdwd35igNgOVEwVqDJ;&ZbF#ATIv%mgJ^S;CapdWP?fVgFl}idDE{r z$IC;Vq%8iwZevujb{}72$NiDEDDgY;<5J!R-sX+|Q6K<-^wAeR0e797TaIUee1ZW; ziG0_YdZ7Aakd!gRO$@AnvvT(Rm5nz?C^WTsj&SAGScvnc97-=Ak4(=UOK{q)4!B@~ zLJ3paOWVy0p6~Lj#AU$diF=saSi`p(1^%I2neGmn%ZR|tD+GUwrOy{vh&ulyV`s+V z7=!oxpK?GLMptk@huqdrWhBbfTBWc+zRy#&mH`O2A4;v};8}$$U$09zmtw;v8od-J z@d5ewaDMrv_`YlWJ$iF)n@a6<;r9UhS!CHd-;^<+#g@42EA0!vA5n2wzYo4;5xe-? z@#QeV=jCuu_s4sjmZM64v=}6;;S0tt`)~@9ayQ`hIpF&(jqv-yygAbz({=j${cV5D zoLg@(1~INN<|<-7kO0r3zq!w^3d(0F0J-x@1rA@h!)po=Y~mC(XWNYuAt7=kJ3)Em zl*{b@yJkPK#kpmKp6ZH#nQAeRcOkbK%;{KbV@d}j@YOMOdcNIm^Z=QMpY`+aosioA zELyoCG|mShF$4ThModWb`WT!A|2Fyy?j;hMTo`|VraBefe_sGX@e0f~5+FZksx2&o`ixzHQ>updqipA;v|=LvZXn13Q1u1@oW z+&@6i6dY^>4Py;WRf|MiHX#VuJA6ZSs!68K^@a9~;RptHJIc$hgdr1d-w^*bnEqGC ziGUPm8vlyew;6313Tcl;7n|3?r8wp9p|?XP@V12lGJ=&c@C^W~NsSmOa<+5k z=l~dUI5fEYB#YnE)2q8%8>}ySFBuqp1iL2GXt(Ge>iiCcVw$SRN5px-_YY+(`V*=m zDB^@yHE%_k?8(B-C?}z%*SWa#2uy7jC-jDvT@GUjcEDp%3OkpJrZ6sZK5F8iwgix=TWKg2n9z~E5E*&fMDraab7??T4_Xw%;a+BHIbfFbaxfMz z{9O{{(0Sm;kO_4k4`;P+bL2vdjkCIHDcvCo%RTySgZgEWy(Sc?i;g`xQjrk00G zrxC_td4oG_Dn+M>dLAar2R|05kgq^N$VWuJg)$d{V z(P3G0FP@I-n5PD}EEldOYIsD@eoOF$-S@nNJ92IP6pQdKK z%a#W1{51x9?`Ka}-WxD86QGmMHAEtvzZgTS9Ux+K#ug{vHC#Z(v>e4Rm8Cyy;-IE7h}}JBgZJ zFN`%LYv~AEAJ8T#i;%;jyJCvP=Z*YZ$SNeW#sR1J#?NjHgH(d!yAO2P$Ic6M4577! zE$j12pa0&Oer~n2;=FO&z2|Ap9YxS!NoTIu9>&VP2mqj^yXa0sDRz0x2Rtpxq?_n0 zBoW@_#_)O3uwn*H(w(E+yo4Rc! zTX$25=+Hi#Y`U=L|Kij_Nx0Ixil5S?RX=<@HRl|+cfDhe5*K{e{cg&Yx(H`w)Px(O61|S${ zbs;dQ*YSi0^=j4ad+*7{eemzuKS=Fsj&71>01ROZBcmZ(vq+88E)w}HIeB*sX3^^& zal|ox$7)=W*9}K5VVFzJyySY!lYGa$aLX!DFUO3{EX_P1vVgDwWa+dXBWt&N`cOHJ zN+fZFyx0>$K-!;-`lOYyHZu4;RUljbBFw7-|1Hd%(ms~&w*6YJKD!7e^QygDGF)|* z0K9t|tJs{v;m>c+9qv}3N0};9V*+P|(oM^}@`qzboFUmYwUM-o>M~9)`_J6xdNf)p zJ~=d~Tm8+qw9U27c@8PyXkFqL58d@wtRmSP=X$pEt?gen6N?cm%Qa_)?9C0T*|Kmf z=y@}@{qT>hznMy%s>(AGXqvXnLWY62fcE7i3oU&B(6ado<-KFM;bXSDfytn1=195C zIMZVDk5}>dGOt@;+P?#J{m0(hTF+K$K=R=TZM*I%-~N}cOXBaO^Pitln7yE(h5LU8 zBsJ1jxKI6Sohq(RVoH3hIj$`Ibmt!>d&5KjU zqS&DZ>ERXW9-kpcpzZxDA-98EL|@3i%(M{DhUt-5$k&K4?0-U>0lRUqTM45RW4g`~ zicpL0RcM!PtQo}b?x%7xHEXU`wGr^1EoGUG3512=2`<|@De#Czs!hOWr;jjFI20xE zM_Fz8aOFvh(w5`~>p>LmxbgvXx3O~&vtb7>2GDr;*zCm%V@o*D5xF;ZF~p(ik{&{n z4%(GqZ|JoX*wh@9e%4A zVTlxScm3lT=k6?05DE~p?sa2Z!QGYRz$3*V!g}zSdGEj5$UHH(0?&Xd_lw5dT}9s| z^NqOGM!x;@=4or?^*ht0b>B8=tKp4k%{)9?!&jmRqb2();F3Ml`ER>|*Q!?T@O|d< z-)=ZhRnshXEa!*QQJQ(Tsmaq`;(X@>&P&Imi`BN%OvNb2uBCry%y_TC#HgQZjh2p& z^a(fQ5`r~~X*ZsagtY-o=dTv?xsO1%)BCm88_m37h$5u8@v7;EYKtX%(&>6eb6>NC z+4M)xI#-VKSK7DS$0JwzG|g+@G0kapJmaOJp-ppNqDime%ts(&t2r;y#XE4y!`gA$ z>mI!}2X^@)_wkO#dTIM;AnB(z@NbFXH{v2-$jAb^MP7+={}%uyCW z%R_2#PLP}N>W^_|o8^4lkI0T>2D!?9=Z8!cy<>?I+`S5jZY%Zk35i5%Yoh_h1x=5^ zrI>|Lmx3z616RNkvD!)Rql#R8j{7EMZJ1MtW8l_Z=Bs|^)?9tA0ReLD-CD~uf;N?|-3Sb=HAxh^@PaE(?LC?ixr}U&b~}WPR~EiR zb|x(4?=384G=l*q;70Z^@94mmrI*}244N%ph-1|J{Su%ryg{ygPwlhBcP>SKuQkRu z0dqURY-0Nt*cjc3ernVRuI-78r68}yH=fh{18p{_gb?&s}&wMA-p%pY9{aKd%f zkwIgD`Oj01xr!^le!Y+?K{VAp!+7SGD$N}UEdIC`Jk^P1y@KK8s#t^7`sc{1c=@*L z#((p_fUN$Zm*+T*Jva*aAuNtenGe1_dlo@KmU(Hk8OyG${Kn+;NPEFQtKv{Kd z*kM4Mk3G_quDD>ePdk}|S-N*^qG(aKgo8yU{>o{5spZkBoc)UW%8OPW#WXof!;Y*I zHU6a#b6p&$GE*#rgG$6OJKye0cSv#fuDwbhs7XB8Mz}TRj1ebW^2^N+Fr3ygg@d+! z-15`>6yuG5-?S(cPB!yZmq@#27!j_JiWuO`gyUg^CgRtakYY_;TjozGU7n~0E0^l) z;Trzre0Lw-GUK9Vj%v@Xnf@JS>MjBTrvo33q25TK)YPRqNK`z?vN2lK-sE})Y(Ca62IHz~8dnB+_&=A|lX}kh~ zp5=@D%D+eXIu4sUfL4{pbf2anpoq2`_8vwYi_krQ@nrCZKF>x20;M&R1+W%zWaLm> zP7tswD~jicJ-`3^B%dC-*VX=7n%4)G^NMT!$_MhOg_}t$9<9<_Gea5H2lk)0;h07D zY~ICA<_rPq6U1wrLz!W$l|GLb**E-sK-rT)ds&fBl^y=;S-w<~LdvKi+8+ zh+7GZzJCwzVOn46d;RT+24KIL5gb`;pU9VAv-*;PkGrC`v&_(Qh;39AHt#(HHtZ{1Xdmo4k8w1i9-772A4^s2Y4#--iOu8}b&J;C za7_7-!Rjj;tr%;;dBb>=lY2_CKEw^SiVJaTu9hGuz3y0!u8CyO03LFGFD5K+Gqq~) z)NVisdU}42udKnet#!Z&|6M8^R#qT+dO2fw{sKrfp2F`Tk2%PX+S)1;q>0qfq(%{= z7Q1mp+F_NBoq3kbK$_}-K0B4byRWy)aHmWi=cosPCWBRM^g`uv)qXi)dmxUL&vtba zc77>^(z(YTv5zI<05Wv$`sxecPlfu#pVgaa>k^Jrgz%D?@(VYx?SSlu12Og|q0GRc z5T|^LXUARYyAI}yWYr>T-#mQgpc*!qt=!T$Md*fHPsoLo-QI-Qf#|$?@FHIT>p_fP z`wJha_*=O8l--MG-`ahPkfZN*{YPq!*3}T%ECJYtv|Ah@;3K^YUCt8TU*HZ2GL-Q5 z-B=vX|H#kuVXExbRRzC^UCU(`h==(V`xt`7lozC!^cM05>h4xl${nNm1WTto$EI$ zZr7K-6=Bm#!%B8R)4dy_6l_f=c^7(W`^!7Zlz=fl#9wF$f=TYC)8x-q@rTB>LnoMEbKPJczHtwf03O?;Z-gf3 z040bQ;N0{;kb&wQ+aDw5Yt!8HL}KE7En-e8>?x%YeuG66*`zlsr#9kY0KIS z4AXnJsr;TO@XbCva+pXM=W{y?pQ@@X=ob5YU9(fv0rrM~Y~Qk`eJeXdRUd(gk|_qT z^G?4y(AAZ}i4oqd_bp7chq$3eq{3` zcK5rSvNg-=nDYE{y1EV*O2u(mzxnH?@BB8hUjz2vz1CMc)4sD+I4ZSYVL%dN0Wt_s z!(~;lIild*e)erTZf_c8?8y|ae_`b*ZttSaCt|wRpN*!EkhQ8D&3kVA zi}4A*TC2r)8AW}I@nA@UHmrXEjx3W00|Rj@okzv$#~AuqsE>MBticQ!ow$1=Y3{?> zb448*M@=kR;^oAxPu&Xxi^#(@ZX9_5aIr8_2smpvZmcFMxQ!~p`2SvniZD;@A7Z%- ztMIgQtC9bZq(uUgN2d(og*7qxgS?IR&C4gim;(VY{uaCcj!CJoV2|eICGhgwpr4POKArehC2HdLZlfUN;w&2A`TOo5pXt-vi*)bP@0CAz z%4<~iys-Q8Cm#&vv7@=;?t-@IOJa<>*U`Wr5{E{1IGkf25Y&tzegSUl^xzl;P!<^l zweVUv^FHM99TR3aK@E5hegvK6{hnWDvJF3;&~S;Je@G$#S~W0jBapX{e3Bc_Ka7(i zSQ!Y}posfqnU~#xa7<_U0<`|0n_VB;O!c31r*}##>u&%RmPZ>}gn#e2Iww2&JD!4t z1zPPXYYP-80>;!vfFDk+{HAX8b||9Bvv1Qg{1X`muv%!mQVfnFWQs&6!02GxK_v-W zeRT=VI6ELcwx-@1?1g}9>3vvgo$Q?0AySirDoo?Kz3~{bW=w5uc+5((rtWyeueF0_ z((!lLn|0ECmsBngLPeCY-r6~na(+O$L}@%*;*-xM9ZOEl5u~Lno|TG4$moiHRLLT+ zWiIOiY7 z`=2`b^Pf7&<@Y&eO*>qfdJ>*+9kd>Ka5@-!#}Vp-suvBxO< z;?hFH1*6HR#_>c$>WXTZv2!GI9(7Syki)xmx?U}p+SSXb!H$ZMdNof-i6>hv6cwIFmk_4PRk*5$~1004)_ zrvjp-S78RV@EF8Q9>1K0!r}o{E2XX|XCn~N#*zAm2)ndnsGHR6;GLUGTHGn-`tEwT z7;>I3K5Fg#dy$^()5RC89ZP8yW{%|eJcjwdw6o`S>q}W8LESmX=4V8^*1E<;x(=h- zTErh`!}>Gn{xnTBbeORfjzxc zaH8$~Fj?cUP9hZ_0ZY*28Kk_s?d%{INfB$A@?Shj2-MW(S zWt>$XB97WW;VYy5b)61quW<5(Z)}mmqoDuuyCZ92ZMtoA=U10F{zJFn5o8pzbC76e zvnjy?QRph(DYNypN2A2==Rnh?y*Z&BX|5N~zuT3+CRMzTkJ8sC=y=l6%r4Dos}FO( zPhYyQ-v`-iZ>hrrerdQh-=3VbP+G&9xOA=JAF?N-l=hbl2 zj$$dCev_W(#9qTjAZ}TDEF^4Nr3N{MUP7@%Z7OBtkD%aK>{0iVzL~=yFw0!&33v1C zz5YDz$S*8H%l+P5k4<)PAYE#CCgg643!Jb&+#(-{%`80xsO|b&0CM;kw;OsB#I#XY zc@4DGq2i8oBUGo_7M5mTQzO+2e%1b|lEcXsX`kP3)_e7Fwn0!HPq{wq076(I+coC;YV zt%B6CyrG{2yg11YK30xsw@79tIq#v^D2x1TyL-iS4ELoIu~cI&_f)A+(^voNbU2Zy6z)-{Z)l$I@bk7+!v_8)*#W&z zCg>oE&>N$)W4-7RPrrv^YMDB>RI=wuOL!!7(n(k%46a5dEJ0-6kZ;H5vgBIkNZ8K>-A-;YQw$}>TPW6 z?Qxg{c)C3L0~{@}8fg14JXILDcoB<%CycUgzE@-23`6wJ3BKLPct34Xi+(t=-U5@w zFJnN0(%#Mf!Ryw?<4a=d5^FwjKI!d}pv>2!L?JZRK!s`ULmR71)Sb&Ynxc*rOjqcf zT(;Py1P|NP^fUV-|1Nt|{v6M307X;rrur4yx(Who{4aL zPdOA3c9gZIcr|k+vc*)6tLfC}i zBxOBwR#?XIh@pvx3a+AuPf}LkT+E^2&F~qdEHE4ublV*V>hm@qy=Y7Y*xAq zmUnQFNA??JJ}{kQ_0zUJu8*0L!xqTKEH>#}G`0O*;D|UUS=~w?`$y>ac2Y)S3~H=I z6Tq({@X{dMm6R3z+u7j`k&@K}yCTAv+!E5sFaSeyg&R*IDNB?U#Z`6_0ZZ(`_LA$5 zAwau9^xZz&1!r{z}y0(V+I$xHU-te#Ip$OxT7TpBIy!s5_+lTg4d3!-^5LB35;QMuno^9Rdoe zT-p?Nkp*vdL`K@@*9x;b6B~E%moJvP!HKj!om|IM-0jiNhtS0hWh39lCjD)G>509S z4jSvMIZv4I>h+aP%vb6^f4Va1GqHTAX+uGh%Z%tR@ritRgq}KBW)$oa)wr^LfB=T# zZoK7kj5ta?4X;ZlN}~}WZtP>9v>#AU!8J6@%d!T1BCf-vI0p3)?238PQ4H2W#TlR3 z#}E$549(&>m@=mqlX)tlVbbY}JBUqnx|Ga$%<8^0JtTU9~!M?{#o& z+n90XRCVw2;v0r?3cu$@pI2X`(lFJ{#x*K5cQ5wlHw}q>%29W;<+Z%^_19A3SUoT_ z!|Ik$E0brMPgyW&6=ps%Zv`+B!r*^?=5=AgG7{x2FT7)In4!s{4lxbg$RFj2S+yq?xVu?79GNIAXEp4<*~)wy0H(wx!+~-WkqOwJY9*h3 zr?|P4V!VSY%b{(>hyhkv>(nYfv-xo=9i+pTx8VxS6m6FJOb>j^)nf7*^k;Cs81FpL z!Wgw9@=imeuYv_P*ch5l_v1{cAVz27lsVCeHcytx)@&w7 zZ&@0`Igq$*zdq|PC9SXzVhK*zgsh0#*fo@tqOQU7%}!?!80ZQT7l3vdT{1XSm5o(M zIK)Eyu@)S$o66*eAw@63Q9RgM*mb_u4tm>sg2fe*241)@10d8R^c7mm+$6?h9Xt=Ui~JN5eQGAWFaUdSJ?4tnH>b zwqGv!i_V(7wbmXLAvR`;W_ylk0*!>h{%I+|G}?TT;Yj0g{YNrX9(7tib#Grhs)#qN zd~Ji0kjD6jSA3lrVhG-26aS@sf`stSZ zXuTq<26R|{u3D=u>)$~y+UdULRY^~Q3U@RnT$94&BRmN|y{`1P##*39e(tZK!|F;* z=B)7?z}a%|Dq3RFRvAYziXXx^{(Z0Im+WC$s3-rQL2WY%4uVU(v~Lva?Tqo+2&T4a zuuk6%;3{=HGfV$V8;-})A1gvhL>x14 zhZX&Wy!?`~q|jMpCpI@zC(WC-inerYd;B@svKP)M&P>NKNUsRos8sim$?uu{vJ-*c zz*t}b38fX5eRSP-gUzGMNeoz?kU|S@Rb1I~^n@Z$M+MC~@I29v{@_`f5_38f3W-Py zi(HjyeBNiL%Lf+<59GNSj{9Y6)COtG-07)mL|yr+zWZHLQ0`%{(N^bb0(~O+ZizKA zz}Diey-(Xq9>s&TNL{*fxq)sJkXxSuuxN4!TfEQK#vNOj(9imNs|x?%xtcpwQq48cqRTlj%_xu{#$n^_^xq_Rf*@F;+_;a+|Oun|F3w%gh`+JkxlX8Mj82 zgia#s8c$Gr^|l*~Ouphyi~dGx$}Dx4@odCSfIc>Z_9^y3T8V?b`1FGefL9NmB=pUO zr%t|}UQR-iyW3gqK59BnH)0$G8pp97I110tq>tRx}dS*y3pN|f~+hV z>}G5yILEZ`JAWXYQ#`OJut&b6L2f3I`pK%hkf>k}zBk&C%&2g7R~}yLxi!5g@ZKYi zwhOTOgiK4eV%TW-3p?u#kXg#c3R08p z=-Z-ffG+_Fd41Jf!b^)Z%;t0W&1y=;z-T`UInC2P4q7>=8bUcm4g7dS-B8S~nYpn1 zxUD%}r?@r0-Gk#YRYhWqDn69T)6aZ6y%Ir=2#R}oaOD-yRAf?AfRw8V*{U-#KdT6lD1(2I?jXN{0HMY20jfSqCsYkfrQNCh|PU1G)hHFf^Z0F?s=k?|Rk|DyxWr^Th%>=r=fQ@6nO)9e=Fqs<{vOu^7R-AUK*71xX%@1@ z%qbPvj@pi%gA*cv#F#@f{Tx0a5@pvcxMNW=TwZyG3xl1qU}3vqi?IVR1eC^~vrTuc zez~Ygy+Ol1%4HnvfP5H{<2nt;O%I7GqRvAo)lYr7km!F3v)8e+c?`LbF0L)PD)D@L zL`cqxqIIAHdGL|XMp0l*5oS;wzE@se3V){QL)w$g}8bKqpQ>pLNqzGAqw^oU0+EI=T;O> z384$C{$ozf2l&>X=8^GHZM1$C6C^r#*;&L4qwACse_>oWu$_B-@8Mh5GXQ1ZSNslP zPK37UhrTmak8h%5!0RV0rfNiEgU0TqTXgA@h+Q)PR<(NwJm0gsU`%E}`&v|xXsn@S z#q}iznFOB}edayCdqf%AE))*;73y~KbQldC>du{{`AGadw@l*q75_Ff^=YBQ(kCy3 zo%11KJkwMC@frFIHxU(cxFY19QAFbk+OhB#1wE3wv37XqwG2!yTQg8ye+OOpFf&&^ z#MchMh&O?U)W^I+?n)W`_)nI#Ew#a?;XIkISrcr(L)lc}R2nq|fa0fr6Wm2@zKG*B;t$8g1LV~I@w-L*-Sm32HP z1I017Xq~;wV(S=b?VjH8Z!TsqrJ*OfVQwI|qh1;dj7?W6JTtxxhQ6>vEp$spg^VU7 zdSev3pvBj$Ne4mK<=czt)E6$sFgX$b{5^C|WLJsF0=1UrWH)OQyvAMEkx@^}d{$kc zR-h+U(Nam-KZxrVlnkbb_V3`bL2CCpS)HOC&_wGoLtQD7ljzl>$RuEt%)25f*FY% zMn3I{?yQ~I&~=lZwh37m6EcoJ`5r=K*VoyoV}P(kDoJWPDhM7c4(Ew1cAI89YPmOe zY6j8=4q+av`Wvu_{OU?pza2)wDr0EGghymuG=E=K!->Xs@I3ibD+^r6bXEfpahB>l zYkuW-uZZQOZ8bsSh4egPg_dadCAwxzYEE@ls(tdzC9sPJYR7pNw`z4vN@p5zr;wpz zC7z-w0}a1LTa5~K6tR!}qEM^HrZtgjTmEz2*rs&qcB-N#dD+t^y+0M?gAS24EQcpN z2f-Qi^0N!AT|-hn8fp-ay#j&A_wU3XJwYq+jANhM7O@>e z;p!bX*7(z4Oro1JRd$7OVY`ZLqZGa_yO}wvM*(Ku%!|x{5@6im{oZ2_uwk?a655a@ z$+hA1>Ahhz?}MRy)7rh^Vjd}$J;T|1C?CGCH?xhWXM3bEJ)8~w&#PWwO_6r*PRNT{ zpb%{MAJMV#Vg9KH`#YenFGyHpLq_-pPpo!PKoK_Q-vbVduz#1FwV>ATABB(GyKy9+ zmxBAL1!XEDj^wz4Ssl+QRx;ki^EwOBrl2B{8bNqYPQ*z z!ss3f_`UY%wvB?c3;Yyac%9b4Ca~$gd$CD=NVLfFom#2&8}O0u+w#mjxeaBOrJ@76QW3TB-fC`YtXCFdyIRhK@>MvW=})X%A^#@SK_M{}>73k1=9 z0Bef1j?AL)yCq`xTj+woVM za2D2;oH#U6K%=TkbVPh^?Ycec_u=GL$GtlhK}n^W+9LZh4Hh^m^sqO7Ueo#7XZCqP zYNqUMja4)r0D&E%|I!rF_Uc(z_ow<77I*t$NAv`ohI1-YEG@nBS-JOl^WFI>Ok;*0 zVRUg9s#p)9HJR`(e#TpVs+#p5muOs(^%pSo3l>htBPQ8#q)5=^7kYb4m*df_)Tw}> zDM&vIh;k^V*b|!iNlX=0Sf;OCCojoHHjsCpKyH8yND7Zy&lliF6+<;1p?8#I-_T^7 z_BR3~GkSmH$6854INFuOycXwd;5Pa4v`84w7vUVfOi3A%dV9dxK}eURUS0iMD$tE8 z7DBGh6@*eS`DcILUkNCT_!W7X#ifuv#56m8+JfATNNbG?rw|Z%Yw2O^53=Ap74$@_ z>>&r{VxWG!O@57k;=s4pW$3_;S(asR4aMBbzhTJfykrEK1)1oM8rGaOwsjh56St!u@!&#Xsy#fQAC-nU z{Bkp?VrBFbDz?uD|7KPqvNJteWDK6gEU+|F4$>rIY<1rmyZXk9E~d_(Sk~1TrMH3v z5<$=Ssi@s|CKVL_FP_wr`l?e;*#D!O(~P1VdF&8*4Fe_E@K*5wpSpzDnYd(O0o6;c z{HIQvLgB*mxhs92W+M|u2S*l!t2B%@R2MP#T8vnGZC%j)! ze5kRcaadK+V4OVW z;)I^w=RDLnEOV4lmLn;;$*l?^vk}*(fB{3|eAj0Ld36-#HkvdyV6&TfBLz0tv?3zE zws*lhXk2qPB?uiWS*@NYMp6NriLfM6G5P1w&T{7bQ0)&J6MLZ(exk%&q+m|ZZp zZNBGk@Hj}fB!WUEs~Zj0$3NCAS z2DQADt@=;@mp>6vR&n(S;#Fo3?S?soW^K{`| zs{xe4)^#{2`_z9(hV$A_^O&>8zW2a~1mQ|r7;SEStTV-ou zj{^-Dv&3|T37H>n<{hE)(AOhe*G^m2yul%y&~6IDPmh)=6(@;Cfo4WaJN(R1k%i^1 zEfSiCw?!bvWvq;lr%ywZO}^u`MRh7pAp^(OUIfUH(@C|8QZ9Ki|2AB2K08B+#z=-` zQ&|!VVNE;RC}wBgDVz;{acHlLQpv4_eZm!rnaOTlQyoU(EOUHQU1`jSH8d?&CD(Ww z$AU~Et74 zP_f}mrS-gwbmg$(uhO<-s(^YCN-8y(^PufU6NRq&!x$9emD``U zDgqs@?4BDPSAE2=jdPKF;I6Vg^`Lk41fD#rD8p;iZa~?UA=DR>&G>qu_12{dIf0T` zW)D~0lzLz~F&}CwoO=1~U2Kbf6)LX3wHqbg%y^qW+G=y{1#J_GO5690?<9+&JE^lp z6qe}KkC~+eozb{Vevcrq_fEnBs@@Y^{yaS2P9D7TBE0zHqMBXG8? zlK*2x0Elnh)|(aHuQO&+p)(!aD%DrK@kNteKd2!?mi`-E=7}(-aoSO}n1pT%HmcA+ z#VU&#A1jQjq5CInw@yUCwXCCAPdtGHR#~lme`C0q*xy5v5Ec2mDBhGQ+$2uHY!PLW zzAP{&;VsrHdOkm2L2H$VR*WKWyu{?y%PiL(z^z*6AD*9lS@-^~iq5uDljDUnxScs6 zoWUhzR23?3mG~Ko7WRZ4ud3R*o{9JI4%-ut7~B1DLCiy;aqJ9)s1B0w%2=3JA(*(`LT`HpP?F!Iort|C-Q|;*S13ZvIhIAF_?HTEW{FW*Ndg zJBySmEBikCw+iSTjX?HJ3xQ?oEVDv)7u7i(_KJEgc@9SQA5(Lk2nTlU_fONFNpja83j` z#!T`Am(Z~G<0nVZwZQO$zxI{ps1Ij1GH!Hf+(UOFrg;xU+xoSHE0{WGKk=^fDI^h_ zn+CHp?HfmQGr>+<%TjTz6p2dPK38m*-?o+zHEj2_R^E1W5=?Sa?n0b4J2YoZIjDL5 z;`1%1EoIZ*krV70vg%m$btx7DBqVXj9OI6oycOI&WxDP3BVT^{RWDgCeb_@W%VZ^C z8@|9ONiqBhMHinHHhl$yl#VGQcXc|Pzi%A5Tp(M9v-<2)$=B%L3B}Hx_%6__&cPid z?mo;QsaA@&gnv75bLY?XY+9xHl#PW5Ds182bd-^;i6;y4TTo}4DIfs}ID&%*ja1yf zn2J9_x3h*Lowi^MYq}pK65xWx2QK7q+`Yx3)XLWTro0@pBpKsh$rYFIpg(_wxTwHO z8PDVp5i5J;ft-pl62-2UKVT1F#qmX2eiDNB$RnBA^&=B#*{m^f2mPXCyjz`%=HY`w zqbz=o@%f~XK&83gLCnVj7(G7*7BFwfO7m@K@+BEf`jG3M#c3x?xRRE1FQlMfYj8lr zGem~8vxNWFuyikusFLP&1q(^6yn`^h3AkofuTlL&8$-cp>B`O!T^eK_N z`S`3UCuZk7k|EE{{VLd>VqUCDA$(DCYt+%J7%qyII(!o97ud)Jyo;aCmFVXGrUW@G zOBWHpqoDM`nh-W61nBmw*KwBnsw>q)H79pO<1OeMj6j3PDEjBUBA9~C8qcS(^yacB zT=xIU|~h&Ee6b-mO12`69w@Fe@+}FBz^wiz z26*UaVSXMLVqW>WE|c~n%4408?K$RA^W>24)pd{UDAmle1U$dw4pKDinJi*L3gm|tcaS;YiN~cXReFB6 zin8&g|Lg+53v^^_x@IIcOEVGsES}rHf4bFkKap%=P=<^hJV1imR1~%CDNO&=hRu|n zdR>`5v$Jq3KlaVMu=#Rxc^ED1ZzYnNx@2;kUQD9sWd_>?>nepV)kFGRfRnj}9(jqI=-vAgSoA9Pi3)~)(sMbv@%>L zbq0iZYmyrpti~se>D-a2vXagqZUr{^`A+2KYLCwSIO?ZB--oFoPonl_k#I9J(*d?f zQ`tY8Q1We`Nu=dR;FM|4hzdomLR;%%ihVBH>jTv;$>>P(V}0||??!3A!eypbc;Y^) z^1DF*al+MEZ2dM@Gb~LyWWlC1@<&_KDxQgz3ZA63f$yPZnz3nrdd_;xp&GCAyU`-c zrfZ1B-S#=dSGOs28w#q)xARKkJynREP^_2UTY9;jmM%AxqH@199fZJa^<-n`sUlh?sN3S6o$k%{qbekUpsJ@+x>v3D}k2{K$wJDD+ z;K$*(k2?>*dDU9vJqD^b*dr{3_DK`y{tj_Jek=aX;SrkH2|d_<@K?l>E>p@8_KjDp z^813XH#L_2Y}ZYrt4qbw5;s|q6e(Zk3&p1SjWRSHBjo^=TA(2KaUt8fJbI|qdS-Er zGGt+UI|`4*#q5r!dg&rZUS61KDknqC^>bGDvA;w^#TU^_c$S(#VuIsSZaX>vPKfbAj#5G+7`Voo*?=vderi~E;T>de7r5D4s)4`R+#+Tt0r6|MfFM($5d=L8u zmAfi|>YL>)#?%X^rC^fADrnyc1ZVy-T#d4C&$Lxs{wi8Q5f$2y@=E{j{RzvLmEYCZ zK_g9%&x(j7_is73E6ee| zTwK)s0Kc1AfB5@qX{`s)R2-{;pqdYyLxxlh+0*`I^dvqf&v080^>KVa?hTTE%@Oet ztg#0aP)fegtnDdFGi$~wSur~cJ*-irhH>EH`61|A+pbPjp9%CsAZ9s?hinmP_v53rC9VFg)RQhk>?um~+T>*H z-9C!Fp5tiXeGO1}$1R_{$yw|m;Zo=5lSpq7bDa>+oKQV@4|3sPRnc9!3~I%Z;EgYN zlYVHEJ_!cwg#6I!v_~10t=t89ZzdDw3`y*z{_?XtYC#b@@aPLZ>!a2`&9mi6wkoO( zT9Z@C+g8E%DpJ&-s2MnlmIAxCU)ypUu!wOAu@fX$hSuB{S9iHWTW0c|$N4#2myqmxe$!|X-HOYa(HbxQobdg<98$7oLEzT69$RW|z$+45)s53w*yGcq?1sa zvu=m>!Fnif!(?rFTB>Dlm?T!i)vfekUBIjbVu%nXg*Z8*F=;y}e6N<}FkVKwTDTF< zqWB8F(1NxP<^dn^Hf;d>ofBQJ9By)rGoisiL8GB_kp~N_roHErLrIA-x{mO z0w*gZw^g(N7UY;_Lv*aP1l)5`ZFWf;MM{zglP`$pZlbRQAPiHBVGT<#p zBB2tjH=*PSSC=dWLVtYr=^q}<_mg<)NaL*(`4!4Nk2q_;S(8k%lFB?sY#ZGWm%2rf zw*f=K8TFh_R+2^MI9+KcJG+3Waxfor=rfu?4hXI`W29Isj22!IYxA!FI^ zAQoy}NV2t_|0MKs!E++Cy+J76Gm^?cEQil*>&%MH@jh@?%O{TPoG&k zuy=&dWDb}tNAmVd?c7T!^#);3d~D{?S-eC$%bT9@Luh6;No(~wt_wH%T_NY+*k6x} zXYPJilQfA~4yng`0aKtnU}(Bc;!Kq8-z4}?Hg8o`x|}V*<4Rzu@#dcwQq96^!~9nA zfz2`v{3mJoggIN+g*qy;Qp3p>>=)cXUkt+P#a+&A;S=Vj1fhWTS|=+LiX45hD)SUl zmub@87)(x0T*8}w^wjd9&8N9DJIV5i`&?8=oBr4>EC=uX4-5b>QU83o0wNn}p{Z?8 zbE5D=lM=@*lzA#Vy8Rg>#@4fFL8Ld{lHDBE(^Xhg#ibH7bQ!_PfBg~O%|=<|yj9{G zg(`E|gTDQ3V?1B2`tkXN(-U&I1KG-`sue%KkDZ{`xi{zv>4H9|!yLk3HpiMI66s?Z`G6tPL+?!C4A@R)qudAV@t zo%u2~w4N32cUh)a8rM*Nbv6|cS{K2kubJ0nL1iX(LyQCb9`Mif(z~BL+Eg&M^!vEV zTj-KFf6>43)#$4Kdgw&9>Wk<>j|K8kGeE!ci%Z-FVS@aaijRkmn(z6?!rgT5J2M`s zX8sM2-wpVbeC&4(KjwcG{8@0!b>&BM|8)}eT1D-YGmDO0Uz1svAy`5uC){aL%fssa z93#40Ga5*X(4hZJrkd)kU{}%BwC0r!(XWU+=Whz)IIQ6MT|WMb3sNK*M=E~|{&mN! zo>F@{9#95~; zmtOm2VLN?p`E8}S%j6|gEz(t7QV-Q8l4mdcd@Of zH~SZh?)&(w;b%N-^El<%4kP(VM;f)KfXj^m37`D0P|ZU6^@kl%o!ZkUv53yoC)25= z#+#k;?5)R`*%4&5f0Hty)4n65ai$+vbNIF6t99q)=-P7nG_lxu%5bX7SiX6bRGyua z6sZ>X#lzq(Acfk6VzKPVq`dRgYh`W8Z73}FzN}gDYPAS{;zF&urB40Lj-QVJs1+-= zjCkXf{UFH9%Nq|?sXUexBKUKDJ~p!q2b*lYXm43=4?-5^GLx%Vjj>{q%*QMWk{Zju z*ftHn2A3+e_otGy7;j9l(fGtwy_ucNyAOr4b|8mPFh8TKw$Xewj9()kAS$od=nrwa0*5c28P2_EET+eppVYWIr)7o9qsRQn^{( z9oMR}kk^XHP}B6GoS1jo%lxBeDpb^)RX$K20x@y>qT>_uz-4ABP0bE~uVi1fmyK<7 z>H4qWDma28r_U4O*zL?PL+g+375NLRz{x1})!I;(RMe%!>tOL4uZALU=%u0Oi}ay2 zL;AcJLUQI**xns*1;JaLj)&GD^$)i<^`*B~Z4kNthuiLh1y!&9!#9jF_LY2gyC5$- zKEJi>d+Oi&qTHPl0KNAXxMOTyqKL@ifyk=IPWB^hx%22>QAn?!jlv(g`7{A0urQN| z+nqGux1dDJrEXNmJr|z_mWJz}CYA?YSnWp>eB*1)O?nhzN1adDjPa~qo9@vU9^>Y+ zA`O{I4Qo%=G;LQ;3{z_rKV7dnZVXT99vTF9Mk+8Ko)zj10ZP9H45i6-jR2!=R&rbQ zN9qctfM))#o@c*?hwvN{v6~;VRqlB4E!G}na~O15I|{=}@-7cp_l%|pa;_1n!f7M# zfd#s+e7g>=ECv=K7zQ^yfo8)3bk`yRJ6)H3X;u#ioJB-eTY|==OzV6E2HW_oS1;k+ zEYL9N%uJ#^Ktk|vQ>3PoA z7)DKJn7VmzLb8D$9tZS6RdefvGBuMe+lF*V2BS7NFvNRMqT~9jm&Ssvs9>W)1|8)D zr2zY6j?t}1>4(<0Rh)l1ce90mQA(W?U>%NLk|ZW>Yd-Q6dhEDR{5?!_!x7tj z=HX`V``G}_gFF@WqjTy$eig1Mg|~DEOBS#L$C?^t+eq~OaSTfUx5aGHB8*P+&e+eX zX&tGQRzSATb8YvKiZ4mG zZc5RE8;r~bNLqai_{c$9yKdG&gfW1CBk87<-P%&Z`ISE@-GY26lQMK{6YU*{0*Iv% zxIi>wj@(fZ;I5Gtvg5b9#bOxPmLwp7q{xV5$;W5gWy8;9l-zVBxs(y~Mc}?@o4fFZ zJ*?$r5sybI^U+C{LG(q0$?0pE!FF@o#!qSu?$9$3`6q9%8ntvQT^1&sT*9E*5174_ zZm`^aT(8a7sn73AKHf3f`FVbc03bH^UrzocSDfl=JT3lI2D`9d+TdGxnhD0GbUH5e zUr#S~f8lojomWT6glNART~AQW=1s!~ z*YB^jDwleylW!hl&>tUH3#8%UTNs}Teh>8c1h|Hiau3}k6SPB+M4 zP0Ut^CbjlmaLW*TeGuP-51!HLq9>(3bVu7|{Vca@XYRB#`>XnP6uo9~@K0AHna!nR zlSc1kC%mkh^Dvg`a?*odHqX=2vto@`L#*n&^#~e-2@dTW=uBGHM$A@=kOeN=FcB3| z3*DheUTG^%Bad>Uzihm`0JSenCd<}G`2Gd-S-(ciEL2an*WB{9Me(U@=97g9lgJcj z?86DS9$0+*(39s-5sxPQHz-~&r=Pqf%*^d2G`gwf3~p@ zag92qU$LbaH*6UYdleWk*2eWHG5UYGx`}K&0)^~mF2&+BpEo#j0*tQ>tWHcT6#n5R z_sT7WWS;`CNsyzt%;(jJ*!}}NjkV(}g}t*D=x>|7M+=wC)^r_iHr#(I)4WRsrXR0e zP>Q;$io#9A^-?bqEW-^k^YsQ%p>XJ^_#jeHQys@ZOGOXG~Y)$-xKM< zisLbQ{aI{2_6d5xCF=Sz$o*OpWN9$oL{FcDf#2SVIz;Igd>&k^$Krt1QfuRxIoZiE z8WOG79|DIpSZAjC@Y(SEezZV<(;Z4eXSIf;Rz|6ET@vc1Tr{C4`{kbBNm#Y=FspsgR1cpRHAe%0pL4#czE|D*M-fbeY{Gdu zx{NZaTeMkKJxiFQ5V`7{8Gw|D!1sX7mEyE^r&O(=Ib?UlX(?br(0uhGLBF@?FWB8j z#_C}Jn*4APovw!~1`kkzrz4M7h+|b)?y`+hnML`%h-NB2$*}M+staW{EU5A5G4;h= z*f~0edN~0Bs8pHlhV#jy?qK!e8CoLGdW7?#7GG4yg~z68Q|3~%>-#A)Lrdw++A;>U zQufF@#S$>A(zb1T=>k8#ya~zUr@qR#`VFE0Zs(O@Mq-w9HU9bPnUA0hN$v$mJSwDr zGV?7bh1c>s9(LmosLgY}qyEaCY({*Szgws&=CQz6<#s)EXDPTLys8<4Hd0zS0r!xb z>Y%l@frY}yhcW?6?%z~lcZ%}tg1QT_)7U3pr}O+~Sqg{0;pFd7WVKLaeqX%(WvgYf z(O*grdcFS5kWux8w#w9f;2*oO7`gN2-93?L%S4LRi@J3}I)uAd+l$V|pIxG)j*V0;IHY!tk3L;>9M9$v(>hwTUXG>Y z?}d8ZxS);n313zLb|0-|+rxX!1gvEwLYV}tw5d&*WY7K`$YC?gvQeAJsOQMNt92-kAyXVHS2t#Sem)d1jhz7=tj(H+?)d zQ+6IHc2T0I`-?OgZ&i&k-4yXNc(#3x%~byug5Bi_)FU>>-tWTcWTMpl_My?z7AfZ8 z1=}iV=HW|jYy&iN$XD(g$gNjTg(cq;zzeJkxeC4O$c6!NVo@NBFs`eELu#UKhhE?s z1<+^cPKE$kq(;!Qe41_I*hK%E&J^9rM~qCr!vcELoqRUT`~0)vkL%wwdZP zYkKX|J${35w)aK;5`(w%590&)TB?RCjyDRad6RZ7)wW~oD&$syOY?WsmL}Rbwc-6; zq<3%o?|(eu($(oIkf&SxrmGfneFZSXzZn%U^6Na*|2R2Hdpu;={N4d4Kj+{ME*%+q zj83i5JK-eGEEc5dZ4UW`*wD$f>!IgPBlzXetv*K~ik17yJwo8iqwiYF8g9+gq}a~2 z>ax1wvQ?jFhEaFbi&q59mGeP=h>(rG3?a#MiT#a(X_T|Y1wrE)f9p*06eqw}U86Tx z^!cqqClU;%DEwFdpLh0&k;O$*Tz^0)Cj9}5;_~YkO-JbBxGv&NGb*@RcOLYD{fN|I zwk}FzH#&)KuXgkNwehQyQ1m$%cxruyrTQH9WLnCdz=lPX>?(E%5|%HuW%mDmo0B8= z7v%Avs%=OTr{m@@eHU4@=sE|i6JbE02^#<*=}j%c@p?d7JvH54Fp$+j!mEAc^1{3; z<$89sXxEL+z%!zb!eNP}Re57u>mQZS0R-JYu1-2U1YERYLH2_4x*A=fyxaY#HCBDQ29a*31Jk!m&=@qq z+v7z)|!61$#}A%b;;YXFrsx?Krv$+RX*Ibpm6~R z^XA=Y*mTa@kgg#A&|`xTsfifrMlc$$;l!D0(abv5hwoeE3azGwWV$_cKUZM0Y?keD z)-wJ}lkH;C#o~#@xoQR2IqF>DEP4Xgdlzey1Ce`2-S#c}W0!OS_nxKyFv?={f{9h> zV|+qRZVR(0Q|DazqjqDtj}BqyPyk3NfA?5lS8Wvx)zLAF!Af07-;lEhS*-6n{@SEK z_1twR+@?+3*=w>EloXccDq^g7#IS(1T^i2fu0Zolly{3s66)+QJ_ z(y;zHpf5l8ccNcA?`(k5jm7NNQlkLPB4q{cl>dm5?UMxRrM_PBYsp3U; zHy%EZ!J9(b-N-uhJxcb%BXZr9eT327Ru@?OHsFDYAEdmm(1UsX+F6um7B#`6{1IZ; zpnTJY|IkE}d0zSfa-;;RRvC++GRxYteEYNGQ_loG6rSOIzo$4}+)dI;Y}B?=8h$^^ zsN7IKNpo+n6?aW{-|6-2-~Hs>rLWA`;alt;a_lM`Ro38K`9lu@hK4ueWd%s@#W}Oj zmRj*~J^N2GguW0nT5t5{JT0^1D45Xdh@y@1zM%WUQxpW8iU6i5zYSh6!=OiBNOnVS zgUIZc7d$rDtCENZTjJ<6q;LanWJhQI(+%!)SFNR1zfvD!+SmR{uDkT68-$;_V#Y5( z(=Bg&sVw}AR$`ZbQ|X#-qrW7l{s&n;f8*pyyV|#lyD6abVD!ORM<4PtK2vQ-X0}Fg zq0f#xTRDUV56~e)*QG0S)j@lj0!ldC|LqC8hMf*q<=Z9p#du3*W~%Lh5$G2?uOg#x z#DhZ>Fw8x-Z+EPt+BS9m2Z;g=n|=B8p1V4pUw;*$_lK+(!6;!7f-XS2Z2B$ze!?d$ zDc9caH0y}%b@$iiW$renV=tL_Y}}KTEBq32MzyhSGx3hzBbkJOFH9LXS0!SKLnPmYw9KckgDhfAJv#$h!3GAm$b^z g>OF08;6%X&KQe@I6QhLh5Lvm|5WjvEQxHe|KN$;t8~^|S diff --git a/strategic_bidding.jl b/strategic_bidding.jl index ffa9add3..c1ff932e 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -34,9 +34,7 @@ using Distributed @everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" @everywhere save_file = save_file_name * ".csv" -@everywhere data - -data = make_basic_network(pglib(casename)) +@everywhere data = make_basic_network(pglib(casename)) # #### test Range Evaluation # Random.seed!(1) From 145f8a9dcdb6f850d242a4c1cf79a3076d46be0d Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 24 Oct 2024 13:48:32 -0400 Subject: [PATCH 100/108] update new solvers --- Project.toml | 12 ++ ...idding_nlopt_pglib_opf_case1354_pegase.csv | 19 ++ ...idding_nlopt_pglib_opf_case1354_pegase.pdf | Bin 18329 -> 19637 bytes ...c_bidding_nlopt_pglib_opf_case300_ieee.csv | 162 ++++++++++-------- ...c_bidding_nlopt_pglib_opf_case300_ieee.pdf | Bin 17702 -> 19025 bytes slurm.jl | 5 +- strategic_bidding.jl | 22 ++- 7 files changed, 139 insertions(+), 81 deletions(-) diff --git a/Project.toml b/Project.toml index c15e4468..21862f2b 100644 --- a/Project.toml +++ b/Project.toml @@ -5,14 +5,26 @@ version = "0.4.2" [deps] BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" ClusterManagers = "34f1f09b-3a8b-5176-ab39-66d58a4d544e" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" +FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" MathOptSetDistances = "3b969827-a86c-476c-9527-bb6f1a8fbad5" +NLopt = "76087f3c-5699-56af-9a33-bf431cd00edd" +PGLib = "07a8691f-3d11-4330-951b-3c50f98338be" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv index 1f3abb1e..cf51f812 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.csv @@ -60,3 +60,22 @@ LD_SLSQP,Ipopt,nothing,7,161525.52344752845,0.983549520184631,1,29.4053981304168 LD_SLSQP,Ipopt,nothing,8,394271.15137901035,4.110846793336604,100,2074.1711959838867,5 LD_SLSQP,Ipopt,nothing,9,161725.68930725995,0.9896961020228838,1,30.21243405342102,4 LD_SLSQP,Ipopt,nothing,10,385325.4647475914,3.744578744748129,100,2041.07581615448,5 +LN_NELDERMEAD,Ipopt,0.0,1,178359.74343438007,1.1200533758856948,100,511.4894938468933,5 +LN_NELDERMEAD,Ipopt,0.0,2,212254.08379190153,1.4180001908515154,100,550.9402630329132,5 +LN_NELDERMEAD,Ipopt,0.0,3,200066.04751296242,1.3332900353516668,100,512.9112930297852,5 +LN_NELDERMEAD,Ipopt,0.0,4,218238.51484798538,1.4738619006311162,100,501.04356598854065,5 +LN_NELDERMEAD,Ipopt,0.0,5,203024.17273709504,1.3468078531035172,100,521.932450056076,5 +LN_NELDERMEAD,Ipopt,0.0,6,191606.14917260138,1.2721033726111335,100,524.1365430355072,5 +LN_NELDERMEAD,Ipopt,0.0,7,216634.18097194025,1.4533418695121343,100,515.4771430492401,5 +LN_NELDERMEAD,Ipopt,0.0,9,214629.68380120143,1.4855060084960066,100,501.47680020332336,5 +LN_NELDERMEAD,Ipopt,0.0,10,189925.59830520058,1.2210286163994604,100,501.95699095726013,5 +LN_NEWUOA_BOUND,Ipopt,0.0,1,167196.39043391688,1.0022938482589328,100,512.7025361061096,5 +LN_NEWUOA_BOUND,Ipopt,0.0,2,156722.70401364815,0.952168590574678,100,535.0520720481873,5 +LN_NEWUOA_BOUND,Ipopt,0.0,3,164443.08291949987,1.0203179439844057,100,493.93674206733704,5 +LN_NEWUOA_BOUND,Ipopt,0.0,4,156794.56570489882,0.9610650569992588,100,482.8457419872284,5 +LN_NEWUOA_BOUND,Ipopt,0.0,5,161033.91621436726,0.9814364867894274,100,534.1026439666748,5 +LN_NEWUOA_BOUND,Ipopt,0.0,6,156725.4964958931,0.9791306609134534,100,475.3539800643921,5 +LN_NEWUOA_BOUND,Ipopt,0.0,7,161535.26400990074,0.983549520184631,100,499.9822299480438,5 +LN_NEWUOA_BOUND,Ipopt,0.0,8,160979.44597772305,0.9917204762644776,100,574.3065629005432,5 +LN_NEWUOA_BOUND,Ipopt,0.0,9,161764.20237385298,0.9896961020228838,100,493.1457760334015,5 +LN_NEWUOA_BOUND,Ipopt,0.0,10,155868.95779836978,0.942203145482624,100,510.560693025589,5 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.pdf b/results/strategic_bidding_nlopt_pglib_opf_case1354_pegase.pdf index 43d2ebdf0e7942e38a7ea14e186ab939c976be81..9f6e04ea2a993f63ded8a1aa854f19f98d801c17 100644 GIT binary patch delta 18928 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°Qkw7?Ss^48jeB2 zby`fODpSWAG@3eQy?=jFf*;3@z0r2yo_>Pk`<Go`-Fq+9*J(Ba^3Jw5&>O0TH_$J~_O^I*B2ekMwsuJpd9GykJXZ!tA* zd(=gEDZOW5Y<^dAzu_#^N9Da@FYigEcQukq?`ou!-m}uc6H0KTOUZq~(MT!13scEG z@g(AgBc1fT;%7TaO6!0$v6S9x08f7ef5n96Z&=dEJ^y2k!1->BG=hF_BaKt*zDC;d zNE8zT@a*())^^?ZkX=*NRRYG-RYD6dR64*(vDw3HC;*yjT2ee zT{8hkA&!d+b)Cy2@ZiiTl#2(XEU!*I zWIOOq5)QW6@%r?4>}=B>i#~pWoo)K}uh`krxvYH%;-O z&|2_fTD;J)OKGE%Cj}ntR7!0F_c52$#voHJwK;NW$T6ih&+Af~mrP#h^*S%S(fNT; zS#@7Qqu*vELWG5KhMJE}YCG|qic4*doKDA-+AMG1q_zR?6qnjmZ%A#TYfX}!&R-cr ze%Q^Io~HeiuetXBe`k*xCuI<~N7;=hoUJSUfH`{Eqh8aQ|5f)SH11~P975JxCp*s1SMOYQs8&8;l$18 z@DT2B{afjIf82Q3y*-cFzXlYK=+pww_pQ;wMQ@ckPQ2PMgN?i8$Wjk@uHZm*I-;wz ziJqRy?u)z?=ipwlqwW9;LD%5A0zN2y+-Vih-Qj1BdD{sRc4=>q~*Xx*6d0wYkC96fZ_I|DQ!>xxQ?$7Ml%9{L| zP88<+JW;kxA#?6@2xE?TUSpg?beuV+inre@C{cfBTrv`3Z~PfIj&N8=sjxM%KZRMPHd4zI{&<$GHm<4Y&B$3bzP6J3F=y7z1@Z`nRxI6+G{uH&DG~URLpX{VL0i&JTpZ zKlYVl?c4+lD{Y3fX*Hu44lCq~1|ODY-pY=;JZ>Gs7t>H;4@~dbePS5N6s!G3uVYf>dHV|R55ognbZhU|YQOKvf41so>|g^IRM-ACI=|-b=P2k9=FX|Sv9GFR z?YiE6-;)o-`|{KScUo88W9%h6ihdtERmvMQt7LD{-rlG7jVq=j$OKUOd0%?e_g~S zbk(gPb~=3mSgY!q9(XEx<@Fw8$&OsQzxC&1q53;a&@rgzxa+z0#3AP}EiR5Dood)C z4t6;anQ-Bgeyw3~5nkz}=M_KONpy8zd%1AP91};d8$O+BnhY{0chJO34i@RWaUN8Q z^et3*{u~&G*pas@G`&jJUaop$LNCA3`GIirZ8vh559w`i=VsltnmbE_-@3&D*HB&3 zQJ|nR)})0fQ{G^G6UTJEr>frAQ$^RBBs-lx$o~%=kqzIInnv^sIW#aZ3MC~)Peuw< C5@II+ delta 17610 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 z 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 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv index 2b3f3234..321bd87a 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv @@ -1,71 +1,91 @@ -solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time -LD_MMA,Ipopt,nothing,1,96110.71882678397,51.409912646352794,55,112.35776281356812 -LD_MMA,Ipopt,nothing,2,169611.93004530968,7.801358484116504,50,69.22345805168152 -LD_MMA,Ipopt,nothing,3,178444.28138203637,15.922922168322174,49,65.88223600387573 -LD_MMA,Ipopt,nothing,4,194336.04203766666,12.075242297979434,44,56.839174032211304 -LD_MMA,Ipopt,nothing,5,176613.2149870944,12.422180711866769,62,85.81530904769897 -LD_MMA,Ipopt,nothing,6,65458.26361763022,26.589452398348463,35,50.73106598854065 -LD_MMA,Ipopt,nothing,7,79498.15073959171,26.38580354505366,42,58.235145807266235 -LD_MMA,Ipopt,nothing,8,74496.46392915075,24.967103266479842,39,51.71993613243103 -LD_MMA,Ipopt,nothing,9,181934.25932745318,7.077167283620686,68,96.30612301826477 -LD_MMA,Ipopt,nothing,10,150551.556318804,15.517238331302416,44,61.61980485916138 -LN_BOBYQA,Ipopt,0.0,1,244232.89513945184,10.81443490086525,100,72.240394115448 -LN_BOBYQA,Ipopt,0.0,2,188387.21949619512,6.913013068576153,100,115.08367490768433 -LN_BOBYQA,Ipopt,0.0,3,193988.9162058156,7.025273557559125,100,65.74386215209961 -LN_BOBYQA,Ipopt,0.0,4,150513.5414421936,4.749046588752977,100,68.16472101211548 -LN_BOBYQA,Ipopt,0.0,5,237987.9450137697,8.291928779369465,100,70.9854621887207 -LN_BOBYQA,Ipopt,0.0,6,232213.14261507624,7.731145691394789,100,76.82888698577881 -LN_BOBYQA,Ipopt,0.0,7,237795.28401277255,10.070900059768746,100,73.00460886955261 -LN_BOBYQA,Ipopt,0.0,8,224848.85898168833,7.89813901386081,100,66.71672105789185 -LN_BOBYQA,Ipopt,0.0,9,274612.1997313932,11.68412522749941,100,67.7688729763031 -LN_BOBYQA,Ipopt,0.0,10,173580.3273015472,8.560154430382207,100,71.89584803581238 -LD_LBFGS,Ipopt,nothing,1,221645.11888298934,11.401696814027988,26,39.10578203201294 -LD_LBFGS,Ipopt,nothing,2,141262.71448922117,26.311399334821562,26,38.36118006706238 -LD_LBFGS,Ipopt,nothing,3,87363.0693632695,24.253863875152994,69,95.3198938369751 -LD_LBFGS,Ipopt,nothing,4,223731.48169915998,15.140592494373312,28,38.29574799537659 -LD_LBFGS,Ipopt,nothing,5,160116.8615679772,22.860132440024824,24,33.951844930648804 -LD_LBFGS,Ipopt,nothing,6,92192.29686791127,22.910628369499896,23,34.021559953689575 -LD_LBFGS,Ipopt,nothing,7,235055.40643308955,13.34543966105306,55,79.17287993431091 -LD_LBFGS,Ipopt,nothing,8,108555.6621307289,23.11464351812243,33,44.80105805397034 -LD_LBFGS,Ipopt,nothing,9,160994.8478759525,24.081755403931364,39,58.67839002609253 -LD_LBFGS,Ipopt,nothing,10,190046.33552746775,10.594052014825742,60,84.64722919464111 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,1,198035.28078277098,7.88719158718009,37,52.36943316459656 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,2,145472.39213218822,26.120541699500915,49,74.90275287628174 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,3,87359.72330924864,24.251828921465503,82,122.74614095687866 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,4,223801.14490554525,15.217660857646734,34,47.43060493469238 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,5,160116.8615679772,22.860132440024824,25,35.79092884063721 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,6,92192.29686791127,22.910628369499896,24,35.60436296463013 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,7,253347.71115981837,11.747118556625795,95,138.99833989143372 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,8,108543.39782339208,23.10815061751523,46,62.6436607837677 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,9,161039.6722133498,24.06518542269386,65,96.75429606437683 -LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,10,190069.3908097993,10.536935293217182,75,104.32000207901001 -LN_COBYLA,Ipopt,0.0,1,176010.19169836925,5.677305066259365,100,72.03100514411926 -LN_COBYLA,Ipopt,0.0,2,127374.33544940113,3.8448240810590555,100,64.18761801719666 -LN_COBYLA,Ipopt,0.0,3,176150.12057850952,5.482315426210346,100,65.934485912323 -LN_COBYLA,Ipopt,0.0,4,91727.20714677343,2.8158531169098886,100,68.77439904212952 -LN_COBYLA,Ipopt,0.0,5,198362.8753737887,6.237497498689416,100,72.6327908039093 -LN_COBYLA,Ipopt,0.0,6,102785.83811041675,2.9832594145233964,100,70.44147896766663 -LN_COBYLA,Ipopt,0.0,7,187911.54789161313,5.893495185002765,100,73.67294597625732 -LN_COBYLA,Ipopt,0.0,8,148226.30580434453,4.7518733303087295,100,66.37015104293823 -LN_COBYLA,Ipopt,0.0,9,106883.42897620534,3.1732717624926936,100,68.65271401405334 -LN_COBYLA,Ipopt,0.0,10,105033.57597358119,3.1867592685601807,100,68.18176698684692 -LD_CCSAQ,Ipopt,nothing,1,238249.22192024256,13.368774486955296,53,77.45764899253845 -LD_CCSAQ,Ipopt,nothing,2,152988.0994922613,19.838917943421166,51,73.36208820343018 -LD_CCSAQ,Ipopt,nothing,3,54459.19634512048,32.066785148062614,53,79.32646608352661 -LD_CCSAQ,Ipopt,nothing,4,223330.3099661669,14.979136317486603,59,77.34801506996155 -LD_CCSAQ,Ipopt,nothing,5,162860.81792287217,22.377943402548755,54,74.73434710502625 -LD_CCSAQ,Ipopt,nothing,6,238090.28459416158,16.0151547078734,56,77.43051195144653 -LD_CCSAQ,Ipopt,nothing,7,154809.254065423,16.97647571581712,55,78.2419638633728 -LD_CCSAQ,Ipopt,nothing,8,186665.13050085382,13.90320875043978,54,73.58215618133545 -LD_CCSAQ,Ipopt,nothing,9,103830.54778112366,29.50307082154892,46,69.35896801948547 -LD_CCSAQ,Ipopt,nothing,10,190069.37436692233,10.537467135211093,56,75.66284680366516 -LD_SLSQP,Ipopt,nothing,1,202122.28641528555,2.267153756014537,100,131.90146803855896 -LD_SLSQP,Ipopt,nothing,2,199331.8254757841,2.5237751272297952,100,138.61620903015137 -LD_SLSQP,Ipopt,nothing,3,223750.4938407642,2.2014218689308698,100,134.73497080802917 -LD_SLSQP,Ipopt,nothing,4,209293.7385906039,2.4208293676255077,100,129.0216929912567 -LD_SLSQP,Ipopt,nothing,5,203328.36023899372,1.4604130279370071,97,135.48368501663208 -LD_SLSQP,Ipopt,nothing,6,231355.38333033636,2.0340602661021707,100,140.8066110610962 -LD_SLSQP,Ipopt,nothing,7,242495.6487788387,4.432860622983777,100,136.01385188102722 -LD_SLSQP,Ipopt,nothing,8,187208.17397587682,1.6937783334110903,100,127.55766701698303 -LD_SLSQP,Ipopt,nothing,9,190462.07305198105,2.353980987448582,100,133.07019090652466 -LD_SLSQP,Ipopt,nothing,10,184874.85776357268,2.6584992062203487,100,135.63582706451416 +solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status +LD_MMA,Ipopt,nothing,1,96110.71882678397,51.409912646352794,55,112.35776281356812,1 +LD_MMA,Ipopt,nothing,2,169611.93004530968,7.801358484116504,50,69.22345805168152,1 +LD_MMA,Ipopt,nothing,3,178444.28138203637,15.922922168322174,49,65.88223600387573,1 +LD_MMA,Ipopt,nothing,4,194336.04203766666,12.075242297979434,44,56.839174032211304,1 +LD_MMA,Ipopt,nothing,5,176613.2149870944,12.422180711866769,62,85.81530904769897,1 +LD_MMA,Ipopt,nothing,6,65458.26361763022,26.589452398348463,35,50.73106598854065,1 +LD_MMA,Ipopt,nothing,7,79498.15073959171,26.38580354505366,42,58.235145807266235,1 +LD_MMA,Ipopt,nothing,8,74496.46392915075,24.967103266479842,39,51.71993613243103,1 +LD_MMA,Ipopt,nothing,9,181934.25932745318,7.077167283620686,68,96.30612301826477,1 +LD_MMA,Ipopt,nothing,10,150551.556318804,15.517238331302416,44,61.61980485916138,1 +LN_BOBYQA,Ipopt,0.0,1,244232.89513945184,10.81443490086525,100,72.240394115448,1 +LN_BOBYQA,Ipopt,0.0,2,188387.21949619512,6.913013068576153,100,115.08367490768433,1 +LN_BOBYQA,Ipopt,0.0,3,193988.9162058156,7.025273557559125,100,65.74386215209961,1 +LN_BOBYQA,Ipopt,0.0,4,150513.5414421936,4.749046588752977,100,68.16472101211548,1 +LN_BOBYQA,Ipopt,0.0,5,237987.9450137697,8.291928779369465,100,70.9854621887207,1 +LN_BOBYQA,Ipopt,0.0,6,232213.14261507624,7.731145691394789,100,76.82888698577881,1 +LN_BOBYQA,Ipopt,0.0,7,237795.28401277255,10.070900059768746,100,73.00460886955261,1 +LN_BOBYQA,Ipopt,0.0,8,224848.85898168833,7.89813901386081,100,66.71672105789185,1 +LN_BOBYQA,Ipopt,0.0,9,274612.1997313932,11.68412522749941,100,67.7688729763031,1 +LN_BOBYQA,Ipopt,0.0,10,173580.3273015472,8.560154430382207,100,71.89584803581238,1 +LD_LBFGS,Ipopt,nothing,1,221645.11888298934,11.401696814027988,26,39.10578203201294,1 +LD_LBFGS,Ipopt,nothing,2,141262.71448922117,26.311399334821562,26,38.36118006706238,1 +LD_LBFGS,Ipopt,nothing,3,87363.0693632695,24.253863875152994,69,95.3198938369751,1 +LD_LBFGS,Ipopt,nothing,4,223731.48169915998,15.140592494373312,28,38.29574799537659,1 +LD_LBFGS,Ipopt,nothing,5,160116.8615679772,22.860132440024824,24,33.951844930648804,1 +LD_LBFGS,Ipopt,nothing,6,92192.29686791127,22.910628369499896,23,34.021559953689575,1 +LD_LBFGS,Ipopt,nothing,7,235055.40643308955,13.34543966105306,55,79.17287993431091,1 +LD_LBFGS,Ipopt,nothing,8,108555.6621307289,23.11464351812243,33,44.80105805397034,1 +LD_LBFGS,Ipopt,nothing,9,160994.8478759525,24.081755403931364,39,58.67839002609253,1 +LD_LBFGS,Ipopt,nothing,10,190046.33552746775,10.594052014825742,60,84.64722919464111,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,1,198035.28078277098,7.88719158718009,37,52.36943316459656,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,2,145472.39213218822,26.120541699500915,49,74.90275287628174,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,3,87359.72330924864,24.251828921465503,82,122.74614095687866,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,4,223801.14490554525,15.217660857646734,34,47.43060493469238,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,5,160116.8615679772,22.860132440024824,25,35.79092884063721,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,6,92192.29686791127,22.910628369499896,24,35.60436296463013,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,7,253347.71115981837,11.747118556625795,95,138.99833989143372,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,8,108543.39782339208,23.10815061751523,46,62.6436607837677,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,9,161039.6722133498,24.06518542269386,65,96.75429606437683,1 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,10,190069.3908097993,10.536935293217182,75,104.32000207901001,1 +LN_COBYLA,Ipopt,0.0,1,176010.19169836925,5.677305066259365,100,72.03100514411926,1 +LN_COBYLA,Ipopt,0.0,2,127374.33544940113,3.8448240810590555,100,64.18761801719666,1 +LN_COBYLA,Ipopt,0.0,3,176150.12057850952,5.482315426210346,100,65.934485912323,1 +LN_COBYLA,Ipopt,0.0,4,91727.20714677343,2.8158531169098886,100,68.77439904212952,1 +LN_COBYLA,Ipopt,0.0,5,198362.8753737887,6.237497498689416,100,72.6327908039093,1 +LN_COBYLA,Ipopt,0.0,6,102785.83811041675,2.9832594145233964,100,70.44147896766663,1 +LN_COBYLA,Ipopt,0.0,7,187911.54789161313,5.893495185002765,100,73.67294597625732,1 +LN_COBYLA,Ipopt,0.0,8,148226.30580434453,4.7518733303087295,100,66.37015104293823,1 +LN_COBYLA,Ipopt,0.0,9,106883.42897620534,3.1732717624926936,100,68.65271401405334,1 +LN_COBYLA,Ipopt,0.0,10,105033.57597358119,3.1867592685601807,100,68.18176698684692,1 +LD_CCSAQ,Ipopt,nothing,1,238249.22192024256,13.368774486955296,53,77.45764899253845,1 +LD_CCSAQ,Ipopt,nothing,2,152988.0994922613,19.838917943421166,51,73.36208820343018,1 +LD_CCSAQ,Ipopt,nothing,3,54459.19634512048,32.066785148062614,53,79.32646608352661,1 +LD_CCSAQ,Ipopt,nothing,4,223330.3099661669,14.979136317486603,59,77.34801506996155,1 +LD_CCSAQ,Ipopt,nothing,5,162860.81792287217,22.377943402548755,54,74.73434710502625,1 +LD_CCSAQ,Ipopt,nothing,6,238090.28459416158,16.0151547078734,56,77.43051195144653,1 +LD_CCSAQ,Ipopt,nothing,7,154809.254065423,16.97647571581712,55,78.2419638633728,1 +LD_CCSAQ,Ipopt,nothing,8,186665.13050085382,13.90320875043978,54,73.58215618133545,1 +LD_CCSAQ,Ipopt,nothing,9,103830.54778112366,29.50307082154892,46,69.35896801948547,1 +LD_CCSAQ,Ipopt,nothing,10,190069.37436692233,10.537467135211093,56,75.66284680366516,1 +LD_SLSQP,Ipopt,nothing,1,202122.28641528555,2.267153756014537,100,131.90146803855896,1 +LD_SLSQP,Ipopt,nothing,2,199331.8254757841,2.5237751272297952,100,138.61620903015137,1 +LD_SLSQP,Ipopt,nothing,3,223750.4938407642,2.2014218689308698,100,134.73497080802917,1 +LD_SLSQP,Ipopt,nothing,4,209293.7385906039,2.4208293676255077,100,129.0216929912567,1 +LD_SLSQP,Ipopt,nothing,5,203328.36023899372,1.4604130279370071,97,135.48368501663208,1 +LD_SLSQP,Ipopt,nothing,6,231355.38333033636,2.0340602661021707,100,140.8066110610962,1 +LD_SLSQP,Ipopt,nothing,7,242495.6487788387,4.432860622983777,100,136.01385188102722,1 +LD_SLSQP,Ipopt,nothing,8,187208.17397587682,1.6937783334110903,100,127.55766701698303,1 +LD_SLSQP,Ipopt,nothing,9,190462.07305198105,2.353980987448582,100,133.07019090652466,1 +LD_SLSQP,Ipopt,nothing,10,184874.85776357268,2.6584992062203487,100,135.63582706451416,1 +LN_NELDERMEAD,Ipopt,0.0,1,142797.72464495822,5.161901992702432,100,80.62988901138306,5 +LN_NELDERMEAD,Ipopt,0.0,2,162906.03031494704,6.7877009221550555,100,82.2264289855957,5 +LN_NELDERMEAD,Ipopt,0.0,3,187053.40210264924,6.456494149939711,100,76.6717300415039,5 +LN_NELDERMEAD,Ipopt,0.0,4,122654.05499742832,4.8276997818123775,100,71.19857501983643,5 +LN_NELDERMEAD,Ipopt,0.0,5,181280.52587454475,5.6929535395111825,100,79.03099584579468,5 +LN_NELDERMEAD,Ipopt,0.0,6,143977.16792034736,5.214501855637245,100,79.06425786018372,5 +LN_NELDERMEAD,Ipopt,0.0,7,191823.29265087267,6.515167628903469,100,85.2777590751648,5 +LN_NELDERMEAD,Ipopt,0.0,8,129787.3066354752,4.8447342062259375,100,74.79859685897827,5 +LN_NELDERMEAD,Ipopt,0.0,9,125691.36379888249,4.614137285862181,100,77.564857006073,5 +LN_NELDERMEAD,Ipopt,0.0,10,115360.68662683672,3.744635559065837,100,75.56841897964478,5 +LN_NEWUOA_BOUND,Ipopt,0.0,1,114609.03285998756,3.5386258929295185,100,77.19337701797485,5 +LN_NEWUOA_BOUND,Ipopt,0.0,2,126877.25981097147,4.278105995840755,100,80.73210501670837,5 +LN_NEWUOA_BOUND,Ipopt,0.0,3,179484.56136182434,5.774421048335941,100,77.0150978565216,5 +LN_NEWUOA_BOUND,Ipopt,0.0,4,69624.62873078427,2.2157777785271127,100,72.36222410202026,5 +LN_NEWUOA_BOUND,Ipopt,0.0,5,185082.98766657148,6.148943205505296,100,80.79146790504456,5 +LN_NEWUOA_BOUND,Ipopt,0.0,6,159079.53750403182,5.2054481202767695,100,80.37452101707458,5 +LN_NEWUOA_BOUND,Ipopt,0.0,7,244099.06401609167,10.392530675873035,100,86.66114091873169,5 +LN_NEWUOA_BOUND,Ipopt,0.0,8,41931.002594318015,1.18971351086284,100,75.69124221801758,5 +LN_NEWUOA_BOUND,Ipopt,0.0,9,186785.7114113925,6.29186177667848,100,78.50683999061584,5 +LN_NEWUOA_BOUND,Ipopt,0.0,10,30108.3054170366,0.9800887548124835,100,79.19660210609436,5 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.pdf index f166879cc6085ac3be6be81ed11a52d0535d2ab2..78198a5fe3c80144b0d20334271e57dda657415c 100644 GIT binary patch delta 18312 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 OI5#*r3MC~)Peuwq(qo?h delta 16979 zcmX`Rb8sL{_x`<`Y;4;b+uGRP*tTuVCfV3FH@0otwrx9a?&tf~ucoGE{^;&=s&Vya zuIcIc?**aXqxm5j8JHLt8Cf~1#W6sBuyHUj#TkP91j@a~BS|?O*E=$E7dCU_-^jE0 z|G3tmYhZ4;h0FQLlkzkD_V6}McO*`BFKsn_No3@o{dOraV z4UbXawtk^8ed3LDzcB`E6EBf-bB2qN4<0i0WAM|9N9XfVVhZ4fdf_vjKfmu3FPe6r zark)L{Jb`6~_c^biz{lJ`A$s*VI=nx|k&D~CNneO-I#Aylb#=J^lYU=k zj2>dU68+{Me#NbI!Y0M;;QMQ9{#%nP&=?O8Jf6!*aoXhDd8g-BH96*#y)8`SVA&ob zX09`D%%FO>&TTrdD{Z{E3E$Hy=O)1I-?U705|KtNP9~wu8x$Ey9V>QD#(yLd=XaJf z=eM!ug*Unlv4KZj z;dbT4MwCDlMP-?rWU-Br7n=rYGD8eNO!4!UDUS!z-P<_srH_*fz}L`*W+YmbyLI$p zKOGxq3MYP)yg<@eFXLX<1&|&*4_vDRCQ)wPLxrLrz;MYIG;YsyOqyM|l>!as8jNi9 z-{4>?d{nX+=2tXpSp14Iud4P-z1ScX^D>Mb7(E>8Wy`5&kj&Rjoc6dmZE$o7_?_s9 z%=J66Ja@md?^s(H&uY=06Ot%TW)t?rHhQ zc8=0uIf;%FAasuUV2-sy1#5=bS`tkAeV(9N6Vv=V+#T9$Ks_;nH6{R`+x&NlDSq-1 zHetA2;QqNnzYa4@Wu;l%;|xdifuqj;Noi`rJ`HPfyxjR=Je*W1(jlV^-kYk@{M_qr z2kb;HJOg%`UE!M#bdu?ylBF=`1bz?h@qiVIMw>4Xr;IhMhl(QZU_MPF>`)MI2Sv zSyEl#`{|Db^KtOZduN~wg!(Mc7!M@TXI|YKaQ?li(w?V0xkzMMR>op! zRvn;^COyGj8P%F~|GZ2ObL;$4rM5^O*JG==P3fZ*NqJK(kOUaEmH#x$epq^fU9kz2 znHG3hKr~>!_gXPiCS%v1Kg4n3L{~$co%wFQd`k)~K%>}oit>|o1j8v;+O{{}J8N3Y zdMc55Fq{0Y*wzdDFz2fUCAr{nrDv~y$|-yP8(P=mr=c?><*QX99g1)%{iCSC7owUrESlNE=UDtf=EOVtES6{WJ)h(-y(_BE#l=Ka1x&0pMiWn7rTJ0;k?iJEL3xw;b{Kd{FwjoYe?pBQt>^V zEjEk(-%=sviYl^U276cL`*Qt2l-zlF^Mvhu0ip_( zG{C1+?k~Kda-!*q9qCFMy!6O!LOo4rNSF&A=&^1N=dX6$OUrNo` zs(uiW`+T0P4KC>og!q^iJtlJtAVs$OG=0!CrqFJi@PzCa$NNk^EfsQDbb`4(T{Zpd zD<`KVONZYN)cnnjC}F}s>bYn}hJ>#xxj^B8As5x14da*-f3k}TY0-0@?d1^;4KrSJ z4+>jh)SsQb;h(K{U}Uo_Y`>&uWe#;2Z3(F`L6SI(tBqRM_H3mnMQapFpw>nUV)9w$ zyfNvWqOEq$>j?8Uk3sHaw;Nr5q%8cdrzaxDBsG1A%V(cX=pTyM`Q^2t#vDWzc@Io- zcFPrHFwCQmHqd^q#3VuuEF~YRKCSWZ+qB!1`$jMc*-A1@U~&{1pD5MEQYO!1D3iIT zW)3?gsM!?^8ln1$C^P(4=}!^JUYZMKxt#Os?jU(((bZ#JX38LT8cdxi`>4_>KsCgPNNST!uiF1?8Q7fx?wY|}SRK~a8R2qt*XbTvf)T^5ka6?MLGd(DtG@XKdo#ndY<<_r#n>Q{^&SHbp(Bn2`fzRL+1TOA#? zugl?|!tPElJT6&qS&*_s7a#sRQMJaLMD@&ZCN!~wIS*gX?i z8*jHSShknIVeB)QbyHoGw)_|Na3qZR{gUHx&8tSxRE2gLe+Dkz8(_HfvU!ULXAO2= z`=QK*YtEGjR|LcjV{erC6n+tsbh(|Kf71ghNoE3Hl+G__f(S(r`#6wfnf7x!hLf1) z11(=OS94AMnkeR(WY&<7YYAYqWJZj!J6ggOA)9VUA>WaoW2ut^7iQ4MSG;P|Qx>H1 z_<0@hZiPu&BLT`SWhjL~Beq3G<=ml8wsU5UlbyzWt&Y+OkIy>&$}KO9;1j4~6DHj{ z2$5LQDmGJeQvxH(>;+|W7zV>Xu;}E78Is~m$+Y4|A+jI&=94m@t?;o89TH~fzt`l| zAo5OOe%0uu!nE$h6EaiU+sC1QTU<7$o1{}^wV9%qKLU2SodnzkMO**9o3o#;k=$9_ zU4!Bxmw+XyE6(oa>7>2wJP104_)mI%@-!M(QvPE`H{eCa{8uLNJa;1FD@oRjjeGPe zu8r7x@w?q{v*P(rIE-mRYt* zgSdZexLQEELz=(8UjkuxOyW7iLNBPD>fQO;S|ma7a~o;m=rM~Wz*kz=K8zdL5p<@8`hd$d|- z$~u8&&Qi--@r$HF;a0}1{~L)?A)*UP;|Ay3ALblPJe8`X?5sE$6A7A~6wzaf7&g+c z%T1!M+VJ?aiPyc~BzT$?MFVc8&mpqq`-v)O1b0ruZtcNZdoTZdam!+c3T+T8ViG3P zB=D_-vJbun9WrOKJ^Z$V+u`7dlOP)w5#0C>hv|ug%t0||%WdAhoi}><(kjY|8lCwX zvEH13sh%qD5*A7T?grMh?PN73_aLu9QZHCOmbB)SXyT%m&4b8#@-o9}tYlNpKW>3) z9`%J9ZsmQG^2=NlqWh;79}m*2W-=A5JiyFh`HGNIO7eG?S1{3%NE%7=FqmpKbbh?n zw2A~x^Nx}lWdONIo3~PF1TULKX%n7}15)fT*d}SJR@q<9^*w{5XnnAH8+g+>iRYR$ zUSWwW&|GVjMmSfVK75BBZqFchOgo!fWylZqZ^nl@}0yc zlTkaw(c+r^4@SdzLLAreQXS()Vgw|)V<^EMx;yiHZ!empr}#7fuF@f zfH{(!d#>Cmq43k%4+8xS^lsn;*o6p=^yqwCb>!I`KEa@W;pm%BuYKFd-%E2@E%uXM zZ#kVWQzvIJ8Gz~ik(j7&sI1|)4xBjb6gSgh`dQ0BIcchMm+u}(%Q5-)Vn++%G>{ZW zHkkNt?ghEGL9Q!!rcYlD^yh<#6f9Z~suVr&ez9WpUw#y_Jt(U4X8>B0WA4pm2 zPUiBMgto1OM~+IwHCZK{F33+Kpt0``)?;Vk{>iO5vmuU|aULE1<5n^5JC_BM3Huz| zNlyb0j1!cSUozAYJ|^Z6pm(M+5-rnRV#^5&E2@#KI;BDLy@JdVIq_S;iZL4>Icdg- z?J1Yli`~(v_uZ&|Ty)kEM<1O3FYE~V9STrETsiBTqF1Nk88@@sI@k!IAQ|_cDGue# zU{1Ltn0>hHGk*m8FgF@iF+t|l$?4pL#&MSv1+%d+AKU%Qhze`b z_27rim%C>96ts~#YV)|+7|9hV3R4IDu^$UUq%vIzEP_<#hnLQOW7(J&ONtFpR*I<2 zRh;vVB;g@!YTlJ*erQR+h?k#zDTHsN6KTZMV5IuxP(mH$`*eIA)beUVUEC2VNi^$# zb)1z`X{H~jk?PCuoJLL-0s)rElMc(wzB~cU4&=T}9)b z=RCwmVMWj=Ed$+!(i~fo%6!q@+a|YCRH!Mw4kdA4as@;cfF!k6E>og1DD?>PEm8hy z@WnpTyOOC`ZHd(8AQgE?FKe2E{ZFK*vB3_!{D+j(Vb{Hjf7#>?DGyfBLYHzs&b6(r z!z*-T3ge6dOOG#>yC?p*D@d-?Z)PI@9g%l_V?KUAjHVjJZdpLJ3k0vV3)^5#2GfVt z0J#VY0yk6z>csN&78AP6lREU9d0!W)0Yg@vl3%Vhjz)8g6OyJmUF%mFGyQyHhs85UwuRXF^FfV6DxMz|sOgidW zF}ujV!!MqxnlYG_SW*^!^iV9UK#XKNTEh(tl(m8bhP%zI$(r|avIJUh*FuJ^-aMx1 z`77(UhDV4a{F`DmO_&?bTBrPC&G1AhR9SNvXi&b&^%B{Llw3j$EaAW*t*6=>6}N-U z_M)#UO)x0DvDEeo!_UdaHxJ9lDD@3Ft(0SbE7uW~W!a&g&TZs!2hp8*C;FhL#?j$6 zJmh@<^3#QrP0D_2m+ri;Q&)B{;Z7U=y0&5i0sg*IRXkid?)cSI=>Al}D^F=e4&o#! zzmKCjJ_})%UMfW1fRc|OPd91DFi*jUOBm>QHAG>LB5cX&DieIVV8Fv|g>4?EdyS~p z=Q^4-B4F1F_{*o>?Q#qG&uD|)^Y3y-ulPIwIyX3ZHA0v$Arc3|t+y?(M6^ii@sRJ2 zp9!M9LGL#{CpW3O|K7udP=s4@8T}#eejQbHqmX@Ao2E8A>XiM0>xVu`&ZS(iVrRW+ z%EG?<@S&d-c~CWj=5O^uL*|&#t##5>it%@@(#sU5&13Ck5UPS*b+FlVa%%);)-4EF zyu;Fk(`twpwGVRe_KQ&<#d3VKbrl(+Y6KEd^@?bXT=j#5m& z`BfYAI3s|p3R8kIpLbP17R>-6==BK25>*}KI~XL^^Ci2w_;kpi$+cnX#C5gjVotmJL8P>i@~Lfll7)aIZ?Un_clLghJ5@jZmL9 z#4}gdMkv%sMuOJ}haUIG3qNB+%S@9_kNig&4#D|7r>QTRzK&yvLIYcQRelvPCs$@} znvp>BOf+`!_^9ciD`c<-5wwSzB!}<$4%%!f{&Q`N3**!Mt$lP{OE>?7p2>N?h_zp5Nu{yBr=odEmSnNaL`$OT>Jay z)vCbsB1lFArns9r59|@gfdjx%g+`Id9Q~#one4{^k2Wxvjlq%_w?0C_K#7))2&spq zfeW2K{ny)D`v@g+#>RZ~R{7fxG>WTa21xJFzVCmU5{S?L%v!5+)v7ThCYSPYEtMUL zl*DF>@MKqm_G;^^)ES_6T~WD}aBgR4C;2CnN%q3~ae!vbmQu{{1J2o{8Suw{$BVd) z?OIt6)-jQYIyVYl*qaIgc*+b<=vWwR4z?iSH~;ICY{xW*;8Oa`N;6HRlq;d@7ba_% zJtse9YO&`bBIN?m`9s7yH{R)EMAN-eSYaGogwBO%L#zi(E{mHX+qo5I7NjA&(oo_Uvh ztbjg%3LvT_LajZfBaP|2%Sli$PAf^O3w6%+3P^OIM=^1&L?FZj%}EYCL=n6{%heQ-g=j5EF;mi9Apct7FV&3)43BxT=xT~- zZSV6QDc5gf_#nX~!YuJSCV}^IeK{G@iF5ec*&q{$m`OC|R`fA6SB$po6Pw5Iti7rp zSV^|46((PLle8%9 z2(vP1P9F(c{Zr&!zQ;-A+?MGMShXF8s{5ekQbt?a!?)-2SK32LIzI?cPT@?XgLR0z ze_nE|dON&*!rKeqG-H_=T>{iEO%Fui9D<>al{&+S|01ylV)}w+mN_)JSbr#iR{P$5SYzo;t-q+8;ZTV~0)KA$CRv)OyCgF60B3AqLHx$7r9 z5<#&kL$FE{bcvkC6gf1x(N(!AdNb^s$v-CV@Hx$$Iu|;vOYi!AGjOEr6P*R~{AWSI zksSBEshG-%bSk}-#9Ie zL8}bEMI=E4 zgA840NvYXUrddVlw{#becXgx^mvuumPX}K(u~~pMs_1oKIu`c4Ztsd>_F}qIi;@#Q zc7x4pd+q^L_!}5&Huf+`L!l1D!8zi4XtO+)SRZb5>plUS`!gMBO`?fw;OBwsT&PLk z3iM7nJ^a7Nd#1*X$I%3ZVfgskrBN~>a-xHHTI`S+iNbh=*c}z_GhX)OOHDZ#EbgCC z>MHz@>Gd`vPCE?Q+y=SoZtm@JOCEj_ylh2!uW%1qhC5(6;YzNt4@pTvwnp+I&{2r}XPoQS zr;Yv3kVKvmsr?qxvD63>jzK+)!Vn|>cMQL90q`YIacDWxBCa{ZsNbqaZG1^|T^js* zhJd}S!qD)b(hwX;B)?!}bYm}_J)Y<93vu0=!iyoCHzha3Ht6WCgK;!`d8ECj5Np`T zv@O1+Qn(u~KK{d;l-?3T%Bp9;cPW!?&YmQ)`s@G?Z{1#*Z#(4g-0?G4Q7+k8pZLL6Z#j?(ovx=b#W9VGDuOT z>JmfCL1|!XLfD{#w=F6MBaaTATNzb1&HnfWNTPpDu=|fR?)-~?T?$fqWef0>A$F}? z4rut(*DcUMDIHO|uTpK|4kGNJs1LHT%q*`Pc5ql(Mi4!8OJEw(lT91j442z+4&aV5 zd0yiVBkzZ3rIk^bw0!UB$Vd^?_RS7*7fF?glIxA_ZDm=(m^;*FsU##{cZSQFpy``| zen|p?{!EYo!MXbtoI2%VMIqS_>c7_D!<7lXZj{t1r-ZhRNqb$!q;k97+Zt{$0XNZT zy^tmlcr2r=<)ezoho$~1>LZc|r+`UCXmr94UVquHM^9=kl~~QpQAxjUJ`#GPd^jR% zETlHtD2>)Kz4QY#Hf;#jy!Ww<;~*naM`FAR5B$4Yt=NPgS}HdzLz0P6=D*m!qBB6X z1SR}*szFephg6qm0ZV@)h4}UA!jBpJxeM)l~kt}LzDl?Mlt#`Z&2KGw3;wIYg_B8qM$k;+BEv` zV|e#bz$2g9g4L6e=d;&RiP`_@nG-kuEy)?73%8QzhY|bq@Hnr*bv;ZEDaK; z9pFnj+0{mcz>k}f%R6Sw0h?Sy=Y}d|bgy(O9LH2fKK>?NCpKWCaG*t-8OTvn4N>Yc z3{OQYO8aQ;?T8+~BCPsrLo~I{4dqjWrnaAjbCaM>Y~egr&MGc~9*LTrwiF;u`E|8A zes3|;^OCk<5q{Hw9-PguQYCY8V{0B&3xSd!NK<`y-a#ai?H*y00`ezwf`=-33CbmF*a(%!zJ2<+}z%R3-;6b=sW-S%385 zL08QoHg9RQuDypHs2}9#dC;3b!1(0;oJJ&l z4=rFmY?jDk%2XealOK8rqk07R^uyF1};+zFF!Ki#0lUK2B! zw`YoyVD2D(DeLPfP#M6v%Zlk#7%rS}N=!#=@acRkZhqWMxdbv(IuRq&ttt7K@{eKN z=qPk-kxL@O$>Z#1Vf!QW|6)a4L!!J7q@7h}M7jXp;vOAnitI1% z3QFx5x6~GA<$aEX;|kt2v@mq;BRZX!D)pC`d(Y*}OUQbL>8+rQOSt@mpS%4;k-j__ zf=eDD-+(Qsf7?}~hj7gv99=nm_!su3)|p{WTIO$fvcK-3`LAh%8t;KvmaNTX>`@_! zbH6MK2^L|A&fMXaoi}HynDe(ij{EIYfV04&5DPmh-|c71dgKv7)gq>1nInnT$lEUI zRdsukk@YlpXKt^I2Mb3 zBWqV74#EPccho>Aq2N8+IncSGKi?K$SV-a7L;P`3%*=hpCvLp)*tyYLJ3AMl_>R!6 zfVO&7={lxA~dF9|E5+%~YS>`y}9Bc&_QCkX34_h$wR{qF2&7MtEFeO(i z#aBqd$apJa$UyrXgxR^;&WeiTwz<0y|MUc!{{f!QNx&iiP=c|WNat`S$ z1YAb9zdrhYel#WlFQu^)?4>P-pWa{w894niKg_+RT6;6SLJ4WFOX%#1y|im?mxaZy z0=PxM2B(mDOw+syHrb$&Geh~UVidnRV@xAK5q16O9Z8qlXYE?E7(Y(u8y7we z=*3hIaEU_zi>Zb=_XcAlMaM;TiR9@Hlsta^5oRtl_Fy684K!T)#)J%B`3#}cmJ3|D zJav0M4Y%teJR(&Ms^SgDBVmFE46QmpZc3R{VOK7aEUz80aM}H>Zgq*&R7LG*Bhy01 zANR$9cpqc({$U4#MRA0B@VX&|5Q%@|ne0{r@cVDwvx(3-wipE4IPtOzTh5G@gcw*S61;`{t#q*0Ev+*d36iUo^*I)!_ z0Y3usI`(OL!mDm6o~M?$R+@O@2@V;1gmSXmooWcB`H!<91(Y{$9_38#ptes{x6|aH zFp_#chBsW|N|WX!uL&Moz$Nd2fE&#|Wsf-L!Le`q#U1anQ%~=$8?~l((IrJN(i@s_ zsZlOR>F8-sk;0f4aZDnXNt?q9I^S*-5X7M4((FeyxP!_P6l4te%Nh{BSQwo&Cb4t> zg6dEbPnGHo?HN&;&s)A0IAeDNY@cjU)!Q8!ge@I`ZV4LWc;t|`&q)d)9wFp65HEPo>z?-=3 zuXVg;V!^w%Nfnb0mgSlDbD?__@AXK$|5Y$XlKm0C+N7?PP*Q*}fU{L^|NQQ-_x&3+ z)+Y;KecWPzlYPWD}gMtK!5X5(1oo@ z#(;uRp;ST0Yoy{{G;2WvyDiHB}bl|p)dUIU>_I^;;QQ!!SHCyek~c^2z}G$&lA&@Y?g{H0cIQHHrm4<2Tr5lBtM+$2RC2_TGT9DRArbH2ZITb z-BB(R#D>#Xbt)&{a@}+1fbMm!8(AjR1RB(P=rF2%xKu;%T#>~G4JuPE)*OU*uycqc ztXoQ|Lo z?c>!8U901f#8&}_)*eDrQe>n$^wWo+8Ja>770wY0ttzQ3)s{2mHwC4qBXHGGAYd7N zPN&W?E${*0v1H={R~{h^WOzbO|86x?okU=-%OzCTAUD2(Yhj5#XMRe^ybv}j*``H; zYs;5AoS`hfMJXJqCA9OHDz7gI6;P5?9M<%>psfO3*ZTAx&hm(Of^{SEh%$ zLo#$hX%~?|CNmlX7=~gcLKda&z7Ds&4ru)Gy{Bs4Z)^c6^iM}xnj#cRSBy{5TVV2s z1nVh(>OveNvAsTmroxIBZrAwZ%C)I4eRRx4imrLC=trLbsX2ZG#&e~|rKWFAhRi*J zze8uIp$XVZ!xsD2K--xh{810ONL!+MqSe?K+o0FE+Iy5PVOhBjb`@~>UR`)s4`imzI1Mgq)0A7fRx{Mm_1ZN*g`!;63wcuTb@8ITze3>3 zAoD~$am$RYiJffmhz*`+-*5apRq^?Q5=AaOa|sNpYC+gCtGS19AF<5C2$=^9M4b&^ zfoVxI`ZC8)k>@=qxKbseHu&}?q((LyK0xKfO;pnr!x5SPvMqHAzl+7$z`OMb5msI) z#$$&$JHuh-*sW%#KGm9C*dRs&A; ze+F3o$&9k!@=yEhgyL9dHp*TjCujvV{#WLtDbEG-+uHZ<)?15(eI8kSlaz6!dF>%v zNs-Q%Gj?RBOy~7V(B;mSVnHoc{H_+uNUWtU660$Wd&>qb{JUFq_t+G(LJxj17_voy z@QX)G%Nu4z?DjZ>p3ZQjjDe~3-&YX!0B+Df`ph_~c1TrPxCmf;O zn=5c?R2Fgmx6E$vjo~0YU?{A zivJC`dm_B|(2)_z;&c%a9N_v2n5~r)pWKXSmcb2l^`B z=kpZR7caMLj?HE%*X%0!S9D{xPFAuo+fPVWR&-&fBPMLk0n%$1Ibh3Q;LFT>^~@wAagPn7NdEVQx^pbKpd|3@3OEE=gZEl)}Wc;<6NP#%fvk8fhos|C(2< zZ=zh@h<}oN3=}DGNng-O7^5_yEww(dGr~8z!Y?fOI1p=G& zHe~-_HCZ63sVQ6$KEu)3sau>WG)=JS`u^25INV#Og++*cP1`BdW1lM&8HL?O+7@=# zKQ0gFKqP0~=*M{TUgyH+N3Hkeq)p3*)h2a|7g}r50ikQd7`Yp+^2Rz;da*Qvsm3oQ z<7Q#c&0D1ltIYpHG4H`&TAWLFKBk+aQfZ;U1LscrrLmjBh4%#vZ(ny?n^@URNA=}? zabYel`Qhpsz;XWB%%LP_HaWk*RuXo zc+p~$>ii}{Mx*Lqwjw4y<5P#JSo3ksr9EQqhJUEj)c*fu^HnF+V{qU)8+Z)9 zbkx0?du48Xle_S2(~d>kqrcjMsa{Ir;BqU-`O|5<+*9g>#^mLGrV^ELhj^VhO2f^n z50xqIst4~Qqdh2}pn?Y!rV0L@8laKDqSW#>ShmcYgHGl;gKfZvgNeb5DCml*!? zR(KxCB+5s>Y#qDK(y|UP41QFA+pX@#RBT(c9zwUX;lfdO%k5{_>4b6C1)6)3d5*Yq zw|T5JJqL6Q?jH_ zF6r&&xo?y^t8Wa}yynUO0=d`9S!OI@%2|3+tEIfxW>Td(jhqJag(4e$%9lf`?S>UE zEX}RYUu~`hpK@L23YB@N$MYy7)q2ktp6LF2@8|3@81CsfIS#%k~V-5887&6HyqED;M! zt9;?txc6;7!D2P7^zb-;ZIR9JJ}lk1fGW@68ZP#pKUIlQdAH{I<*o}cX=AYhOnu;_ z6^)_VUKqMfF8(7vc$+ zxpT0bv;AC(u|G$!SM9zy=OBS$vUD-`pOGwaGnXs;TLnU-Jo;6P)*XFSkU(700*z&f%Jm)|e)ffn#qe!l4-{+4nw{+Q8tB6BuUZ}Y?@*><-3q(_0X zpEuc&1W45>+sfrToO6ccY7LgnCp>H~Dzde2%C}pm6=Te&xqS`K9#mE7if$D#r-ysg z(&%g8NroM2weqE5%ZWIYjUm-gUj;9LlpM8qbHkrlW+hwE%+@^g5~Q)wDtyiRFR-$m zi5bqAD4!8slmv^cNcc*l#8qL*NVUSbc}*UelJL6vrCajIct4V%WDD=N_?6HlW-d;~ zrb0UwmviYxxL&pLIlWqr%H?ww+4q$;4;-@nI!&}D#-%1oI?Pk+}otn&{YaWAOR&Z&dt}XIF4XuSX9-GEmY>S#SsK|79{abmQm_>Ggo&I@?RoeD5 zTesvs(hXg~TyN9ey=9ve<@^CA?!WH=<(kH$m5sxANS*Gr+K%Z;8WV3EFQkd(_A6Pj zr*V1?6YoE&}9k)XA%O#wTiIS?pjgdqc5GnW~>*vu3R~pW(FDyoWx=4_U z@~u^hkTn$9^7Z8GNdPKEaB>%Tkzb=D%_BoAz&IGv{u(k2H z<;UPZH^e-n&rvkTBl4%8Ew0e1vqqp%ouFrZ$8KM&)#*+Q55|}t5`@>s zhCI8ATmoCu^-+HUmtFDr2^&F54*Tofg4q7(OwXmSt9AhQJLrbc+9j<1RQR?K?{c-w z%j1@iO9&gd*v0rRhbGbyiL7C&7dY)4`dv%s#_Q`0ysluRea@Ajsa|F)%0Gza?3P92 zKn8G-cEV}OaDZASMwL8Lo+&AM@l@(a^w3e!+Qjr!%h_>lZo7+)Q~KrDyPGabmP_=% z$IBxE$KG^^-ji;%9M<$}aA)m`%HfN-*DakL>&PS(dKTqj@|;Mm@J*U;*w@})RD_OR z%Tc2wtQ<9miVTi9$wg7tTTc~zIT3k%mVd~TDD4GME$I&U;Y}5V^q0jx!KoOK`yS~& zklSWMcNI&T;r3MFWB!?_joCc)TVul~@=2re@fwhw$SB9-ir<5SD`zg6Kg2HQdV2D? zzLLUfUZAeql7^Z7R9H4s_FMY$bO;NEPTaM#?_)W0a^D%(k79Ob;euc^F&oSRxr`2kk>ecp{*%aT+TTOxTVm8Ku>5wO>}x~-3(H+_=*(S^ zBrVydvnhJ{HD})qvY)lpb$>lLWNZ0V*<^tBU@M*V z5(0;bJZ1PjPBJ`2sI1jo#A zX~LV%`{qewqLuf}@vb#b_AqSfpC;ZJ?obrQ4`911z&$@2O=q;OhKYN_$gfuC7?;C> zwT;8@TJ%qScm2B~d_$jNq40Ji>t9}4CkYp)BlGC4**k{ORdZQGoTJZ+88K}e=YTtX zrG{$7AOx~CV%Ss5x-2@taByq2)HjLI*VRXc)0QSt**mwW0M{XNc3VM@`-@&HPih}IjL62 z@eCi-BvQ=W^O*6TpPUs_VV2Lq(rX^MfBkWi7XA)Uf#`O!r_+1epLXBiBMca&L8NAxXk8Hf)$R%^l{ zZD{)!f_H9Vrq;GGRX(jGm_4=x6a9M$__n%%_xx~?Eaib}I;xeZqJqG~$<xqB;(E&cp{^nf&R{ylf*DB(q)E=ivPH>UjfWN_`&A1?EB@jQ5 zil7RSv15FvNxR{3S30^i?FU)?M?j@8wUAu$P z#>Y1~Zi5}Pm|+ZM5cbZQ@8oeXbY38!OLVuZpkbxWI1>B*5?(O#)6rPd*LkNwH!t0F zNwAW+8^yo<*ts_GR%ft@%Be3?>e8;P>mgS?!X=R>Z0iQ8b_wy}x-72IAL-xttYI7Y4Cs#U_B_HO z^h4L(M&sfZx<9-cPlQ}Fu4KQ3T5!1|p-<+$OY4vB`WP*z6-2HV)`X;y<152pB1rgl z4Zy!UXjans?)bf0b-oz#KCa6R3vp%*tm^v`C&n?ANY|E2odl3MW(}Wkzw4m zO@Or~6yz`l;g`YB8MdU^Hiw9QxrrR^M^~7)HYr zU}y#$O<(07uXqsX%(`t317KEY+m}vDt>(MIm<{>EV7MIUj*_*aJMhwAOoaDb>5@_0 z;~cxwIpK!E93S38wWfXvIoNeaMEb~(V&suu9{Al6JJ6Z!V%H26K8o3Odw%{p=gz9s zPRF{|bG09gYh**i>~hg=NpA!`R+@bHD3D}N*I`^@9S<$lqBuG_XKtPTXvE$6S7U&O zAmaRrQP1+1(0Iy3r-?UX#vJVRhuz=pNms9_wOMGzY7L;*;6_W*p?8N+t&+s=$W*R$ zLXgjgsWa#PJy);kWKFMVDuyA4@LTg9Is+oFp9XsE=|(FdOvjwph^ieZfN5o8kL{X1 z5T`AZDqbdFKgG{R*Nscz4rL>6@E=QeXJ!-iJ6+0hJ< zue|CiX7XPRr3P{z;Tv-D7Ckq48$*=Nm#?m0yR5r=C zJ4X3l(NOc6Jn^>q+vVK*pF>Crbz8!E%JW`ML(es$7gTTQD(-q#&p!wmx$ zobx8y62A(8`%`jxJ$dB3j~W64_kVkOVTgjtVD6xSURw*$kg!UxRx`@;Ia}Gd1YPB7 z)2hY)r!buuA^CiY8kA~g2c~Vi*(Pd8W(>W^yMLM-wfWz<5-%)!VUXEAbs0(!mhVbr z$2Tn`&b3=~)o^8p0^D+sTOUO*B}!!$Q(#Vzwnt{e%vN5SZ%|LkqH|-z#LBO~ura8# z+pUM&{nZk1y@Ps8aygsxpNu`tw^2N0+7xGJuKMo}PhZ}N90{iBR-Os#&=ppLFh#rW4xU7Src9wA4w3^()7ML!rRRjX6`FIZ zK!AgRiaQeE2e1GC1(E@6{s>!<*QI=wtO@0G#B{@jkeF`V+avEWt|dE)ejit=Jg?KN zlD$RO_CBp#;tBi4H&g>>my{8(?t=MzE-_+WO|IYwlB%H&2WoJRTPohIkGw9pddZEU zYgKPkG{}D&o$njNZS_KTcZpsTG%%uR3fHUw8@d*LV$(M!y_6kISDpfowDYd5n5M%i ziFzaQz?DwAa9y{1w6pBJOD82QeyA^jQ;Sw z{N6uLE0?`qySUO27F`Ta6ikI7<#Km9mCIG`-?EqZNBkWUD=r4u)J(8a)x#G*PAp7-ulb6v-xH^66Hf)D zT}LFX;)#+?az%v73ocAAHrP}k<^xXGQ7R9kc&ndX@c#f0SiIPiQc3g+H#9ai3MC~) GPeuxi4vKvM diff --git a/slurm.jl b/slurm.jl index b0a20356..e99a603e 100644 --- a/slurm.jl +++ b/slurm.jl @@ -1,5 +1,6 @@ # - +using Pkg +Pkg.activate(@__DIR__) try using Distributed, ClusterManagers @@ -10,7 +11,7 @@ end using Distributed, ClusterManagers -np = 50 # +np = 20 # addprocs(SlurmManager(np), job_file_loc = ARGS[1]) println("We are all connected and ready.") diff --git a/strategic_bidding.jl b/strategic_bidding.jl index c1ff932e..f4c85566 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -30,7 +30,7 @@ using Distributed # Parameters @everywhere max_eval = 100 @everywhere solver_lower_name = "Ipopt" -@everywhere casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" +@everywhere casename = "pglib_opf_case1354_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" @everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" @everywhere save_file = save_file_name * ".csv" @@ -64,13 +64,15 @@ using Distributed seeds = collect(1:10) experiements = Dict( - :LD_MMA => [nothing], - :LN_BOBYQA => [0.0], - :LD_CCSAQ => [nothing], - :LD_SLSQP => [nothing], - :LD_LBFGS => [nothing], - :LD_TNEWTON_PRECOND_RESTART => [nothing], - :LN_COBYLA => [0.0], + # :LD_MMA => [nothing], + # :LN_BOBYQA => [0.0], + # :LD_CCSAQ => [nothing], + # :LD_SLSQP => [nothing], + # :LD_LBFGS => [nothing], + # :LD_TNEWTON_PRECOND_RESTART => [nothing], + # :LN_COBYLA => [0.0], + :LN_NELDERMEAD => [0.0], + :LN_NEWUOA_BOUND => [0.0] ) @everywhere res = Dict( @@ -187,6 +189,10 @@ using Statistics results = CSV.read(save_file, DataFrame) +ignore_solver_upper = [:LD_LBFGS] + +results = results[[!(Symbol(sv_up) ∈ ignore_solver_upper) for sv_up in results.solver_upper], :] + maximum_per_seed = [maximum(results.profit[results.seed .== seed]) for seed in seeds] results.gap = (maximum_per_seed[results.seed] - results.profit) * 100 ./ maximum_per_seed[results.seed] From a83712b84993eb5007273f8899d71078e703f04f Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Thu, 24 Oct 2024 15:50:03 -0400 Subject: [PATCH 101/108] update final --- ...idding_nlopt_pglib_opf_case2869_pegase.csv | 17 +++++++++++++++++ ...idding_nlopt_pglib_opf_case2869_pegase.pdf | Bin 17034 -> 19360 bytes strategic_bidding.jl | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv index e557a2c7..d6eea0a1 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.csv @@ -47,3 +47,20 @@ LD_CCSAQ,Ipopt,nothing,8,843574.6079864681,4.987640110007977,47,3896.16326785087 LD_CCSAQ,Ipopt,nothing,9,893301.5427800094,4.964824798862503,53,4455.742611169815,4 LD_CCSAQ,Ipopt,nothing,10,870338.9921688911,4.91134686506627,60,4816.567409992218,4 LD_SLSQP,Ipopt,nothing,1,963555.3278867417,4.030547695525361,100,8082.192088127136,5 +LN_NELDERMEAD,Ipopt,0.0,1,445078.1430213009,1.2348499223505311,100,1329.5679678916931,5 +LN_NELDERMEAD,Ipopt,0.0,2,455690.4622564971,1.2768479925360656,100,1422.1589450836182,5 +LN_NELDERMEAD,Ipopt,0.0,3,453681.89531589224,1.268292117138062,100,1389.1774249076843,5 +LN_NELDERMEAD,Ipopt,0.0,4,445688.28183857014,1.2431566389557018,100,1524.381493806839,5 +LN_NELDERMEAD,Ipopt,0.0,5,438022.6043508742,1.2197590396224085,100,1577.4441611766815,5 +LN_NELDERMEAD,Ipopt,0.0,7,452235.4254595,1.2748403654661529,100,2957.7306208610535,5 +LN_NELDERMEAD,Ipopt,0.0,8,402582.56670931267,1.1039157409656242,100,1325.344002008438,5 +LN_NELDERMEAD,Ipopt,0.0,9,460929.0832423285,1.2936620440698983,100,1377.7193779945374,5 +LN_NELDERMEAD,Ipopt,0.0,10,441021.8828340604,1.234313197882759,100,1383.0285909175873,5 +LN_NEWUOA_BOUND,Ipopt,0.0,1,369089.77692523686,0.9878201820710817,100,1370.1421120166779,5 +LN_NEWUOA_BOUND,Ipopt,0.0,2,383773.56707333704,1.0405736572886726,100,1459.6795320510864,5 +LN_NEWUOA_BOUND,Ipopt,0.0,3,382966.3199129843,1.0285899909371885,100,1412.3048560619354,5 +LN_NEWUOA_BOUND,Ipopt,0.0,4,371114.4143419697,0.9958303959944159,100,1539.8862872123718,5 +LN_NEWUOA_BOUND,Ipopt,0.0,7,370847.88196845306,1.000010615588849,100,1651.1666140556335,5 +LN_NEWUOA_BOUND,Ipopt,0.0,8,384438.4956870938,1.0358707175496766,100,1306.5130269527435,5 +LN_NEWUOA_BOUND,Ipopt,0.0,9,375783.6996468839,1.0228448016738099,100,1501.0024898052216,5 +LN_NEWUOA_BOUND,Ipopt,0.0,10,362687.60243511904,0.9738140647887583,100,1411.0102169513702,5 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf b/results/strategic_bidding_nlopt_pglib_opf_case2869_pegase.pdf index e17c4e22347390f33718889878ac53e1d47053db..9b9f545563dce86113315686534fa1321457eee7 100644 GIT binary patch delta 18650 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 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index f4c85566..f6685fd9 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -30,7 +30,7 @@ using Distributed # Parameters @everywhere max_eval = 100 @everywhere solver_lower_name = "Ipopt" -@everywhere casename = "pglib_opf_case1354_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" +@everywhere casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" @everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" @everywhere save_file = save_file_name * ".csv" From fc0a3abec4ed5a53f7c9f181ee30c3ce913daed7 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Sat, 26 Oct 2024 11:21:47 -0400 Subject: [PATCH 102/108] update --- ...c_bidding_nlopt_pglib_opf_case2000_goc.csv | 73 ++++++++++++++++++ ...c_bidding_nlopt_pglib_opf_case2000_goc.pdf | Bin 0 -> 19278 bytes strategic_bidding.jl | 14 ++-- 3 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case2000_goc.csv create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case2000_goc.pdf diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2000_goc.csv b/results/strategic_bidding_nlopt_pglib_opf_case2000_goc.csv new file mode 100644 index 00000000..2dffa9fe --- /dev/null +++ b/results/strategic_bidding_nlopt_pglib_opf_case2000_goc.csv @@ -0,0 +1,73 @@ +solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status +LD_MMA,Ipopt,nothing,1,369523.35100522,6.281331171600099,57,2927.388349056244,4 +LD_MMA,Ipopt,nothing,2,319324.6427443133,6.434705664578061,57,2780.2165780067444,4 +LD_MMA,Ipopt,nothing,3,360352.16414853133,6.5808096766567,60,3062.843197107315,4 +LD_MMA,Ipopt,nothing,4,363082.0923797429,6.460565006687831,60,3123.9112889766693,4 +LD_MMA,Ipopt,nothing,5,374396.0648059043,6.328558034627275,57,2961.078145980835,4 +LD_MMA,Ipopt,nothing,6,358114.98152800533,6.191722678203054,58,2993.763321876526,4 +LD_MMA,Ipopt,nothing,7,358894.43974197324,6.275367062460499,58,2930.235079050064,4 +LD_MMA,Ipopt,nothing,8,346924.9173690083,6.330849145471191,61,3128.324747800827,4 +LD_MMA,Ipopt,nothing,9,353932.7637797668,6.591812236705967,58,2978.2513270378113,4 +LD_MMA,Ipopt,nothing,10,360641.74436154397,6.394534482828754,58,2845.8516099452972,4 +LN_BOBYQA,Ipopt,0.0,1,297977.62147699925,3.8930735360049527,100,1144.0847730636597,5 +LN_BOBYQA,Ipopt,0.0,2,299355.77298776305,3.9404747232021657,100,1082.5064489841461,5 +LN_BOBYQA,Ipopt,0.0,3,277492.64827409753,3.759131393699533,100,1122.313206911087,5 +LN_BOBYQA,Ipopt,0.0,4,307781.33752304706,4.059088566045905,100,1152.3991768360138,5 +LN_BOBYQA,Ipopt,0.0,5,295187.4306268543,3.859893460772294,100,1182.9611599445343,5 +LN_BOBYQA,Ipopt,0.0,6,273338.4756316369,3.6000920228228708,100,954.2082068920135,5 +LN_BOBYQA,Ipopt,0.0,7,295616.27458341373,3.7874468131431374,100,999.1024839878082,5 +LN_BOBYQA,Ipopt,0.0,8,301629.43296452175,3.904028118971772,100,973.1779978275299,5 +LN_BOBYQA,Ipopt,0.0,9,286589.33802552376,3.8070028445188564,100,993.2643630504608,5 +LN_BOBYQA,Ipopt,0.0,10,306357.57064501574,4.02694776337728,100,982.7908020019531,5 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,1,374285.5654676295,6.041712643937134,62,3104.1543350219727,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,4,378759.2477216333,6.0447506876567285,56,2764.367919921875,4 +LN_COBYLA,Ipopt,0.0,1,368770.77971220895,5.154334896050398,100,1043.3742561340332,5 +LN_COBYLA,Ipopt,0.0,2,371232.7086212399,5.234096040616625,100,1079.748556137085,5 +LN_COBYLA,Ipopt,0.0,3,340062.9032832226,4.925229352545818,100,1151.005206823349,5 +LN_COBYLA,Ipopt,0.0,4,368212.45521732606,5.284610897966902,100,1045.847148180008,5 +LN_COBYLA,Ipopt,0.0,5,365393.8200764219,5.200453644061956,100,1072.3246850967407,5 +LN_COBYLA,Ipopt,0.0,6,336817.12414661905,4.700154258913552,100,1098.9553389549255,5 +LN_COBYLA,Ipopt,0.0,7,362356.2631309275,4.952592988627873,100,1035.2730069160461,5 +LN_COBYLA,Ipopt,0.0,8,364983.8689721409,5.103900633505085,100,1041.5780990123749,5 +LN_COBYLA,Ipopt,0.0,9,345868.855240273,4.772237635224206,100,1089.8890671730042,5 +LN_COBYLA,Ipopt,0.0,10,375547.58596656244,5.284457465353491,100,1023.4139959812164,5 +LN_NELDERMEAD,Ipopt,0.0,1,368770.7797122093,5.154334896050398,100,1029.9820940494537,5 +LN_NELDERMEAD,Ipopt,0.0,2,371232.7086212405,5.234096040616625,100,1155.5187888145447,5 +LN_NELDERMEAD,Ipopt,0.0,3,340062.90328358323,4.931194237636005,100,1123.0712430477142,5 +LN_NELDERMEAD,Ipopt,0.0,4,368212.4552173274,5.284610897966902,100,1079.9773318767548,5 +LN_NELDERMEAD,Ipopt,0.0,5,365393.82007642306,5.200453644061957,100,1069.9418380260468,5 +LN_NELDERMEAD,Ipopt,0.0,6,336817.12414661655,4.700154258913552,100,1003.5985488891602,5 +LN_NELDERMEAD,Ipopt,0.0,7,362356.26313091733,4.952592988627873,100,1048.8882961273193,5 +LN_NELDERMEAD,Ipopt,0.0,8,364983.86897214066,5.103900633505085,100,969.1135029792786,5 +LN_NELDERMEAD,Ipopt,0.0,9,345868.8552402731,4.772237635224206,100,1127.8717539310455,5 +LN_NELDERMEAD,Ipopt,0.0,10,375547.5859665647,5.2844574653534915,100,1156.0585510730743,5 +LD_CCSAQ,Ipopt,nothing,1,374287.53230167157,6.041903274738079,61,3224.8785610198975,4 +LD_CCSAQ,Ipopt,nothing,2,386275.29943069845,5.983625043111539,60,2969.909969806671,4 +LD_CCSAQ,Ipopt,nothing,3,357828.6772629269,6.32449112674849,61,2903.837574005127,4 +LD_CCSAQ,Ipopt,nothing,4,378759.3506686415,6.044780871279457,59,2952.851217985153,4 +LD_CCSAQ,Ipopt,nothing,5,377585.18819117936,6.187277191005196,61,3007.517009973526,4 +LD_CCSAQ,Ipopt,nothing,6,357932.69040892785,5.856561795392329,61,3102.083848953247,4 +LD_CCSAQ,Ipopt,nothing,7,365780.49135180935,5.613264808250105,59,3015.02862405777,4 +LD_CCSAQ,Ipopt,nothing,8,345286.2376051097,5.821766834284973,58,2842.756448984146,4 +LD_CCSAQ,Ipopt,nothing,9,352682.0504250703,6.401288787080837,60,3061.2380788326263,4 +LD_CCSAQ,Ipopt,nothing,10,364185.8161657268,6.141413958902178,59,2888.3543219566345,4 +LN_NEWUOA_BOUND,Ipopt,0.0,1,296056.5240687083,3.865897697776273,100,1021.5894551277161,5 +LN_NEWUOA_BOUND,Ipopt,0.0,2,297281.23558716115,3.9125716510869886,100,1070.2945890426636,5 +LN_NEWUOA_BOUND,Ipopt,0.0,3,275479.41710101644,3.7330186146693944,100,1144.276999950409,5 +LN_NEWUOA_BOUND,Ipopt,0.0,4,305627.4761332325,4.030881678722003,100,1118.0375318527222,5 +LN_NEWUOA_BOUND,Ipopt,0.0,5,293247.731179087,3.8324415568176824,100,1148.7592511177063,5 +LN_NEWUOA_BOUND,Ipopt,0.0,6,270883.3068218491,4.22518758895544,100,986.9172720909119,5 +LN_NEWUOA_BOUND,Ipopt,0.0,7,293612.63005215995,5.289572893298926,100,1073.409821987152,5 +LN_NEWUOA_BOUND,Ipopt,0.0,8,299671.27750323794,3.876275233464007,100,1015.0241770744324,5 +LN_NEWUOA_BOUND,Ipopt,0.0,9,284526.2713338175,6.1455362066452155,100,1145.198306798935,5 +LN_NEWUOA_BOUND,Ipopt,0.0,10,304363.3091196187,3.998324157548252,100,1121.8096508979797,5 +LD_SLSQP,Ipopt,nothing,1,367656.99680260976,6.309903805593335,100,4979.890875816345,5 +LD_SLSQP,Ipopt,nothing,2,297264.6518479442,3.9125716510869886,1,64.51116108894348,4 +LD_SLSQP,Ipopt,nothing,3,275474.4664725441,3.7330186146693944,1,60.47740888595581,4 +LD_SLSQP,Ipopt,nothing,4,305611.4541170079,4.030881678722003,1,64.25777292251587,4 +LD_SLSQP,Ipopt,nothing,5,377031.3091568417,6.279839303967756,100,5056.611438989639,5 +LD_SLSQP,Ipopt,nothing,6,356265.1239745704,6.038739548828272,85,4219.168741941452,4 +LD_SLSQP,Ipopt,nothing,7,293608.42417688074,3.759529176498192,1,62.80996298789978,4 +LD_SLSQP,Ipopt,nothing,8,353997.8791784004,5.186601584731852,100,4799.876461029053,5 +LD_SLSQP,Ipopt,nothing,9,284516.79877157696,3.779076104265464,1,62.72271800041199,4 +LD_SLSQP,Ipopt,nothing,10,363296.23298497265,6.016711778607968,100,5127.965346813202,5 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2000_goc.pdf b/results/strategic_bidding_nlopt_pglib_opf_case2000_goc.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1c9b6b18e25b0e9d93c80d5a63d7910fb0a3ac69 GIT binary patch literal 19278 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

*!iBWk`vAyOG;T?| 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 Mk4j0Y>uMbR8$c>Kvj6}9 literal 0 HcmV?d00001 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index f6685fd9..bbd772ac 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -30,7 +30,7 @@ using Distributed # Parameters @everywhere max_eval = 100 @everywhere solver_lower_name = "Ipopt" -@everywhere casename = "pglib_opf_case2869_pegase" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" +@everywhere casename = "pglib_opf_case3970_goc.m" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" "pglib_opf_case2000_goc" @everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" @everywhere save_file = save_file_name * ".csv" @@ -64,13 +64,13 @@ using Distributed seeds = collect(1:10) experiements = Dict( - # :LD_MMA => [nothing], - # :LN_BOBYQA => [0.0], - # :LD_CCSAQ => [nothing], - # :LD_SLSQP => [nothing], + :LD_MMA => [nothing], + :LN_BOBYQA => [0.0], + :LD_CCSAQ => [nothing], + :LD_SLSQP => [nothing], # :LD_LBFGS => [nothing], - # :LD_TNEWTON_PRECOND_RESTART => [nothing], - # :LN_COBYLA => [0.0], + :LD_TNEWTON_PRECOND_RESTART => [nothing], + :LN_COBYLA => [0.0], :LN_NELDERMEAD => [0.0], :LN_NEWUOA_BOUND => [0.0] ) From 0cf88925d985b03c51172dd8ced4b4f64942a02f Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Sat, 26 Oct 2024 14:30:25 -0400 Subject: [PATCH 103/108] update --- opf.jl | 3 +++ ...bidding_nlopt_pglib_opf_case2868_rte.m.csv | 1 + ...c_bidding_nlopt_pglib_opf_case300_ieee.csv | 10 +++++++++ strategic_bidding.jl | 21 ++++++++++--------- 4 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv diff --git a/opf.jl b/opf.jl index 58d04635..e0d2697e 100644 --- a/opf.jl +++ b/opf.jl @@ -390,6 +390,9 @@ function test_bidding_nlopt(data; percen_bidding_nodes=0.1, Δp=0.0001, solver_u end opt = NLopt.Opt(solver_upper, num_bidding_nodes) + if solver_upper == :G_MLSL_LDS + NLopt.nlopt_set_local_optimizer(opt, NLopt.Opt(:LN_NELDERMEAD, num_bidding_nodes)) + end NLopt.lower_bounds!(opt, fill(0.0, num_bidding_nodes)) NLopt.upper_bounds!(opt, fill(pmax, num_bidding_nodes)) NLopt.xtol_rel!(opt, 1e-4) diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv b/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv new file mode 100644 index 00000000..d8df2fa6 --- /dev/null +++ b/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv @@ -0,0 +1 @@ +solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status diff --git a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv index 321bd87a..978dbdf9 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case300_ieee.csv @@ -89,3 +89,13 @@ LN_NEWUOA_BOUND,Ipopt,0.0,7,244099.06401609167,10.392530675873035,100,86.6611409 LN_NEWUOA_BOUND,Ipopt,0.0,8,41931.002594318015,1.18971351086284,100,75.69124221801758,5 LN_NEWUOA_BOUND,Ipopt,0.0,9,186785.7114113925,6.29186177667848,100,78.50683999061584,5 LN_NEWUOA_BOUND,Ipopt,0.0,10,30108.3054170366,0.9800887548124835,100,79.19660210609436,5 +G_MLSL_LDS,Ipopt,0.0,1,105853.85021551093,40.9637678014902,100,85.87584209442139,5 +G_MLSL_LDS,Ipopt,0.0,2,161395.64760316032,35.422872229648874,100,87.4148759841919,5 +G_MLSL_LDS,Ipopt,0.0,3,85866.78664958006,35.958121523824374,100,90.04950094223022,5 +G_MLSL_LDS,Ipopt,0.0,4,104546.12177641856,34.056326861683445,100,76.05526995658875,5 +G_MLSL_LDS,Ipopt,0.0,5,132577.46325762256,40.43476393507388,100,81.02201795578003,5 +G_MLSL_LDS,Ipopt,0.0,6,143977.16792034736,5.214501855637245,100,80.43115305900574,5 +G_MLSL_LDS,Ipopt,0.0,7,171313.9448243225,5.7162965290433005,100,83.96484804153442,5 +G_MLSL_LDS,Ipopt,0.0,8,129787.3066354752,4.8447342062259375,100,72.29825091362,5 +G_MLSL_LDS,Ipopt,0.0,9,125391.20812140916,35.508563827663444,100,92.51962304115295,5 +G_MLSL_LDS,Ipopt,0.0,10,145739.28083574225,35.126302700293266,100,83.76881694793701,5 diff --git a/strategic_bidding.jl b/strategic_bidding.jl index bbd772ac..aa5f1034 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -30,7 +30,7 @@ using Distributed # Parameters @everywhere max_eval = 100 @everywhere solver_lower_name = "Ipopt" -@everywhere casename = "pglib_opf_case3970_goc.m" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" "pglib_opf_case2000_goc" +@everywhere casename = "pglib_opf_case300_ieee" # "pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" "pglib_opf_case2000_goc" "pglib_opf_case2868_rte" @everywhere save_file_name = "results/strategic_bidding_nlopt_$(casename)" @everywhere save_file = save_file_name * ".csv" @@ -64,15 +64,16 @@ using Distributed seeds = collect(1:10) experiements = Dict( - :LD_MMA => [nothing], - :LN_BOBYQA => [0.0], - :LD_CCSAQ => [nothing], - :LD_SLSQP => [nothing], - # :LD_LBFGS => [nothing], - :LD_TNEWTON_PRECOND_RESTART => [nothing], - :LN_COBYLA => [0.0], - :LN_NELDERMEAD => [0.0], - :LN_NEWUOA_BOUND => [0.0] + # :LD_MMA => [nothing], + # :LN_BOBYQA => [0.0], + # :LD_CCSAQ => [nothing], + # :LD_SLSQP => [nothing], + # # :LD_LBFGS => [nothing], + # :LD_TNEWTON_PRECOND_RESTART => [nothing], + # :LN_COBYLA => [0.0], + # :LN_NELDERMEAD => [0.0], + # :LN_NEWUOA_BOUND => [0.0], + # :G_MLSL_LDS => [0.0], ) @everywhere res = Dict( From d082f73938c494387fb164f620df0f21a4e7d4d3 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Sat, 26 Oct 2024 15:57:04 -0400 Subject: [PATCH 104/108] update --- ...bidding_nlopt_pglib_opf_case2868_rte.m.csv | 73 ++++++++++++++++++ ...bidding_nlopt_pglib_opf_case2868_rte.m.pdf | Bin 0 -> 19369 bytes 2 files changed, 73 insertions(+) create mode 100644 results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.pdf diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv b/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv index d8df2fa6..e4bedfa6 100644 --- a/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv +++ b/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.csv @@ -1 +1,74 @@ solver_upper,solver_lower,Δp,seed,profit,market_share,num_evals,time,status +LD_MMA,Ipopt,nothing,1,629583.9288212913,6.890645774703093,58,3505.258134126663,4 +LD_MMA,Ipopt,nothing,2,627113.0582729869,6.977007604840815,57,3446.124926805496,4 +LD_MMA,Ipopt,nothing,3,503483.0698924671,9.437976029449642,31,1844.9283549785614,4 +LD_MMA,Ipopt,nothing,4,660150.8766179021,6.745448274791942,54,3128.340518951416,4 +LD_MMA,Ipopt,nothing,5,515708.90176392137,8.192006367082637,55,3282.953973054886,4 +LD_MMA,Ipopt,nothing,6,593330.5314686802,6.607485304488751,51,3126.637830018997,4 +LD_MMA,Ipopt,nothing,7,641960.0279960671,6.677319587937669,56,3369.3064410686493,4 +LD_MMA,Ipopt,nothing,8,441400.4075766634,9.50172834519057,52,3100.031136035919,4 +LD_MMA,Ipopt,nothing,9,505161.25023751793,9.46362463803075,54,3583.3756749629974,4 +LD_MMA,Ipopt,nothing,10,493257.18844268494,9.283018737480724,47,2828.7756350040436,4 +LN_BOBYQA,Ipopt,0.0,1,398781.7018975408,2.681879516265251,100,1111.6657028198242,5 +LN_BOBYQA,Ipopt,0.0,2,424994.5800923622,2.8237977709425417,100,1057.730427980423,5 +LN_BOBYQA,Ipopt,0.0,3,413469.5047721841,2.7924051654318505,100,1075.452929019928,5 +LN_BOBYQA,Ipopt,0.0,4,408094.1512728784,2.703931462513121,100,1070.8178579807281,5 +LN_BOBYQA,Ipopt,0.0,5,391744.736736471,2.6342395759849984,100,1036.4698250293732,5 +LN_BOBYQA,Ipopt,0.0,6,405613.8253293786,2.747279007315161,100,1144.034110069275,5 +LN_BOBYQA,Ipopt,0.0,7,405356.3161075126,2.714075307402132,100,1039.9613230228424,5 +LN_BOBYQA,Ipopt,0.0,8,416907.8601131197,2.8107092203326007,100,1105.951191186905,5 +LN_BOBYQA,Ipopt,0.0,9,407432.24270854925,2.7734348694199,100,1056.5739369392395,5 +LN_BOBYQA,Ipopt,0.0,10,388927.06299417454,2.6444588672801075,100,1018.8784649372101,5 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,4,644985.796929955,8.088733324695841,38,2319.878350019455,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,5,604839.8342306449,6.224460851907855,65,3976.07225894928,4 +LD_TNEWTON_PRECOND_RESTART,Ipopt,nothing,8,579232.5206609542,5.863372546358108,56,3459.778429031372,4 +LN_COBYLA,Ipopt,0.0,1,473527.52936282655,3.2914449436356303,100,1078.0652148723602,5 +LN_COBYLA,Ipopt,0.0,2,453459.1354854593,3.0572512242586547,100,1023.7090928554535,5 +LN_COBYLA,Ipopt,0.0,3,489848.76781296195,3.4914091683973116,100,1097.9070301055908,5 +LN_COBYLA,Ipopt,0.0,4,483885.64886411006,3.3273901264543326,100,962.7221291065216,5 +LN_COBYLA,Ipopt,0.0,5,456512.7012026482,3.2580187147012483,100,1095.7954370975494,5 +LN_COBYLA,Ipopt,0.0,6,475336.5075895495,3.408706698951814,100,1169.6885168552399,5 +LN_COBYLA,Ipopt,0.0,7,484500.91024239425,3.413132685829475,100,989.1927120685577,5 +LN_COBYLA,Ipopt,0.0,8,486279.8287879631,3.457433477227418,100,1181.4076688289642,5 +LN_COBYLA,Ipopt,0.0,9,460900.76954934996,3.2901854106692205,100,1078.2053899765015,5 +LN_COBYLA,Ipopt,0.0,10,470602.82532957214,3.334392838176782,100,1000.6699910163879,5 +LN_NELDERMEAD,Ipopt,0.0,1,473527.5293628363,3.2914449436356303,100,1118.1400711536407,5 +LN_NELDERMEAD,Ipopt,0.0,2,453459.1354854287,3.0572512242586547,100,1039.071209192276,5 +LN_NELDERMEAD,Ipopt,0.0,3,489848.76781296177,3.4914091683973116,100,1075.2348010540009,5 +LN_NELDERMEAD,Ipopt,0.0,4,483885.6488640899,3.3273901264543326,100,1033.4427018165588,5 +LN_NELDERMEAD,Ipopt,0.0,5,456512.6996559689,3.2580187147012483,100,984.0106539726257,5 +LN_NELDERMEAD,Ipopt,0.0,6,475336.507588969,3.408706698951814,100,1242.2352900505066,5 +LN_NELDERMEAD,Ipopt,0.0,7,484500.9102423927,3.413132685829475,100,1054.6953508853912,5 +LN_NELDERMEAD,Ipopt,0.0,8,486279.82878796756,3.457433477227418,100,1140.80641913414,5 +LN_NELDERMEAD,Ipopt,0.0,9,460900.7695494116,3.2901854106692205,100,1067.6936299800873,5 +LN_NELDERMEAD,Ipopt,0.0,10,470602.82532957214,3.334392838176782,100,1035.6403188705444,5 +LD_CCSAQ,Ipopt,nothing,1,624324.240321726,7.229484587659512,59,3568.3671629428864,4 +LD_CCSAQ,Ipopt,nothing,2,633243.8458275096,7.202017625481673,52,3150.5890390872955,4 +LD_CCSAQ,Ipopt,nothing,3,604991.2995579528,6.851712961741274,56,3349.7063908576965,4 +LD_CCSAQ,Ipopt,nothing,4,654112.6526452895,6.849097981255742,55,3309.5860052108765,4 +LD_CCSAQ,Ipopt,nothing,5,598384.9908440018,6.875877332376444,47,2825.262242794037,4 +LD_CCSAQ,Ipopt,nothing,6,599138.5529587846,6.570572979767129,43,2549.805088043213,4 +LD_CCSAQ,Ipopt,nothing,7,633182.7971276582,6.8977303515942365,57,3401.1067910194397,4 +LD_CCSAQ,Ipopt,nothing,8,563108.9678337795,7.421622914249337,59,3577.2335109710693,4 +LD_CCSAQ,Ipopt,nothing,9,605788.7008906608,6.966615464773943,57,3441.102548122406,4 +LD_CCSAQ,Ipopt,nothing,10,560763.6032972333,6.715269444455808,59,3569.4729290008545,4 +LN_NEWUOA_BOUND,Ipopt,0.0,1,396694.5937481017,2.668257454596464,100,1160.3855929374695,5 +LN_NEWUOA_BOUND,Ipopt,0.0,2,423004.55900321295,112.7964266242699,100,1102.4114229679108,5 +LN_NEWUOA_BOUND,Ipopt,0.0,3,411612.76630639104,3.1219005757152187,100,1098.7946500778198,5 +LN_NEWUOA_BOUND,Ipopt,0.0,4,406143.30047178635,2.6898943004534064,100,975.0623390674591,5 +LN_NEWUOA_BOUND,Ipopt,0.0,5,389837.04905492143,2.621566390611279,100,1086.1601371765137,5 +LN_NEWUOA_BOUND,Ipopt,0.0,6,403540.38210378896,2.5112192725532276,100,1164.4824831485748,5 +LN_NEWUOA_BOUND,Ipopt,0.0,7,403425.04139754584,2.7011857301054225,100,1085.67134308815,5 +LN_NEWUOA_BOUND,Ipopt,0.0,8,415381.09212414495,2.7980494975362054,100,1140.2639210224152,5 +LN_NEWUOA_BOUND,Ipopt,0.0,9,404450.27723066194,2.762864452960727,100,1073.3985180854797,5 +LN_NEWUOA_BOUND,Ipopt,0.0,10,386770.63151524833,2.630424732075897,100,1050.3003928661346,5 +LD_SLSQP,Ipopt,nothing,1,396692.3285684554,2.668257454596464,1,79.38984894752502,4 +LD_SLSQP,Ipopt,nothing,2,422994.42245616694,2.8107528763948797,1,76.5017671585083,4 +LD_SLSQP,Ipopt,nothing,3,595256.5472788628,5.229886902134434,100,6124.816920995712,5 +LD_SLSQP,Ipopt,nothing,4,406133.88114358974,2.6898943004534064,1,75.31735301017761,4 +LD_SLSQP,Ipopt,nothing,5,599438.027210647,6.302735019143101,100,6119.690332889557,5 +LD_SLSQP,Ipopt,nothing,6,403529.5235328151,2.7339725059105686,1,77.51345705986023,4 +LD_SLSQP,Ipopt,nothing,7,635602.1987549539,6.280930279767297,100,6045.816938877106,5 +LD_SLSQP,Ipopt,nothing,8,573160.8856220098,5.296483320893043,100,6050.58663392067,5 +LD_SLSQP,Ipopt,nothing,9,602261.8160636103,6.863588314096251,100,6154.7616510391235,5 +LD_SLSQP,Ipopt,nothing,10,386764.4662304388,2.630424732075897,1,76.16425085067749,4 diff --git a/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.pdf b/results/strategic_bidding_nlopt_pglib_opf_case2868_rte.m.pdf new file mode 100644 index 0000000000000000000000000000000000000000..547e197398d7e06f6506742fb89099db2a1bec1f GIT binary patch literal 19369 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 TzrGvzG}u62N=xg4BV6i#YFF4P literal 0 HcmV?d00001 From cf0725931f96cd2b9569bccc755b2a2a0fbd442f Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Sun, 27 Oct 2024 18:04:48 -0400 Subject: [PATCH 105/108] update --- results/complexity_mpec.csv | 5 ++ results/complexity_mpec.pdf | Bin 0 -> 7194 bytes src/bilevel.jl | 117 ++++++++++++++++++++++++++++++++++++ strategic_bidding.jl | 74 +++++++++++++++++++---- 4 files changed, 185 insertions(+), 11 deletions(-) create mode 100644 results/complexity_mpec.csv create mode 100644 results/complexity_mpec.pdf create mode 100644 src/bilevel.jl diff --git a/results/complexity_mpec.csv b/results/complexity_mpec.csv new file mode 100644 index 00000000..c3264db0 --- /dev/null +++ b/results/complexity_mpec.csv @@ -0,0 +1,5 @@ +nodes,nlp,exact +7,53.13220869623141,756.8090725907176 +8,352.3375626043406,9864.554924874792 +9,2649.935359301545,21176.209872397583 +10,182.72862915760024,56244.64081442338 diff --git a/results/complexity_mpec.pdf b/results/complexity_mpec.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0d4496c0708f0ae0c43a283accf41b7d15ee16cc GIT binary patch literal 7194 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.Optimizer{Float64}(Gurobi.Optimizer()) + ) + BilevelJuMP.set_mode(model, + BilevelJuMP.FortunyAmatMcCarlMode(dual_big_M = 1000) + ) + set_lower_bound.(lambda, 0.0) + set_upper_bound.(lambda, 1000.0) + start_time = time() + optimize!(model) + end_time = time() + return end_time - start_time +end + +@everywhere function calculate_profit(evaluator_lower_level, demand_equilibrium, gS, _qS, qS) + fix.(_qS, value.(qS)) + # @constraint(evaluator_lower_level, _qS .== value.(qS)) + optimize!(evaluator_lower_level) + return dot(value.(gS), dual.(demand_equilibrium)) +end + +@everywhere function compare_methods(num_nodes=10; seed=1) + bilevel_model, qS, lambda = build_bilevel(num_nodes; seed=seed) + evaluator_lower_level = JuMP.Model(Gurobi.Optimizer) + @variable(evaluator_lower_level, _qS[i=1:num_nodes]) + evaluator_lower_level, demand_equilibrium, gS = build_lower_level(num_nodes; seed=seed, jump_model=evaluator_lower_level, qS=_qS) + nlp_time = solve_nlp(bilevel_model) + opf_time = time() + nlp_profit = calculate_profit(evaluator_lower_level, demand_equilibrium, gS, _qS, qS) + opf_time = time() - opf_time + exact_time = solve_bilevel_exact(bilevel_model, lambda) + # exact_profit = calculate_profit(evaluator_lower_level, demand_equilibrium, gS, _qS, qS) + return nlp_time / opf_time, exact_time / opf_time +end + +nodes = 7:10:200 +results = pmap(x->compare_methods(x), nodes) + +# Save results +using CSV, DataFrames +CSV.write(joinpath(@__DIR__, "results", "complexity_mpec.csv"), DataFrame(nodes=nodes, nlp=[r[1] for r in results], exact=[r[2] for r in results])) + +using Plots +# log y axis scale +plt = Plots.plot(nodes, [r[1] for r in results], label="NLP", xlabel="Number of nodes", ylabel="Time (x OPF)", yscale=:log10) +Plots.plot!(plt, nodes, [r[2] for r in results], label="Exact", xlabel="Number of nodes", ylabel="Time (x OPF)", yscale=:log10) +Plots.savefig(plt, joinpath(@__DIR__, "results", "complexity_mpec.pdf")) + + + diff --git a/strategic_bidding.jl b/strategic_bidding.jl index aa5f1034..c566948b 100644 --- a/strategic_bidding.jl +++ b/strategic_bidding.jl @@ -64,15 +64,15 @@ using Distributed seeds = collect(1:10) experiements = Dict( - # :LD_MMA => [nothing], - # :LN_BOBYQA => [0.0], - # :LD_CCSAQ => [nothing], - # :LD_SLSQP => [nothing], - # # :LD_LBFGS => [nothing], - # :LD_TNEWTON_PRECOND_RESTART => [nothing], - # :LN_COBYLA => [0.0], - # :LN_NELDERMEAD => [0.0], - # :LN_NEWUOA_BOUND => [0.0], + :LD_MMA => [nothing], + :LN_BOBYQA => [0.0], + :LD_CCSAQ => [nothing], + :LD_SLSQP => [nothing], + # :LD_LBFGS => [nothing], + :LD_TNEWTON_PRECOND_RESTART => [nothing], + :LN_COBYLA => [0.0], + :LN_NELDERMEAD => [0.0], + :LN_NEWUOA_BOUND => [0.0], # :G_MLSL_LDS => [0.0], ) @@ -190,7 +190,7 @@ using Statistics results = CSV.read(save_file, DataFrame) -ignore_solver_upper = [:LD_LBFGS] +ignore_solver_upper = [:LD_LBFGS; :G_MLSL_LDS] results = results[[!(Symbol(sv_up) ∈ ignore_solver_upper) for sv_up in results.solver_upper], :] @@ -215,4 +215,56 @@ plt = scatter(results_d.market_share_mean, results_d.gap_mean, group=results_d.s ) # save -savefig(plt, "results/strategic_bidding_nlopt_$(casename).pdf") \ No newline at end of file +savefig(plt, "results/strategic_bidding_nlopt_$(casename).pdf") + +# save a csv and plot for all cases the percentage gain (or loss) the best gradient-solver (Δp=="nothing") is when compared to the best non-gradient solver (Δp!=nothing) (inform mean and std) +using Plots +using Statistics +using DataFrames, CSV +seeds = collect(1:10) +cases = ["pglib_opf_case300_ieee", "pglib_opf_case1354_pegase", "pglib_opf_case2869_pegase", "pglib_opf_case2000_goc", "pglib_opf_case2868_rte.m"] +ignore_solver_upper = [:LD_LBFGS; :G_MLSL_LDS] +avg_gains = Array{Float64}(undef, length(cases)) +std_gains = Array{Float64}(undef, length(cases)) +avg_gains_all = Array{Float64}(undef, length(cases)) +std_gains_all = Array{Float64}(undef, length(cases)) +for (i, casename) in enumerate(cases) + save_file = "results/strategic_bidding_nlopt_$(casename).csv" + results = CSV.read(save_file, DataFrame) + results = results[[!(Symbol(sv_up) ∈ ignore_solver_upper) for sv_up in results.solver_upper], :] + results_d = combine(groupby(results, [:solver_upper, :Δp]), :profit => mean) + solver_upper_grad = results_d[results_d.Δp .== "nothing", :].solver_upper + solver_upper_nograd = results_d[results_d.Δp .!= "nothing", :].solver_upper + best_solver_upper_grad = solver_upper_grad[argmax(results_d[results_d.Δp .== "nothing", :].profit_mean)] + best_solver_upper_nograd = solver_upper_nograd[argmax(results_d[results_d.Δp .!= "nothing", :].profit_mean)] + mean_per_seed_non_gradient = [mean(results.profit[(results.seed .== seed) .& (results.Δp .!= "nothing")]) for seed in seeds] + mean_per_seed_gradient = [mean(results.profit[(results.seed .== seed) .& (results.Δp .== "nothing")]) for seed in seeds] + gains = [] + for seed in seeds + profit_grad = results[(results.seed .== seed) .* (results.solver_upper .== best_solver_upper_grad), :] + if isempty(profit_grad) + continue + end + profit_grad = profit_grad.profit[1] + profit_nograd = results[(results.seed .== seed) .* (results.solver_upper .== best_solver_upper_nograd), :] + if isempty(profit_nograd) + continue + end + profit_nograd = profit_nograd.profit[1] + push!(gains, (profit_grad - profit_nograd) * 100 / profit_nograd) + end + avg_gains[i] = mean(gains) + try + std_gains[i] = std(gains) + catch e + std_gains[i] = 0.0 + end + gains_all = (mean_per_seed_gradient .- mean_per_seed_non_gradient) * 100 ./ mean_per_seed_non_gradient + avg_gains_all[i] = mean(gains_all) + std_gains_all[i] = std(gains_all) +end + +# save +df = DataFrame(casename=cases, Best_Solver_Improvement_AVG=avg_gains, Best_Solver_Improvement_STD=std_gains, All_Solvers_Improvement_AVG=avg_gains_all, All_Solvers_Improvement_STD=std_gains_all) + + From 68cdf1773775b8a4797bd3296be95e28ac1f152d Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Mon, 28 Oct 2024 09:27:08 -0400 Subject: [PATCH 106/108] update --- src/bilevel.jl => bilevel.jl | 17 ++++++++++++++--- results/complexity_mpec.csv | 14 ++++++++++---- results/complexity_mpec.pdf | Bin 7194 -> 7907 bytes 3 files changed, 24 insertions(+), 7 deletions(-) rename src/bilevel.jl => bilevel.jl (89%) diff --git a/src/bilevel.jl b/bilevel.jl similarity index 89% rename from src/bilevel.jl rename to bilevel.jl index 700275f3..7f156a10 100644 --- a/src/bilevel.jl +++ b/bilevel.jl @@ -27,7 +27,7 @@ using Distributed # circle network with num_nodes extra random lines comb = collect(combinations(1:num_nodes,2)) num_comb = length(comb) - rand_lines = comb[sample(1:num_comb, num_nodes * 2, replace = false)] + rand_lines = comb[sample(1:num_comb, ceil(Int, num_nodes / 2), replace = false)] lines = vcat([(i, i+1) for i=1:num_nodes-1], [(num_nodes, 1)], rand_lines) num_lines = length(lines) line_limits = rand(1:1:100, num_lines) @@ -87,6 +87,7 @@ end end @everywhere function compare_methods(num_nodes=10; seed=1) + try bilevel_model, qS, lambda = build_bilevel(num_nodes; seed=seed) evaluator_lower_level = JuMP.Model(Gurobi.Optimizer) @variable(evaluator_lower_level, _qS[i=1:num_nodes]) @@ -98,14 +99,24 @@ end exact_time = solve_bilevel_exact(bilevel_model, lambda) # exact_profit = calculate_profit(evaluator_lower_level, demand_equilibrium, gS, _qS, qS) return nlp_time / opf_time, exact_time / opf_time + catch e + return NaN, NaN + end end -nodes = 7:10:200 +nodes = 7:2:25 results = pmap(x->compare_methods(x), nodes) +# success nodes +_results = [(i, (r[1], r[2])) for (i,r) in enumerate(results) if !isnan(r[1]) && !isnan(r[2])] +nodes = [nodes[r[1]] for r in _results] +results = [r[2] for r in _results] + # Save results using CSV, DataFrames -CSV.write(joinpath(@__DIR__, "results", "complexity_mpec.csv"), DataFrame(nodes=nodes, nlp=[r[1] for r in results], exact=[r[2] for r in results])) +df_old = CSV.read(joinpath(@__DIR__, "results", "complexity_mpec.csv")) +df = DataFrame(nodes=nodes, nlp=[r[1] for r in results], exact=[r[2] for r in results]) +CSV.write(joinpath(@__DIR__, "results", "complexity_mpec.csv"), df) using Plots # log y axis scale diff --git a/results/complexity_mpec.csv b/results/complexity_mpec.csv index c3264db0..95d370d9 100644 --- a/results/complexity_mpec.csv +++ b/results/complexity_mpec.csv @@ -1,5 +1,11 @@ nodes,nlp,exact -7,53.13220869623141,756.8090725907176 -8,352.3375626043406,9864.554924874792 -9,2649.935359301545,21176.209872397583 -10,182.72862915760024,56244.64081442338 +7,1.826055306314643,2.4007004113055763 +9,1.8254023963015003,4.765382874031797 +11,1.8909989527585487,4.443772287067811 +13,1.936895375875876,3.2488053353878232 +15,1.9941189036660998,3.236170364379556 +17,2.4846767455272536,12.518518482602955 +19,2.196193977401741,9.148711559589 +21,2.1941770659897473,11.130893060174877 +23,2.0267384964434783,16.131121391718693 +25,2.124540178988656,37.568622204061555 diff --git a/results/complexity_mpec.pdf b/results/complexity_mpec.pdf index 0d4496c0708f0ae0c43a283accf41b7d15ee16cc..3e18d195a971c456fcae6eeb929546cb231f7913 100644 GIT binary patch delta 7105 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 delta 6386 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 A4FCWD From e0225475abd0acf1bd4d22682873733112c53a37 Mon Sep 17 00:00:00 2001 From: Andrew David Werner Rosemberg Date: Tue, 29 Oct 2024 18:59:52 -0400 Subject: [PATCH 107/108] update --- bilevel.jl | 21 ++++++++++++--------- results/complexity_mpec.csv | 21 +++++++++++---------- results/complexity_mpec.pdf | Bin 7907 -> 8001 bytes slurm.jl | 2 +- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/bilevel.jl b/bilevel.jl index 7f156a10..75dd30fe 100644 --- a/bilevel.jl +++ b/bilevel.jl @@ -66,7 +66,7 @@ end @everywhere function solve_bilevel_exact(model, lambda) set_optimizer(model, - ()->QuadraticToBinary.Optimizer{Float64}(Gurobi.Optimizer()) + ()->QuadraticToBinary.Optimizer{Float64}(MOI.instantiate(optimizer_with_attributes(Gurobi.Optimizer, "MIPGap" => 0.1))) ) BilevelJuMP.set_mode(model, BilevelJuMP.FortunyAmatMcCarlMode(dual_big_M = 1000) @@ -87,7 +87,7 @@ end end @everywhere function compare_methods(num_nodes=10; seed=1) - try + # try bilevel_model, qS, lambda = build_bilevel(num_nodes; seed=seed) evaluator_lower_level = JuMP.Model(Gurobi.Optimizer) @variable(evaluator_lower_level, _qS[i=1:num_nodes]) @@ -99,12 +99,12 @@ end exact_time = solve_bilevel_exact(bilevel_model, lambda) # exact_profit = calculate_profit(evaluator_lower_level, demand_equilibrium, gS, _qS, qS) return nlp_time / opf_time, exact_time / opf_time - catch e - return NaN, NaN - end + # catch e + # return NaN, NaN + # end end -nodes = 7:2:25 +nodes = 7:5:57 results = pmap(x->compare_methods(x), nodes) # success nodes @@ -114,14 +114,17 @@ results = [r[2] for r in _results] # Save results using CSV, DataFrames -df_old = CSV.read(joinpath(@__DIR__, "results", "complexity_mpec.csv")) df = DataFrame(nodes=nodes, nlp=[r[1] for r in results], exact=[r[2] for r in results]) +if isfile(joinpath(@__DIR__, "results", "complexity_mpec.csv")) + df_old = CSV.read(joinpath(@__DIR__, "results", "complexity_mpec.csv"), DataFrame) + df = vcat(df_old, df) +end CSV.write(joinpath(@__DIR__, "results", "complexity_mpec.csv"), df) using Plots # log y axis scale -plt = Plots.plot(nodes, [r[1] for r in results], label="NLP", xlabel="Number of nodes", ylabel="Time (x OPF)", yscale=:log10) -Plots.plot!(plt, nodes, [r[2] for r in results], label="Exact", xlabel="Number of nodes", ylabel="Time (x OPF)", yscale=:log10) +plt = Plots.plot(df.nodes, df.nlp, label="NLP", xlabel="Number of nodes", ylabel="Time (x OPF)", yscale=:log10) +Plots.plot!(plt, df.nodes, df.exact, label="Exact", xlabel="Number of nodes", ylabel="Time (x OPF)", yscale=:log10) Plots.savefig(plt, joinpath(@__DIR__, "results", "complexity_mpec.pdf")) diff --git a/results/complexity_mpec.csv b/results/complexity_mpec.csv index 95d370d9..07674d4b 100644 --- a/results/complexity_mpec.csv +++ b/results/complexity_mpec.csv @@ -1,11 +1,12 @@ nodes,nlp,exact -7,1.826055306314643,2.4007004113055763 -9,1.8254023963015003,4.765382874031797 -11,1.8909989527585487,4.443772287067811 -13,1.936895375875876,3.2488053353878232 -15,1.9941189036660998,3.236170364379556 -17,2.4846767455272536,12.518518482602955 -19,2.196193977401741,9.148711559589 -21,2.1941770659897473,11.130893060174877 -23,2.0267384964434783,16.131121391718693 -25,2.124540178988656,37.568622204061555 +7,1.9666185609002593,2.7846410583761947 +12,2.0380107267563576,3.1072689575976105 +17,3.09061303995118,10.24712725907874 +22,2.297800196291057,13.512257289968211 +27,2.343125283792045,20.846918358124633 +32,356.02001668056715,154373.9398308114 +37,437.41794076766814,40769.94301293702 +42,276.7964300892478,452999.32044198894 +47,706.4255439924314,6.779457147587512e6 +52,745.8290398126463,3.3012040145018916e6 +57,670.5347723909827,3.824821345983114e6 diff --git a/results/complexity_mpec.pdf b/results/complexity_mpec.pdf index 3e18d195a971c456fcae6eeb929546cb231f7913..885a022d7879cffa324c2bf2b3c834bf351b52b0 100644 GIT binary patch delta 7209 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 delta 7101 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 Date: Fri, 21 Feb 2025 18:34:19 -0500 Subject: [PATCH 108/108] time tables --- results/time_summary.csv | 6 ++++ tables.jl | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 results/time_summary.csv create mode 100644 tables.jl diff --git a/results/time_summary.csv b/results/time_summary.csv new file mode 100644 index 00000000..5fb5779f --- /dev/null +++ b/results/time_summary.csv @@ -0,0 +1,6 @@ +Best Improvement AVG,Best Improvement STD,Overall Improvement AVG,Overall Improvement STD,case +19.0,24.8327740429189,41.260000000000005,8.133360655691813,pglib_opf_case300_ieee +41.0,25.342103744997615,49.63166666666667,16.57324269588755,pglib_opf_case1354_pegase +23.7,22.7305472388541,42.50833333333333,16.63823732725389,pglib_opf_case2869_pegase +36.5,12.868998061663973,44.1,16.24431524496425,pglib_opf_case2000_goc +44.4,5.601587076693333,48.25,16.796714846351048,pglib_opf_case2868_rte.m diff --git a/tables.jl b/tables.jl new file mode 100644 index 00000000..d709c7e2 --- /dev/null +++ b/tables.jl @@ -0,0 +1,66 @@ +using DataFrames, Statistics, CSV + +function compute_improvements(df::DataFrame) + # Get all unique seeds in the dataframe + seeds = unique(df.seed) + + best_improvements = Float64[] + overall_improvements = Float64[] + + for s in seeds + # Filter data for the current seed + df_seed = df[df.seed .== s, :] + + # Select rows for LD and LN methods. + # We assume that the 'solver_upper' column indicates "LD" or "LN" at the beginning. + ld = df_seed[startswith.(df_seed.solver_upper, "LD"), :] + ln = df_seed[startswith.(df_seed.solver_upper, "LN"), :] + + # Skip this seed if one of the groups is missing + if nrow(ld) == 0 || nrow(ln) == 0 + continue + end + + # For the best improvement: select the row with the best (highest) profit from each group. + ld_best = ld[findmax(ld.profit)[2], :] + ln_best = ln[findmax(ln.profit)[2], :] + + # Compute percentage improvement: how much lower LD's evaluations are compared to LN's. + best_impr = ((ln_best.num_evals - ld_best.num_evals) / ln_best.num_evals) * 100 + push!(best_improvements, best_impr) + + # For overall improvement: compute average num_evals for LD and LN. + ld_avg = mean(ld.num_evals) + ln_avg = mean(ln.num_evals) + overall_impr = ((ln_avg - ld_avg) / ln_avg) * 100 + push!(overall_improvements, overall_impr) + end + + # Compute overall averages and standard deviations across seeds. + best_avg = mean(best_improvements) + best_std = std(best_improvements) + overall_avg = mean(overall_improvements) + overall_std = std(overall_improvements) + + # Return the results as a one-row DataFrame. + return DataFrame( + "Best Improvement AVG" => best_avg, + "Best Improvement STD" => best_std, + "Overall Improvement AVG" => overall_avg, + "Overall Improvement STD" => overall_std, + ) +end + +# Example usage +prefix = "strategic_bidding_nlopt_" +case_names = ["pglib_opf_case300_ieee" "pglib_opf_case1354_pegase" "pglib_opf_case2869_pegase" "pglib_opf_case2000_goc" "pglib_opf_case2868_rte.m"] +df = DataFrame() +for case in case_names + file = "results/$(prefix)$(case).csv" + df_case = CSV.read(file, DataFrame) + df_results = compute_improvements(df_case) + df_results.case = [case] + append!(df, df_results) +end + +CSV.write("results/time_summary.csv", df) \ No newline at end of file