From aff115a216fad4ab2dfdbb82ffb239fdf4e3dec9 Mon Sep 17 00:00:00 2001 From: jalving Date: Thu, 4 Jan 2024 16:06:49 -0800 Subject: [PATCH] got quadratic working --- Project.toml | 1 + src/dev1.jl | 31 +++-- src/dev2.jl | 40 ++++--- src/dev3.jl | 7 ++ src/graph_backend.jl | 183 ++++++++++++++-------------- src/jump_interop.jl | 280 ++++++++++++++++++++++++++++++++++++++++++- src/optiedge.jl | 10 +- src/optigraph.jl | 16 +++ src/optinode.jl | 17 ++- 9 files changed, 459 insertions(+), 126 deletions(-) create mode 100644 src/dev3.jl diff --git a/Project.toml b/Project.toml index a3fea14..51d5f65 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,7 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" diff --git a/src/dev1.jl b/src/dev1.jl index 8e1984d..bf767fd 100644 --- a/src/dev1.jl +++ b/src/dev1.jl @@ -1,5 +1,6 @@ using Plasmo -using HiGHS +# using HiGHS +using Ipopt graph = OptiGraph(; name=:g1) @@ -8,6 +9,9 @@ n1 = Plasmo.add_node(graph) @variable(n1, y >= 0) @constraint(n1, ref1, x+y <= 10) +# TODO: quadratic functions +@constraint(n1, refq, x^2 + y^2 <= 2) + n2 = Plasmo.add_node(graph) @variable(n2, x >= 1) @variable(n2, y >= 2) @@ -15,22 +19,25 @@ n2 = Plasmo.add_node(graph) # linking constraint edge1 = Plasmo.add_edge(graph, n1, n2) -@constraint(edge1, ref3, n1[:x] == n2[:x]) - -@objective(graph, Min, n1[:x] + n2[:x]) +@constraint(edge1, ref3a, n1[:x] == n2[:x]) +@constraint(edge1, ref3b, n1[:x]^2 + n2[:x]^2 <= 3) -obj = objective_function(graph) +#@objective(graph, Min, n1[:x] + n2[:x])# + n2[:x]^2) +@objective(graph, Min, n1[:x]^2 + n2[:x]^2) -# TODO: -#@linkconstraint(graph, n1[:x] + n2[:x] == 2) +obj = objective_function(graph) -# TODO: -set_optimizer(graph, HiGHS.Optimizer) -optimize!(graph) +# TODO linkconstraint macro: +# @linkconstraint(graph, n1[:x] + n2[:x] == 2) -# TODO: nonlinear +# TODO: nonlinear functions +# @constraint(n1, ) # TODO: build backend from multiple graphs -# TODO: hypergraph interface \ No newline at end of file +# TODO: hypergraph interface + +# set_optimizer(graph, HiGHS.Optimizer) +set_optimizer(graph, Ipopt.Optimizer) +optimize!(graph) \ No newline at end of file diff --git a/src/dev2.jl b/src/dev2.jl index 1d73218..da2c05b 100644 --- a/src/dev2.jl +++ b/src/dev2.jl @@ -1,56 +1,64 @@ # dev file for subgraphs - using Plasmo using HiGHS graph = OptiGraph(; name=:g1) +# subgraph1 sg1 = Plasmo.add_subgraph(graph; name=:sg1, optimizer_graph=graph) # node 1 n1 = Plasmo.add_node(sg1) @variable(n1, x >= 0) @variable(n1, y >= 0) -@constraint(n1, ref1, x+y <= 10) +@constraint(n1, ref1, n1[:x]+ n1[:y] <= 10) #node 2 n2 = Plasmo.add_node(sg1) @variable(n2, x >= 1) @variable(n2, y >= 2) -@constraint(n2, ref2, x+y <= 4) +@constraint(n2, ref2, n2[:x] + n2[:y] <= 4) -# linking constraint +# linking constraint on subgraph1 edge1 = Plasmo.add_edge(sg1, n1, n2) @constraint(edge1, ref_edge_1, n1[:x] == n2[:x]) -sg2 = Plasmo.add_subgraph(graph; name=:sg2) +# subgraph 2 +sg2 = Plasmo.add_subgraph(graph; name=:sg2, optimizer_graph=graph) # node 3 n3 = Plasmo.add_node(sg2) @variable(n3, x >= 0) @variable(n3, y >= 0) -@constraint(n3, ref3, x+y <= 10) +@constraint(n3, ref3, n3[:x] + n3[:y] <= 10) #node 4 n4 = Plasmo.add_node(sg2) @variable(n4, x >= 1) @variable(n4, y >= 2) -@constraint(n2, ref4, x+y <= 4) +@constraint(n4, ref4, n4[:x] + n4[:y] <= 4) -# linking constraint +# linking constraint on subgraph2 edge2 = Plasmo.add_edge(sg2, n3, n4) -@constraint(edge2, ref3, n3[:x] == n4[:x]) - +@constraint(edge2, ref_edge_2, n3[:x] == n4[:x]) +# link across subgraphs +edge3 = Plasmo.add_edge(graph, n2, n4) +@constraint(edge3, ref_edge_3, n2[:y] == n4[:y]) -@objective(graph, Min, n1[:x] + n2[:x] + n3[:x] + n4[:x]) +# total objective function +@objective(graph, Max, n1[:x] + n2[:x] + n3[:x] + n4[:x]) obj = objective_function(graph) +set_optimizer(graph, HiGHS.Optimizer) +optimize!(graph) -# TODO: -#@linkconstraint(graph, n1[:x] + n2[:x] == 2) +@show value(n1[:x]) +@show value(n2[:x]) +@show value(n3[:x]) +@show value(n4[:x]) +@show value(n2[:y]) +@show value(n4[:y]) -# TODO: -set_optimizer(graph, HiGHS.Optimizer) -optimize!(graph) \ No newline at end of file +println(objective_value(graph)) \ No newline at end of file diff --git a/src/dev3.jl b/src/dev3.jl new file mode 100644 index 0000000..0367189 --- /dev/null +++ b/src/dev3.jl @@ -0,0 +1,7 @@ +using JuMP + +m = Model() +@variable(m, x >= 0) +@variable(m, y >= 0) +@constraint(m, ref, x^3 + y^3 >= 0) + diff --git a/src/graph_backend.jl b/src/graph_backend.jl index f11e252..2e61e22 100644 --- a/src/graph_backend.jl +++ b/src/graph_backend.jl @@ -99,7 +99,7 @@ function JuMP.backend(gb::GraphMOIBackend) return gb.moi_backend end -# MOI Wrapper +# MOI Extension function MOI.get(gb::GraphMOIBackend, attr::MOI.AnyAttribute) return MOI.get(gb.moi_backend, attr) @@ -110,55 +110,18 @@ function MOI.get(gb::GraphMOIBackend, attr::MOI.AnyAttribute, ref::ConstraintRef return MOI.get(gb.moi_backend, attr, graph_index) end +function MOI.get(gb::GraphMOIBackend, attr::MOI.AnyAttribute, ref::NodeVariableRef) + graph_index = gb.node_to_graph_map[ref] + return MOI.get(gb.moi_backend, attr, graph_index) +end + function MOI.set(graph_backend::GraphMOIBackend, attr::MOI.AnyAttribute, args...) MOI.set(graph_backend.moi_backend, attr, args...) end ### Variables -# TODO: attribute where we copied this from in JuMP -function _moi_constrain_node_variable( - gb::GraphMOIBackend, - index, - info, - ::Type{T}, -) where {T} - if info.has_lb - _moi_add_constraint( - gb.moi_backend, - index, - MOI.GreaterThan{T}(info.lower_bound), - ) - end - if info.has_ub - _moi_add_constraint( - gb.moi_backend, - index, - MOI.LessThan{T}(info.upper_bound), - ) - end - if info.has_fix - _moi_add_constraint( - gb.moi_backend, - index, - MOI.EqualTo{T}(info.fixed_value), - ) - end - if info.binary - _moi_add_constraint(gb.moi_backend, index, MOI.ZeroOne()) - end - if info.integer - _moi_add_constraint(gb.moi_backend, index, MOI.Integer()) - end - if info.has_start && info.start !== nothing - MOI.set( - gb.moi_backend, - MOI.VariablePrimalStart(), - index, - convert(T, info.start), - ) - end -end + function next_variable_index(node::OptiNode) return MOI.VariableIndex(num_variables(node) + 1) @@ -209,15 +172,21 @@ function _moi_add_node_constraint( node::OptiNode, con::JuMP.AbstractConstraint ) - func, set = moi_function(con), moi_set(con) + # get moi function and set + jump_func = JuMP.jump_function(con) + moi_func = JuMP.moi_function(con) + moi_set = JuMP.moi_set(con) + constraint_index = next_constraint_index( node, - typeof(func), - typeof(set) - )::MOI.ConstraintIndex{typeof(func),typeof(set)} + typeof(moi_func), + typeof(moi_set) + )::MOI.ConstraintIndex{typeof(moi_func),typeof(moi_set)} cref = ConstraintRef(node, constraint_index, JuMP.shape(con)) for graph in containing_optigraphs(node) - _add_node_constraint_to_backend(graph_backend(graph), cref, func, set) + _update_moi_func!(graph_backend(graph), moi_func, jump_func) + + _add_node_constraint_to_backend(graph_backend(graph), cref, moi_func, moi_set) end return cref end @@ -245,6 +214,15 @@ function next_constraint_index( return MOI.ConstraintIndex{F,S}(index + 1) end +""" + _update_moi_func!( + backend::GraphMOIBackend, + moi_func::MOI.ScalarAffineFunction, + jump_func::JuMP.GenericAffExpr + ) + +Update an MOI function with the actual variable indices from a backend graph. +""" function _update_moi_func!( backend::GraphMOIBackend, moi_func::MOI.ScalarAffineFunction, @@ -256,6 +234,37 @@ function _update_moi_func!( backend_var_idx = backend.node_to_graph_map[var] moi_func.terms[i] = MOI.ScalarAffineTerm{Float64}(coeff, backend_var_idx) end + return +end + +function _update_moi_func!( + backend::GraphMOIBackend, + moi_func::MOI.ScalarQuadraticFunction, + jump_func::JuMP.GenericQuadExpr +) + #quadratic terms + for (i, term) in enumerate(JuMP.quad_terms(jump_func)) + coeff = term[1] + var1 = term[2] + var2 = term[3] + var_idx_1 = backend.node_to_graph_map[var1] + var_idx_2 = backend.node_to_graph_map[var2] + + moi_func.quadratic_terms[i] = MOI.ScalarQuadraticTerm{Float64}( + coeff, + var_idx_1, + var_idx_2 + ) + end + + # linear terms + for (i, term) in enumerate(JuMP.linear_terms(jump_func)) + coeff = term[1] + var = term[2] + backend_var_idx = backend.node_to_graph_map[var] + moi_func.affine_terms[i] = MOI.ScalarAffineTerm{Float64}(coeff, backend_var_idx) + end + return end function _add_backend_variables( @@ -267,10 +276,27 @@ function _add_backend_variables( for var in vars_to_add _add_variable_to_backend(backend, var) end + return end # TODO: QuadExpr +function _add_backend_variables( + backend::GraphMOIBackend, + jump_func::JuMP.GenericQuadExpr +) + vars_aff = [term[2] for term in JuMP.linear_terms(jump_func)] + vars_quad = vcat([[term[2], term[3]] for term in JuMP.quad_terms(jump_func)]...) + vars_unique = unique([vars_aff;vars_quad]) + vars_to_add = setdiff(vars_unique, keys(backend.node_to_graph_map.var_map)) + for var in vars_to_add + _add_variable_to_backend(backend, var) + end + return +end + +# TODO: NonlinearExpr + function _moi_add_edge_constraint( edge::OptiEdge, con::JuMP.AbstractConstraint @@ -337,32 +363,31 @@ function _moi_set_objective_function( return end +function _moi_set_objective_function( + graph::OptiGraph, + expr::JuMP.GenericQuadExpr{C,NodeVariableRef} +) where C <: Real + moi_func = JuMP.moi_function(expr) + + # add variables to backend if using subgraphs + _add_backend_variables(graph_backend(graph), expr) + + # update the moi function variable indices + _update_moi_func!(graph_backend(graph), moi_func, expr) + + MOI.set( + graph_backend(graph), + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{C}}(), + moi_func, + ) + return +end + function MOI.optimize!(graph_backend::GraphMOIBackend) MOI.optimize!(graph_backend.moi_backend) return nothing end -### JuMP interoperability - -# copied from: https://github.com/jump-dev/JuMP.jl/blob/0df25a9185ceede762af533bc965c9374c97450c/src/constraints.jl#L673 -function _moi_add_constraint( - model::MOI.ModelLike, - f::F, - s::S, -) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet} - if !MOI.supports_constraint(model, F, S) - error( - "Constraints of type $(F)-in-$(S) are not supported by the " * - "solver.\n\nIf you expected the solver to support your problem, " * - "you may have an error in your formulation. Otherwise, consider " * - "using a different solver.\n\nThe list of available solvers, " * - "along with the problem types they support, is available at " * - "https://jump.dev/JuMP.jl/stable/installation/#Supported-solvers.", - ) - end - return MOI.add_constraint(model, f, s) -end - ### Helpful utilities # function _swap_indices(variable::MOI.VariableIndex, idxmap::MOIU.IndexMap) @@ -481,24 +506,6 @@ end # return index_map #return an idxmap for each source model # end -# #Add a LinkConstraint to the MOI backend. This is used as part of _aggregate_backends! -# function _add_link_constraint!(id::Symbol, dest::MOI.ModelLike, link::LinkConstraint) -# jump_func = JuMP.jump_function(link) -# moi_func = JuMP.moi_function(link) -# for (i, term) in enumerate(JuMP.linear_terms(jump_func)) -# coeff = term[1] -# var = term[2] -# src = JuMP.backend(optinode(var)) -# idx_map = src.optimizers[id].node_to_optimizer_map -# var_idx = JuMP.index(var) -# dest_idx = idx_map[var_idx] -# moi_func.terms[i] = MOI.ScalarAffineTerm{Float64}(coeff, dest_idx) -# end -# moi_set = JuMP.moi_set(link) -# constraint_index = MOI.add_constraint(dest, moi_func, moi_set) -# return constraint_index -# end - """ diff --git a/src/jump_interop.jl b/src/jump_interop.jl index 8f454ff..b10d04a 100644 --- a/src/jump_interop.jl +++ b/src/jump_interop.jl @@ -1,4 +1,70 @@ -# copied from: https://github.com/jump-dev/JuMP.jl/blob/f496535f560ea1a6bbf5df19031997bdcc1e4022/src/aff_expr.jl#L651 +### directly copied functions from JuMP +# TODO: attribute file +function _moi_constrain_node_variable( + gb::GraphMOIBackend, + index, + info, + ::Type{T}, +) where {T} + if info.has_lb + _moi_add_constraint( + gb.moi_backend, + index, + MOI.GreaterThan{T}(info.lower_bound), + ) + end + if info.has_ub + _moi_add_constraint( + gb.moi_backend, + index, + MOI.LessThan{T}(info.upper_bound), + ) + end + if info.has_fix + _moi_add_constraint( + gb.moi_backend, + index, + MOI.EqualTo{T}(info.fixed_value), + ) + end + if info.binary + _moi_add_constraint(gb.moi_backend, index, MOI.ZeroOne()) + end + if info.integer + _moi_add_constraint(gb.moi_backend, index, MOI.Integer()) + end + if info.has_start && info.start !== nothing + MOI.set( + gb.moi_backend, + MOI.VariablePrimalStart(), + index, + convert(T, info.start), + ) + end +end + +# copied from: https://github.com/jump-dev/JuMP.jl/blob/0df25a9185ceede762af533bc965c9374c97450c/src/constraints.jl +function _moi_add_constraint( + model::MOI.ModelLike, + f::F, + s::S, +) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet} + if !MOI.supports_constraint(model, F, S) + error( + "Constraints of type $(F)-in-$(S) are not supported by the " * + "solver.\n\nIf you expected the solver to support your problem, " * + "you may have an error in your formulation. Otherwise, consider " * + "using a different solver.\n\nThe list of available solvers, " * + "along with the problem types they support, is available at " * + "https://jump.dev/JuMP.jl/stable/installation/#Supported-solvers.", + ) + end + return MOI.add_constraint(model, f, s) +end + + +### functions adapted from: https://github.com/jump-dev/JuMP.jl/blob/0df25a9185ceede762af533bc965c9374c97450c/src/aff_expr.jl + function _assert_isfinite(a::JuMP.GenericAffExpr) for (coef, var) in linear_terms(a) if !isfinite(coef) @@ -14,7 +80,6 @@ function _assert_isfinite(a::JuMP.GenericAffExpr) return end -# Adapted from: https://github.com/jump-dev/JuMP.jl/blob/0df25a9185ceede762af533bc965c9374c97450c/src/aff_expr.jl#L633-L641 function MOI.ScalarAffineFunction( a::GenericAffExpr{C,<:NodeVariableRef}, ) where {C} @@ -25,7 +90,7 @@ function MOI.ScalarAffineFunction( return MOI.ScalarAffineFunction(terms, a.constant) end -# Adapted from: https://github.com/jump-dev/JuMP.jl/blob/0df25a9185ceede762af533bc965c9374c97450c/src/aff_expr.jl#L706-L719 + function JuMP.GenericAffExpr{C,NodeVariableRef}( node::OptiNode, f::MOI.ScalarAffineFunction, @@ -79,4 +144,213 @@ function JuMP.GenericAffExpr{C,NodeVariableRef}( ) end return aff +end + +### TODO: quadratic interop attribution + +function _assert_isfinite(q::GenericQuadExpr) + _assert_isfinite(q.aff) + for (coef, var1, var2) in quad_terms(q) + isfinite(coef) || + error("Invalid coefficient $coef on quadratic term $var1*$var2.") + end +end + +function _moi_quadratic_term(t::Tuple) + return MOI.ScalarQuadraticTerm( + t[2] == t[3] ? 2t[1] : t[1], + index(t[2]), + index(t[3]), + ) +end + +function MOI.ScalarQuadraticFunction( + q::GenericQuadExpr{C,NodeVariableRef}, +) where {C} + _assert_isfinite(q) + qterms = MOI.ScalarQuadraticTerm{C}[ + _moi_quadratic_term(t) for t in quad_terms(q) + ] + moi_aff = MOI.ScalarAffineFunction(q.aff) + return MOI.ScalarQuadraticFunction(qterms, moi_aff.terms, moi_aff.constant) +end + +function GenericQuadExpr{C,NodeVariableRef}( + node::OptiNode, + f::MOI.ScalarQuadraticFunction, +) where {C} + quad = JuMP.GenericQuadExpr{C,NodeVariableRef}( + JuMP.GenericAffExpr{C,NodeVariableRef}( + node, + MOI.ScalarAffineFunction(f.affine_terms, f.constant), + ), + ) + for t in f.quadratic_terms + v1 = t.variable_1 + v2 = t.variable_2 + coef = t.coefficient + if v1 == v2 + coef /= 2 + end + add_to_expression!( + quad, + coef, + NodeVariableRef(node, v1), + NodeVariableRef(node, v2), + ) + end + return quad +end + +function GenericQuadExpr{C,NodeVariableRef}( + edge::OptiEdge, + f::MOI.ScalarQuadraticFunction, +) where {C} + + # affine terms + quad = JuMP.GenericQuadExpr{C,NodeVariableRef}( + JuMP.GenericAffExpr{C,NodeVariableRef}( + edge, + MOI.ScalarAffineFunction(f.affine_terms, f.constant), + ), + ) + + # quadratic terms + for t in f.quadratic_terms + # node variable indices + v1 = t.variable_1 + v2 = t.variable_2 + coef = t.coefficient + if v1 == v2 + coef /= 2 + end + + # variable index 1 + node_var_1 = graph_backend(edge).graph_to_node_map[v1] + node1 = node_var_1.node + var_index_1 = node_var_1.index + + # variable index 2 + node_var_2 = graph_backend(edge).graph_to_node_map[v2] + node2 = node_var_2.node + var_index_2 = node_var_2.index + + # add to quadratic expression + add_to_expression!( + quad, + coef, + NodeVariableRef(node1, var_index_1), + NodeVariableRef(node2, var_index_2), + ) + end + return quad +end + +function GenericQuadExpr{C,NodeVariableRef}( + edge::OptiEdge, + f::MOI.ScalarQuadraticFunction, +) where {C} + + # affine terms + quad = JuMP.GenericQuadExpr{C,NodeVariableRef}( + JuMP.GenericAffExpr{C,NodeVariableRef}( + edge, + MOI.ScalarAffineFunction(f.affine_terms, f.constant), + ), + ) + + # quadratic terms + for t in f.quadratic_terms + # node variable indices + v1 = t.variable_1 + v2 = t.variable_2 + coef = t.coefficient + if v1 == v2 + coef /= 2 + end + + # variable index 1 + node_var_1 = graph_backend(edge).graph_to_node_map[v1] + node1 = node_var_1.node + var_index_1 = node_var_1.index + + # variable index 2 + node_var_2 = graph_backend(edge).graph_to_node_map[v2] + node2 = node_var_2.node + var_index_2 = node_var_2.index + + # add to quadratic expression + add_to_expression!( + quad, + coef, + NodeVariableRef(node1, var_index_1), + NodeVariableRef(node2, var_index_2), + ) + end + return quad +end + +function GenericQuadExpr{C,NodeVariableRef}( + graph::OptiGraph, + f::MOI.ScalarQuadraticFunction, +) where {C} + + # affine terms + quad = JuMP.GenericQuadExpr{C,NodeVariableRef}( + JuMP.GenericAffExpr{C,NodeVariableRef}( + graph, + MOI.ScalarAffineFunction(f.affine_terms, f.constant), + ), + ) + + gb = graph_backend(graph) + + # quadratic terms + for t in f.quadratic_terms + # node variable indices + v1 = t.variable_1 + v2 = t.variable_2 + coef = t.coefficient + if v1 == v2 + coef /= 2 + end + + # variable index 1 + node_var_1 = gb.graph_to_node_map[v1] + node1 = node_var_1.node + var_index_1 = node_var_1.index + + # variable index 2 + node_var_2 = gb.graph_to_node_map[v2] + node2 = node_var_2.node + var_index_2 = node_var_2.index + + # add to quadratic expression + add_to_expression!( + quad, + coef, + NodeVariableRef(node1, var_index_1), + NodeVariableRef(node2, var_index_2), + ) + end + return quad +end + +function JuMP.GenericAffExpr{C,NodeVariableRef}( + graph::OptiGraph, + f::MOI.ScalarAffineFunction, +) where {C} + aff = GenericAffExpr{C,NodeVariableRef}(f.constant) + for t in f.terms + gb = graph_backend(graph) + node_var = gb.graph_to_node_map[t.variable] + node = node_var.node + node_index = node_var.index + JuMP.add_to_expression!( + aff, + t.coefficient, + NodeVariableRef(node, node_index), + ) + end + return aff end \ No newline at end of file diff --git a/src/optiedge.jl b/src/optiedge.jl index 8fefca3..ab16237 100644 --- a/src/optiedge.jl +++ b/src/optiedge.jl @@ -83,6 +83,13 @@ function JuMP.jump_function( return JuMP.GenericAffExpr{C,NodeVariableRef}(edge, f) end +function JuMP.jump_function( + edge::OptiEdge, + f::MOI.ScalarQuadraticFunction{C}, +) where {C} + return JuMP.GenericQuadExpr{C,NodeVariableRef}(edge, f) +end + function JuMP.num_constraints( edge::OptiEdge, ::Type{F}, @@ -95,13 +102,12 @@ function JuMP.num_constraints( return length(filter((cref) -> cref.model == edge, refs)) end - ### Edge Constraints function JuMP.add_constraint( edge::OptiEdge, con::JuMP.AbstractConstraint, name::String="" ) - con = JuMP.model_convert(edge, con) # converts coefficient and constant types + con = JuMP.model_convert(edge, con) cref = _moi_add_edge_constraint(edge, con) # TODO: set constraint name return cref diff --git a/src/optigraph.jl b/src/optigraph.jl index 1e991eb..5209cb2 100644 --- a/src/optigraph.jl +++ b/src/optigraph.jl @@ -75,6 +75,13 @@ function JuMP.jump_function( return JuMP.GenericAffExpr{C,NodeVariableRef}(graph, f) end +function JuMP.jump_function( + graph::OptiGraph, + f::MOI.ScalarQuadraticFunction{C}, +) where {C} + return JuMP.GenericQuadExpr{C,NodeVariableRef}(graph, f) +end + ### Add Node function add_node( @@ -159,6 +166,15 @@ function JuMP.set_objective_function( return end +function JuMP.set_objective_function( + graph::OptiGraph, + expr::JuMP.GenericQuadExpr{C,NodeVariableRef} +) where C <: Real + _moi_set_objective_function(graph, expr) + return +end + + """ JuMP.objective_value(graph::OptiGraph) diff --git a/src/optinode.jl b/src/optinode.jl index 3d2d250..b44a5d7 100644 --- a/src/optinode.jl +++ b/src/optinode.jl @@ -88,6 +88,13 @@ function JuMP.jump_function( return JuMP.GenericAffExpr{C,NodeVariableRef}(node, f) end +function JuMP.jump_function( + node::OptiNode, + f::MOI.ScalarQuadraticFunction{C}, +) where {C} + return JuMP.GenericQuadExpr{C,NodeVariableRef}(node, f) +end + function JuMP.num_constraints( node::OptiNode, ::Type{F}, @@ -113,6 +120,11 @@ Base.print(io::IO, vref::NodeVariableRef) = Base.print(io, Base.string(vref)) Base.show(io::IO, vref::NodeVariableRef) = Base.print(io, vref) Base.broadcastable(vref::NodeVariableRef) = Ref(vref) +function JuMP.value(nvref::NodeVariableRef) + gb = graph_backend(nvref.node) + return MOI.get(gb, MOI.VariablePrimal(), nvref) +end + """ JuMP.add_variable(node::OptiNode, v::JuMP.AbstractVariable, name::String="") @@ -147,13 +159,8 @@ function JuMP.num_variables(node::OptiNode) return length(filter((vref) -> vref.node == node, keys(n2g.var_map))) end - ### Node Constraints -# NOTE: Using an alias on ConstraintRef{M,C,S} causes issues with dispatching JuMP functions. I'm not sure it is really necessary vs just using ConstraintRef for dispatch. -# const NodeConstraintRef = JuMP.ConstraintRef{OptiNode, MOI.ConstraintIndex{F,S} where {F,S}, Shape where Shape <: JuMP.AbstractShape} -# const NodeConstraintRef = JuMP.ConstraintRef{OptiNode, MOI.ConstraintIndex} - """ JuMP.add_constraint(node::OptiNode, con::JuMP.AbstractConstraint, base_name::String="")