From 17807bab3bd12d16cda568ca37572dc14ef74df7 Mon Sep 17 00:00:00 2001 From: pulsipher Date: Thu, 12 Oct 2023 15:26:30 -0400 Subject: [PATCH 1/3] initial commit --- src/datatypes.jl | 27 ++++++++++++++++++++++ src/moi.jl | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/moi.jl diff --git a/src/datatypes.jl b/src/datatypes.jl index b477358..62c7469 100644 --- a/src/datatypes.jl +++ b/src/datatypes.jl @@ -373,6 +373,33 @@ A type for using indicator constraint approach for linear disjunctive constraint """ struct Indicator <: AbstractReformulationMethod end +""" + MOIDisjunction <: AbstractReformulationMethod +A reformulation type for reformulating disjunctions into their MathOptInterface +set representations [`DisjucntionSet`](@ref) which are then added to the model. +""" +struct MOIDisjunction <: AbstractReformulationMethod end + +""" + DisjunctionSet{S <: MOI.AbstractSet} <: MOI.AbstractVectorSet +A MathOptInterface set for representing disjunctions in the format vector of +functions in set to enable: +```julia +@constraint(model, [funcs...] in DisjunctionSet(n, idxs, sets)) +``` +where the vector of functions `funcs` is a flattened version of all the disjunct +constraint functions where the indicator variable is listed first, `n` is the +length of `[funcs...]`, `idxs` is a vector of the indices tracking where each +disjunct begins (i.e., it stores the indices of the indicator variables in +`[funcs...]`), and `sets` is a uses a vector of vectors structure for the MOI sets +that correspond to all the disjunct constraint functions. +""" +struct DisjunctionSet{S <: _MOI.AbstractSet} <: _MOI.AbstractVectorSet + dimension::Int + disjunct_indices::Vector{Int} + constraint_sets::Vector{Vector{S}} +end + ################################################################################ # GDP Data ################################################################################ diff --git a/src/moi.jl b/src/moi.jl new file mode 100644 index 0000000..245ce1f --- /dev/null +++ b/src/moi.jl @@ -0,0 +1,58 @@ +################################################################################ +# UTILITY METHODS +################################################################################ +# Requred for extensions to MOI.AbstractVectorSet +# function _MOI.Utilities.set_dot(x::AbstractVector, y::AbstractVector, set::DisjunctionSet) +# return LinearAlgebra.dot(x, y) # TODO figure out what we should actually do here +# end + +# TODO create a bridge for `DisjunctionSet` + +# TODO create helper method to unpack DisjunctionSet at the MOI side of things + +################################################################################ +# REFRORMULATION METHODS +################################################################################ +# Helper methods to handle recursively flattening the disjuncts +function _constr_set!(funcs, con::JuMP.AbstractConstraint) + append!(funcs, JuMP.jump_function(con)) + return JuMP.moi_set(con) +end +function _constr_set!(funcs, con::Disjunction) + inner_funcs, set = _disjunction_to_set(con) + append!(funcs, inner_funcs) + return set +end + +# Create the vectors needed for a disjunction vector constraint +function _disjunction_to_set(d::Disjunction) + # allocate memory for the storage vectors + num_disjuncts = length(d.indicators) + funcs = sizehint!(JuMP.AbstractJuMPScalar[], num_disjuncts) + sets = Vector{Vector{_MOI.AbstractSet}}(undef, num_disjuncts) + d_idxs = Vector{Int}(undef, num_disjuncts) + # iterate over the underlying disjuncts to fill in the storage vectors + for (i, lvref) in enumerate(d.indicators) + model = JuMP.owner_model(lvref) + push!(funcs, _indicator_to_binary(model)[lvref]) + d_idxs[i] = length(funcs) + crefs = _indicator_to_constraints(model)[lvref] + sets[i] = map(c -> _constr_set!(funcs, JuMP.constraint_object(c)), crefs) + end + # convert the `sets` type to be concrete if possible (TODO benchmark if this is worth it) + SetType = typeof(first(sets)) + if SetType != Vector{_MOI.AbstractSet} && all(s -> s isa SetType, sets) + sets = convert(SetType, sets) + end + return funcs, DisjunctionSet(length(funcs), d_idxs, sets) +end + +# Extend the disjunction reformulation +function reformulate_disjunction( + model::JuMP.Model, + d::Disjunction, + ::MOIDisjunction + ) + funcs, set = _disjunction_to_set(d) + return [JuMP.VectorConstraint(funcs, set, JuMP.VectorShape())] +end \ No newline at end of file From 2231536dc7dcb39a8b902d90c6801da78734c598 Mon Sep 17 00:00:00 2001 From: pulsipher Date: Thu, 12 Oct 2023 16:47:03 -0400 Subject: [PATCH 2/3] Improve allocation --- src/moi.jl | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/moi.jl b/src/moi.jl index 245ce1f..259fc48 100644 --- a/src/moi.jl +++ b/src/moi.jl @@ -14,30 +14,31 @@ # REFRORMULATION METHODS ################################################################################ # Helper methods to handle recursively flattening the disjuncts -function _constr_set!(funcs, con::JuMP.AbstractConstraint) +function _constr_set!(model, funcs, con::JuMP.AbstractConstraint) append!(funcs, JuMP.jump_function(con)) return JuMP.moi_set(con) end -function _constr_set!(funcs, con::Disjunction) - inner_funcs, set = _disjunction_to_set(con) +function _constr_set!(model, funcs, con::Disjunction) + inner_funcs, set = _disjunction_to_set(model, con) append!(funcs, inner_funcs) return set end # Create the vectors needed for a disjunction vector constraint -function _disjunction_to_set(d::Disjunction) +function _disjunction_to_set(model::JuMP.Model, d::Disjunction) # allocate memory for the storage vectors num_disjuncts = length(d.indicators) - funcs = sizehint!(JuMP.AbstractJuMPScalar[], num_disjuncts) + constr_mappings = _indicator_to_constraints(model) + num_constrs = sum(length(constr_mappings[lvref]) for lvref in d.indicators) + funcs = sizehint!(JuMP.AbstractJuMPScalar[], num_disjuncts + num_constrs) sets = Vector{Vector{_MOI.AbstractSet}}(undef, num_disjuncts) d_idxs = Vector{Int}(undef, num_disjuncts) # iterate over the underlying disjuncts to fill in the storage vectors for (i, lvref) in enumerate(d.indicators) - model = JuMP.owner_model(lvref) push!(funcs, _indicator_to_binary(model)[lvref]) d_idxs[i] = length(funcs) - crefs = _indicator_to_constraints(model)[lvref] - sets[i] = map(c -> _constr_set!(funcs, JuMP.constraint_object(c)), crefs) + crefs = constr_mappings(model)[lvref] + sets[i] = map(c -> _constr_set!(model, funcs, JuMP.constraint_object(c)), crefs) end # convert the `sets` type to be concrete if possible (TODO benchmark if this is worth it) SetType = typeof(first(sets)) @@ -53,6 +54,6 @@ function reformulate_disjunction( d::Disjunction, ::MOIDisjunction ) - funcs, set = _disjunction_to_set(d) + funcs, set = _disjunction_to_set(model, d) return [JuMP.VectorConstraint(funcs, set, JuMP.VectorShape())] end \ No newline at end of file From 2507c209c33c580465ac692b3900e45f385670dc Mon Sep 17 00:00:00 2001 From: Hector Perez Date: Thu, 12 Oct 2023 23:39:11 -0400 Subject: [PATCH 3/3] typo Co-authored-by: David Bernal --- src/datatypes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datatypes.jl b/src/datatypes.jl index 62c7469..a51d6f6 100644 --- a/src/datatypes.jl +++ b/src/datatypes.jl @@ -376,7 +376,7 @@ struct Indicator <: AbstractReformulationMethod end """ MOIDisjunction <: AbstractReformulationMethod A reformulation type for reformulating disjunctions into their MathOptInterface -set representations [`DisjucntionSet`](@ref) which are then added to the model. +set representations [`DisjunctionSet`](@ref) which are then added to the model. """ struct MOIDisjunction <: AbstractReformulationMethod end