diff --git a/examples/ex5.jl b/examples/ex5.jl index 92bbb31..d81e938 100644 --- a/examples/ex5.jl +++ b/examples/ex5.jl @@ -1,4 +1,4 @@ -# https://arxiv.org/pdf/2303.04375.pdf +# Nested GDP: https://arxiv.org/pdf/2303.04375.pdf using DisjunctiveProgramming ## diff --git a/examples/ex6.jl b/examples/ex6.jl index 114f78f..0e057cc 100644 --- a/examples/ex6.jl +++ b/examples/ex6.jl @@ -1,6 +1,6 @@ using DisjunctiveProgramming -## +## Multi-level Nested GDP m = GDPModel() @variable(m, -5 <= x[1:3] <= 5) diff --git a/src/bigm.jl b/src/bigm.jl index d4598c5..97d1d7f 100644 --- a/src/bigm.jl +++ b/src/bigm.jl @@ -2,7 +2,7 @@ # BIG-M VALUE ################################################################################ # Get Big-M value for a particular constraint -function _get_M_value(func::JuMP.AbstractJuMPScalar, set::_MOI.AbstractSet, method::BigM) +function _get_M_value(func::AbstractJuMPScalar, set::_MOI.AbstractSet, method::BigM) if method.tighten M = _get_tight_M(func, set, method) else @@ -12,7 +12,7 @@ function _get_M_value(func::JuMP.AbstractJuMPScalar, set::_MOI.AbstractSet, meth end # Get the tightest Big-M value for a particular constraint -function _get_tight_M(func::JuMP.AbstractJuMPScalar, set::_MOI.AbstractSet, method::BigM) +function _get_tight_M(func::AbstractJuMPScalar, set::_MOI.AbstractSet, method::BigM) M = min.(method.value, _calculate_tight_M(func, set, method)) #broadcast for when S <: MOI.Interval or MOI.EqualTo or MOI.Zeros if any(isinf.(M)) error("A finite Big-M value must be used. The value obtained was $M.") @@ -21,14 +21,14 @@ function _get_tight_M(func::JuMP.AbstractJuMPScalar, set::_MOI.AbstractSet, meth end # Get user-specified Big-M value -function _get_M(::JuMP.AbstractJuMPScalar, ::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives}, method::BigM) +function _get_M(::AbstractJuMPScalar, ::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives}, method::BigM) M = method.value if isinf(M) error("A finite Big-M value must be used. The value given was $M.") end return M end -function _get_M(::JuMP.AbstractJuMPScalar, ::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros}, method::BigM) +function _get_M(::AbstractJuMPScalar, ::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros}, method::BigM) M = method.value if isinf(M) error("A finite Big-M value must be used. The value given was $M.") @@ -37,64 +37,64 @@ function _get_M(::JuMP.AbstractJuMPScalar, ::Union{_MOI.Interval, _MOI.EqualTo, end # Apply interval arithmetic on a linear constraint to infer the tightest Big-M value from the bounds on the constraint. -function _calculate_tight_M(func::JuMP.AffExpr, set::_MOI.LessThan, method::BigM) +function _calculate_tight_M(func::AffExpr, set::_MOI.LessThan, method::BigM) return _interval_arithmetic_LessThan(func, -set.upper, method) end -function _calculate_tight_M(func::JuMP.AffExpr, set::_MOI.GreaterThan, method::BigM) +function _calculate_tight_M(func::AffExpr, set::_MOI.GreaterThan, method::BigM) return _interval_arithmetic_GreaterThan(func, -set.lower, method) end -function _calculate_tight_M(func::JuMP.AffExpr, ::_MOI.Nonpositives, method::BigM) +function _calculate_tight_M(func::AffExpr, ::_MOI.Nonpositives, method::BigM) return _interval_arithmetic_LessThan(func, 0.0, method) end -function _calculate_tight_M(func::JuMP.AffExpr, ::_MOI.Nonnegatives, method::BigM) +function _calculate_tight_M(func::AffExpr, ::_MOI.Nonnegatives, method::BigM) return _interval_arithmetic_GreaterThan(func, 0.0, method) end -function _calculate_tight_M(func::JuMP.AffExpr, set::_MOI.Interval, method::BigM) +function _calculate_tight_M(func::AffExpr, set::_MOI.Interval, method::BigM) return ( _interval_arithmetic_GreaterThan(func, -set.lower, method), _interval_arithmetic_LessThan(func, -set.upper, method) ) end -function _calculate_tight_M(func::JuMP.AffExpr, set::_MOI.EqualTo, method::BigM) +function _calculate_tight_M(func::AffExpr, set::_MOI.EqualTo, method::BigM) return ( _interval_arithmetic_GreaterThan(func, -set.value, method), _interval_arithmetic_LessThan(func, -set.value, method) ) end -function _calculate_tight_M(func::JuMP.AffExpr, ::_MOI.Zeros, method::BigM) +function _calculate_tight_M(func::AffExpr, ::_MOI.Zeros, method::BigM) return ( _interval_arithmetic_GreaterThan(func, 0.0, method), _interval_arithmetic_LessThan(func, 0.0, method) ) end # fallbacks for other scalar constraints -_calculate_tight_M(func::Union{JuMP.QuadExpr, JuMP.NonlinearExpr}, set::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros}, method::BigM) = (Inf, Inf) -_calculate_tight_M(func::Union{JuMP.QuadExpr, JuMP.NonlinearExpr}, set::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives}, method::BigM) = Inf +_calculate_tight_M(func::Union{QuadExpr, NonlinearExpr}, set::Union{_MOI.Interval, _MOI.EqualTo, _MOI.Zeros}, method::BigM) = (Inf, Inf) +_calculate_tight_M(func::Union{QuadExpr, NonlinearExpr}, set::Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.Nonnegatives, _MOI.Nonpositives}, method::BigM) = Inf _calculate_tight_M(func, set, method::BigM) = error("BigM method not implemented for constraint type $(typeof(func)) in $(typeof(set))") # get variable bounds for interval arithmetic -function _update_variable_bounds(vref::JuMP.VariableRef, method::BigM) - if JuMP.is_binary(vref) +function _update_variable_bounds(vref::VariableRef, method::BigM) + if is_binary(vref) lb = 0 - elseif !JuMP.has_lower_bound(vref) + elseif !has_lower_bound(vref) lb = -Inf else - lb = JuMP.lower_bound(vref) + lb = lower_bound(vref) end - if JuMP.is_binary(vref) + if is_binary(vref) ub = 1 - elseif !JuMP.has_upper_bound(vref) + elseif !has_upper_bound(vref) ub = Inf else - ub = JuMP.upper_bound(vref) + ub = upper_bound(vref) end return lb, ub end # perform interval arithmetic to update the initial M value -function _interval_arithmetic_LessThan(func::JuMP.AffExpr, M::Float64, method::BigM) +function _interval_arithmetic_LessThan(func::AffExpr, M::Float64, method::BigM) for (var,coeff) in func.terms - JuMP.is_binary(var) && continue #skip binary variables + is_binary(var) && continue #skip binary variables if coeff > 0 M += coeff*method.variable_bounds[var][2] else @@ -103,9 +103,9 @@ function _interval_arithmetic_LessThan(func::JuMP.AffExpr, M::Float64, method::B end return M + func.constant end -function _interval_arithmetic_GreaterThan(func::JuMP.AffExpr, M::Float64, method::BigM) +function _interval_arithmetic_GreaterThan(func::AffExpr, M::Float64, method::BigM) for (var,coeff) in func.terms - JuMP.is_binary(var) && continue #skip binary variables + is_binary(var) && continue #skip binary variables if coeff < 0 M += coeff*method.variable_bounds[var][2] else @@ -119,81 +119,81 @@ end # BIG-M REFORMULATION ################################################################################ function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::BigM ) where {T, S <: _MOI.LessThan} M = _get_M_value(con.func, con.set, method) - new_func = JuMP.@expression(model, con.func - M*(1-bvref)) - reform_con = JuMP.build_constraint(error, new_func, con.set) + new_func = @expression(model, con.func - M*(1-bvref)) + reform_con = build_constraint(error, new_func, con.set) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S, R}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S, R}, + bvref::VariableRef, method::BigM ) where {T, S <: _MOI.Nonpositives, R} M = [_get_M_value(func, con.set, method) for func in con.func] - new_func = JuMP.@expression(model, [i=1:con.set.dimension], + new_func = @expression(model, [i=1:con.set.dimension], con.func[i] - M[i]*(1-bvref) ) - reform_con = JuMP.build_constraint(error, new_func, con.set) + reform_con = build_constraint(error, new_func, con.set) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::BigM ) where {T, S <: _MOI.GreaterThan} M = _get_M_value(con.func, con.set, method) - new_func = JuMP.@expression(model, con.func + M*(1-bvref)) - reform_con = JuMP.build_constraint(error, new_func, con.set) + new_func = @expression(model, con.func + M*(1-bvref)) + reform_con = build_constraint(error, new_func, con.set) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S, R}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S, R}, + bvref::VariableRef, method::BigM ) where {T, S <: _MOI.Nonnegatives, R} M = [_get_M_value(func, con.set, method) for func in con.func] - new_func = JuMP.@expression(model, [i=1:con.set.dimension], + new_func = @expression(model, [i=1:con.set.dimension], con.func[i] + M[i]*(1-bvref) ) - reform_con = JuMP.build_constraint(error, new_func, con.set) + reform_con = build_constraint(error, new_func, con.set) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::BigM ) where {T, S <: Union{_MOI.Interval, _MOI.EqualTo}} M = _get_M_value(con.func, con.set, method) - new_func_gt = JuMP.@expression(model, con.func + M[1]*(1-bvref)) - new_func_lt = JuMP.@expression(model, con.func - M[2]*(1-bvref)) + new_func_gt = @expression(model, con.func + M[1]*(1-bvref)) + new_func_lt = @expression(model, con.func - M[2]*(1-bvref)) set_values = _set_values(con.set) - reform_con_gt = JuMP.build_constraint(error, new_func_gt, _MOI.GreaterThan(set_values[1])) - reform_con_lt = JuMP.build_constraint(error, new_func_lt, _MOI.LessThan(set_values[2])) + reform_con_gt = build_constraint(error, new_func_gt, _MOI.GreaterThan(set_values[1])) + reform_con_lt = build_constraint(error, new_func_lt, _MOI.LessThan(set_values[2])) return [reform_con_gt, reform_con_lt] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S, R}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S, R}, + bvref::VariableRef, method::BigM ) where {T, S <: _MOI.Zeros, R} M = [_get_M_value(func, con.set, method) for func in con.func] - new_func_nn = JuMP.@expression(model, [i=1:con.set.dimension], + new_func_nn = @expression(model, [i=1:con.set.dimension], con.func[i] + M[i][1]*(1-bvref) ) - new_func_np = JuMP.@expression(model, [i=1:con.set.dimension], + new_func_np = @expression(model, [i=1:con.set.dimension], con.func[i] - M[i][2]*(1-bvref) ) - reform_con_nn = JuMP.build_constraint(error, new_func_nn, _MOI.Nonnegatives(con.set.dimension)) - reform_con_np = JuMP.build_constraint(error, new_func_np, _MOI.Nonpositives(con.set.dimension)) + reform_con_nn = build_constraint(error, new_func_nn, _MOI.Nonnegatives(con.set.dimension)) + reform_con_np = build_constraint(error, new_func_np, _MOI.Nonpositives(con.set.dimension)) return [reform_con_nn, reform_con_np] end \ No newline at end of file diff --git a/src/constraints.jl b/src/constraints.jl index 4d19a5e..6b5be0b 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -45,17 +45,17 @@ for (RefType, loc) in ((:DisjunctConstraintRef, :disjunct_constraints), JuMP.index(cref::$RefType) = cref.index @doc """ - JuMP.is_valid(model::JuMP.Model, cref::$($RefType)) + JuMP.is_valid(model::Model, cref::$($RefType)) Return `true` if `cref` refers to a valid constraint in the `GDP model`. """ - function JuMP.is_valid(model::JuMP.Model, cref::$RefType) # TODO: generalize for AbstractModel - return model === JuMP.owner_model(cref) + function JuMP.is_valid(model::Model, cref::$RefType) # TODO: generalize for AbstractModel + return model === owner_model(cref) end # Get the ConstraintData object function _constraint_data(cref::$RefType) - return gdp_data(JuMP.owner_model(cref)).$loc[JuMP.index(cref)] + return gdp_data(owner_model(cref)).$loc[index(cref)] end @doc """ @@ -74,7 +74,7 @@ for (RefType, loc) in ((:DisjunctConstraintRef, :disjunct_constraints), """ function JuMP.set_name(cref::$RefType, name::String) _constraint_data(cref).name = name - _set_ready_to_optimize(JuMP.owner_model(cref), false) + _set_ready_to_optimize(owner_model(cref), false) return end @@ -94,25 +94,25 @@ for (RefType, loc) in ((:DisjunctConstraintRef, :disjunct_constraints), end Base.copy(cref::$RefType) = cref @doc """ - Base.getindex(map::JuMP.GenericReferenceMap, cref::$($RefType)) + Base.getindex(map::GenericReferenceMap, cref::$($RefType)) ... """ - function Base.getindex(map::JuMP.ReferenceMap, cref::$RefType) - $RefType(map.model, JuMP.index(cref)) + function Base.getindex(map::ReferenceMap, cref::$RefType) + $RefType(map.model, index(cref)) end end end # Extend delete """ - JuMP.delete(model::JuMP.Model, cref::DisjunctionRef) + JuMP.delete(model::Model, cref::DisjunctionRef) Delete a disjunction constraint from the `GDP model`. """ -function JuMP.delete(model::JuMP.Model, cref::DisjunctionRef) - @assert JuMP.is_valid(model, cref) "Disjunctive constraint does not belong to model." - cidx = JuMP.index(cref) +function JuMP.delete(model::Model, cref::DisjunctionRef) + @assert is_valid(model, cref) "Disjunctive constraint does not belong to model." + cidx = index(cref) dict = _disjunctions(model) delete!(dict, cidx) _set_ready_to_optimize(model, false) @@ -120,13 +120,13 @@ function JuMP.delete(model::JuMP.Model, cref::DisjunctionRef) end """ - JuMP.delete(model::JuMP.Model, cref::DisjunctConstraintRef) + JuMP.delete(model::Model, cref::DisjunctConstraintRef) Delete a disjunct constraint from the `GDP model`. """ -function JuMP.delete(model::JuMP.Model, cref::DisjunctConstraintRef) - @assert JuMP.is_valid(model, cref) "Disjunctive constraint does not belong to model." - cidx = JuMP.index(cref) +function JuMP.delete(model::Model, cref::DisjunctConstraintRef) + @assert is_valid(model, cref) "Disjunctive constraint does not belong to model." + cidx = index(cref) dict = _disjunct_constraints(model) delete!(dict, cidx) _set_ready_to_optimize(model, false) @@ -134,13 +134,13 @@ function JuMP.delete(model::JuMP.Model, cref::DisjunctConstraintRef) end """ - JuMP.delete(model::JuMP.Model, cref::LogicalConstraintRef) + JuMP.delete(model::Model, cref::LogicalConstraintRef) Delete a logical constraint from the `GDP model`. """ -function JuMP.delete(model::JuMP.Model, cref::LogicalConstraintRef) - @assert JuMP.is_valid(model, cref) "Logical constraint does not belong to model." - cidx = JuMP.index(cref) +function JuMP.delete(model::Model, cref::LogicalConstraintRef) + @assert is_valid(model, cref) "Logical constraint does not belong to model." + cidx = index(cref) dict = _logical_constraints(model) delete!(dict, cidx) _set_ready_to_optimize(model, false) @@ -151,9 +151,9 @@ end # Disjunct Constraints ################################################################################ function _check_expression(expr) - vars = Set{JuMP.VariableRef}() + vars = Set{VariableRef}() _interrogate_variables(v -> push!(vars, v), expr) - if any(JuMP.is_binary.(vars)) || any(JuMP.is_integer.(vars)) + if any(is_binary.(vars)) || any(is_integer.(vars)) error("Disjunct constraints cannot contain binary or integer variables.") end return @@ -179,13 +179,13 @@ function JuMP.build_constraint( tag::DisjunctConstraint ) _check_expression(func) - constr = JuMP.build_constraint(_error, func, set) + constr = build_constraint(_error, func, set) return _DisjunctConstraint(constr, tag.indicator) end # Allows for building DisjunctConstraints for VectorConstraints since these get parsed differently by JuMP (JuMP changes the set to a MOI.AbstractScalarSet) for SetType in ( - JuMP.Nonnegatives, JuMP.Nonpositives, JuMP.Zeros, + Nonnegatives, Nonpositives, Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.Zeros ) @eval begin @@ -206,7 +206,7 @@ for SetType in ( tag::DisjunctConstraint ) _check_expression(func) - constr = JuMP.build_constraint(_error, func, set) + constr = build_constraint(_error, func, set) return _DisjunctConstraint(constr, tag.indicator) end end @@ -215,21 +215,21 @@ end # Allow intervals to handle tags function JuMP.build_constraint( _error::Function, - func::JuMP.AbstractJuMPScalar, + func::AbstractJuMPScalar, lb::Real, ub::Real, tag::DisjunctConstraint ) _check_expression(func) - constr = JuMP.build_constraint(_error, func, lb, ub) - func = JuMP.jump_function(constr) - set = JuMP.moi_set(constr) - return JuMP.build_constraint(_error, func, set, tag) + constr = build_constraint(_error, func, lb, ub) + func = jump_function(constr) + set = moi_set(constr) + return build_constraint(_error, func, set, tag) end """ JuMP.add_constraint( - model::JuMP.Model, + model::Model, con::_DisjunctConstraint, name::String = "" )::DisjunctConstraintRef @@ -238,7 +238,7 @@ Extend `JuMP.add_constraint` to add a [`DisjunctConstraint`](@ref) to a [`GDPMod The constraint is added to the `GDPData` in the `.ext` dictionary of the `GDPModel`. """ function JuMP.add_constraint( - model::JuMP.Model, + model::Model, con::_DisjunctConstraint, name::String = "" ) @@ -257,8 +257,8 @@ function _add_indicator_var( con::_DisjunctConstraint{C, LogicalVariableRef}, cref, model - ) where {C <: JuMP.AbstractConstraint} - JuMP.is_valid(model, con.lvref) || error("Logical variable belongs to a different model.") + ) where {C <: AbstractConstraint} + is_valid(model, con.lvref) || error("Logical variable belongs to a different model.") if !haskey(_indicator_to_constraints(model), con.lvref) _indicator_to_constraints(model)[con.lvref] = Vector{Union{DisjunctConstraintRef, DisjunctionRef}}() end @@ -266,10 +266,10 @@ function _add_indicator_var( return end # check disjunction -function _check_disjunction(_error, lvrefs::AbstractVector{LogicalVariableRef}, model::JuMP.Model) +function _check_disjunction(_error, lvrefs::AbstractVector{LogicalVariableRef}, model::Model) isequal(unique(lvrefs),lvrefs) || _error("Not all the logical indicator variables are unique.") for lvref in lvrefs - if !JuMP.is_valid(model, lvref) + if !is_valid(model, lvref) _error("`$lvref` is not a valid logical variable reference.") end end @@ -277,14 +277,14 @@ function _check_disjunction(_error, lvrefs::AbstractVector{LogicalVariableRef}, end # fallback -function _check_disjunction(_error, lvrefs, model::JuMP.Model) +function _check_disjunction(_error, lvrefs, model::Model) _error("Unrecognized disjunction input structure.") # TODO add details on proper syntax end # Write the main function for creating disjunctions that is macro friendly function _create_disjunction( _error::Function, - model::JuMP.Model, # TODO: generalize to AbstractModel + model::Model, # TODO: generalize to AbstractModel structure::AbstractVector, #generalize for containers name::String, nested::Bool @@ -306,7 +306,7 @@ end # Disjunction build for unnested disjunctions function _disjunction( _error::Function, - model::JuMP.Model, # TODO: generalize to AbstractModel + model::Model, # TODO: generalize to AbstractModel structure::AbstractVector, #generalize for containers name::String ) @@ -316,7 +316,7 @@ end # Fallback disjunction build for nonvector structure function _disjunction( _error::Function, - model::JuMP.Model, # TODO: generalize to AbstractModel + model::Model, # TODO: generalize to AbstractModel structure, name::String ) @@ -326,13 +326,13 @@ end # Disjunction build for nested disjunctions function _disjunction( _error::Function, - model::JuMP.Model, # TODO: generalize to AbstractModel + model::Model, # TODO: generalize to AbstractModel structure, name::String, tag::DisjunctConstraint ) dref = _create_disjunction(_error, model, structure, name, true) - obj = JuMP.constraint_object(dref) + obj = constraint_object(dref) _add_indicator_var(_DisjunctConstraint(obj, tag.indicator), dref, model) return dref end @@ -340,7 +340,7 @@ end # General fallback for additional arguments function _disjunction( _error::Function, - model::JuMP.Model, # TODO: generalize to AbstractModel + model::Model, # TODO: generalize to AbstractModel structure, name::String, extra... @@ -352,7 +352,7 @@ end """ disjunction( - model::JuMP.Model, + model::Model, disjunct_indicators::Vector{LogicalVariableRef} name::String = "" ) @@ -360,7 +360,7 @@ end Function to add a [`Disjunction`](@ref) to a [`GDPModel`](@ref). disjunction( - model::JuMP.Model, + model::Model, disjunct_indicators::Vector{LogicalVariableRef}, nested_tag::DisjunctConstraint, name::String = "" @@ -369,14 +369,14 @@ Function to add a [`Disjunction`](@ref) to a [`GDPModel`](@ref). Function to add a nested [`Disjunction`](@ref) to a [`GDPModel`](@ref). """ function disjunction( - model::JuMP.Model, + model::Model, disjunct_indicators, name::String = "" ) # TODO add kw argument to build exactly 1 constraint return _disjunction(error, model, disjunct_indicators, name) end function disjunction( - model::JuMP.Model, + model::Model, disjunct_indicators, nested_tag::DisjunctConstraint, name::String = "", @@ -442,7 +442,7 @@ function JuMP.build_constraint( # Cardinality logical constraint ) where {T <: LogicalVariableRef, S <: Union{Exactly, AtLeast, AtMost}} new_set = _jump_to_moi_selector(set)(length(func) + 1) new_func = Union{Number,LogicalVariableRef}[set.value, func...] - return JuMP.VectorConstraint(new_func, new_set) + return VectorConstraint(new_func, new_set) end function JuMP.build_constraint( # Cardinality logical constraint _error::Function, @@ -461,7 +461,7 @@ function JuMP.build_constraint( if !(func.head in _LogicalOperatorHeads) _error("Unrecognized logical operator `$(func.head)`.") else - return JuMP.ScalarConstraint(func, set) + return ScalarConstraint(func, set) end end @@ -480,7 +480,7 @@ end # Fallback for Affine/Quad expressions function JuMP.build_constraint( _error::Function, - expr::Union{JuMP.GenericAffExpr{C, LogicalVariableRef}, JuMP.GenericQuadExpr{C, LogicalVariableRef}}, + expr::Union{GenericAffExpr{C, LogicalVariableRef}, GenericQuadExpr{C, LogicalVariableRef}}, set::_MOI.AbstractScalarSet ) where {C} _error("Cannot add, subtract, or multiply with logical variables.") @@ -497,8 +497,8 @@ end """ function JuMP.add_constraint( - model::JuMP.Model, - c::JuMP.ScalarConstraint{<:F, S}, + model::Model, + c::ScalarConstraint{<:F, S}, name::String = "" ) where {F <: Union{LogicalVariableRef, _LogicalExpr}, S} @@ -506,8 +506,8 @@ Extend `JuMP.add_constraint` to allow creating logical proposition constraints for a [`GDPModel`](@ref) with the `@constraint` macro. function JuMP.add_constraint( - model::JuMP.Model, - c::JuMP.VectorConstraint{<:F, S, Shape}, + model::Model, + c::VectorConstraint{<:F, S, Shape}, name::String = "" ) where {F <: Union{Number, LogicalVariableRef, _LogicalExpr}, S, Shape} @@ -515,24 +515,24 @@ Extend `JuMP.add_constraint` to allow creating logical cardinality constraints for a [`GDPModel`](@ref) with the `@constraint` macro. """ function JuMP.add_constraint( - model::JuMP.Model, - c::JuMP.ScalarConstraint{F, S}, + model::Model, + c::ScalarConstraint{F, S}, name::String = "" ) where {F <: Union{LogicalVariableRef, _LogicalExpr}, S <: IsTrue} is_gdp_model(model) || error("Can only add logical constraints to `GDPModel`s.") - @assert all(JuMP.is_valid.(model, _get_constraint_variables(model, c))) "Constraint variables do not belong to model." + @assert all(is_valid.(model, _get_constraint_variables(model, c))) "Constraint variables do not belong to model." constr_data = ConstraintData(c, name) idx = _MOIUC.add_item(_logical_constraints(model), constr_data) _set_ready_to_optimize(model, false) return LogicalConstraintRef(model, idx) end function JuMP.add_constraint( - model::JuMP.Model, - c::JuMP.VectorConstraint{F, S, Shape}, + model::Model, + c::VectorConstraint{F, S, Shape}, name::String = "" ) where {F, S <: Union{_MOIAtLeast, _MOIAtMost, _MOIExactly}, Shape} is_gdp_model(model) || error("Can only add logical constraints to `GDPModel`s.") - @assert all(JuMP.is_valid.(model, _get_constraint_variables(model, c))) "Constraint variables do not belong to model." + @assert all(is_valid.(model, _get_constraint_variables(model, c))) "Constraint variables do not belong to model." constr_data = ConstraintData(c, name) idx = _MOIUC.add_item(_logical_constraints(model), constr_data) _set_ready_to_optimize(model, false) diff --git a/src/datatypes.jl b/src/datatypes.jl index 292b495..65292b0 100644 --- a/src/datatypes.jl +++ b/src/datatypes.jl @@ -3,7 +3,7 @@ ################################################################################ """ - LogicalVariable <: JuMP.AbstractVariable + LogicalVariable <: AbstractVariable A variable type the logical variables associated with disjuncts in a [`Disjunction`](@ref). @@ -11,7 +11,7 @@ A variable type the logical variables associated with disjuncts in a [`Disjuncti - `fix_value::Union{Nothing, Bool}`: A fixed boolean value if there is one. - `start_value::Union{Nothing, Bool}`: An initial guess if there is one. """ -struct LogicalVariable <: JuMP.AbstractVariable +struct LogicalVariable <: AbstractVariable fix_value::Union{Nothing, Bool} start_value::Union{Nothing, Bool} end @@ -50,8 +50,8 @@ end A type for looking up logical variables. """ -struct LogicalVariableRef <: JuMP.AbstractVariableRef - model::JuMP.Model # TODO: generalize for AbstractModels +struct LogicalVariableRef <: AbstractVariableRef + model::Model # TODO: generalize for AbstractModels index::LogicalVariableIndex end @@ -99,29 +99,29 @@ end # Create our own JuMP level sets to infer the dimension using the expression """ - AtLeast{T<:Union{Int,LogicalVariableRef}} <: JuMP.AbstractVectorSet + AtLeast{T<:Union{Int,LogicalVariableRef}} <: AbstractVectorSet Convenient alias for using [`_MOIAtLeast`](@ref). """ -struct AtLeast{T<:Union{Int,LogicalVariableRef}} <: JuMP.AbstractVectorSet +struct AtLeast{T<:Union{Int,LogicalVariableRef}} <: AbstractVectorSet value::T end """ - AtMost{T<:Union{Int,LogicalVariableRef}} <: JuMP.AbstractVectorSet + AtMost{T<:Union{Int,LogicalVariableRef}} <: AbstractVectorSet Convenient alias for using [`_MOIAtMost`](@ref). """ -struct AtMost{T<:Union{Int,LogicalVariableRef}} <: JuMP.AbstractVectorSet +struct AtMost{T<:Union{Int,LogicalVariableRef}} <: AbstractVectorSet value::T end """ - Exactly <: JuMP.AbstractVectorSet + Exactly <: AbstractVectorSet Convenient alias for using [`_MOIExactly`](@ref). """ -struct Exactly{T<:Union{Int,LogicalVariableRef}} <: JuMP.AbstractVectorSet +struct Exactly{T<:Union{Int,LogicalVariableRef}} <: AbstractVectorSet value::T end @@ -133,10 +133,10 @@ JuMP.moi_set(set::Exactly, dim::Int) = _MOIExactly(dim) ################################################################################ # LOGICAL CONSTRAINTS ################################################################################ -const _LogicalExpr = JuMP.GenericNonlinearExpr{LogicalVariableRef} +const _LogicalExpr = GenericNonlinearExpr{LogicalVariableRef} """ - ConstraintData{C <: JuMP.AbstractConstraint} + ConstraintData{C <: AbstractConstraint} A type for storing constraint objects in [`GDPData`](@ref) and any meta-data they possess. @@ -145,7 +145,7 @@ they possess. - `constraint::C`: The constraint. - `name::String`: The name of the proposition. """ -mutable struct ConstraintData{C <: JuMP.AbstractConstraint} +mutable struct ConstraintData{C <: AbstractConstraint} constraint::C name::String end @@ -168,7 +168,7 @@ end A type for looking up logical constraints. """ struct LogicalConstraintRef - model::JuMP.Model # TODO: generalize for AbstractModels + model::Model # TODO: generalize for AbstractModels index::LogicalConstraintIndex end @@ -194,7 +194,7 @@ struct DisjunctConstraint end # Create internal type for temporarily packaging constraints for disjuncts -struct _DisjunctConstraint{C <: JuMP.AbstractConstraint, L <: LogicalVariableRef} +struct _DisjunctConstraint{C <: AbstractConstraint, L <: LogicalVariableRef} constr::C lvref::L end @@ -217,7 +217,7 @@ end A type for looking up disjunctive constraints. """ struct DisjunctConstraintRef - model::JuMP.Model # TODO: generalize for AbstractModels + model::Model # TODO: generalize for AbstractModels index::DisjunctConstraintIndex end @@ -225,7 +225,7 @@ end # DISJUNCTIONS ################################################################################ """ - Disjunction <: JuMP.AbstractConstraint + Disjunction <: AbstractConstraint A type for a disjunctive constraint that is comprised of a collection of disjuncts of indicated by a unique [`LogicalVariableRef`](@ref). @@ -235,7 +235,7 @@ disjuncts of indicated by a unique [`LogicalVariableRef`](@ref). (indicators) that uniquely identify each disjunct in the disjunction. - `nested::Bool`: Is this disjunction nested within another disjunction? """ -struct Disjunction <: JuMP.AbstractConstraint +struct Disjunction <: AbstractConstraint indicators::Vector{LogicalVariableRef} nested::Bool end @@ -258,7 +258,7 @@ end A type for looking up disjunctive constraints. """ struct DisjunctionRef - model::JuMP.Model # TODO: generalize for AbstractModels + model::Model # TODO: generalize for AbstractModels index::DisjunctionIndex end @@ -325,9 +325,9 @@ A type for using the big-M reformulation approach for disjunctive constraints. struct BigM <: AbstractReformulationMethod value::Float64 tighten::Bool - variable_bounds::Dict{JuMP.VariableRef, Tuple{Float64, Float64}} # TODO support other number types? + variable_bounds::Dict{VariableRef, Tuple{Float64, Float64}} # TODO support other number types? function BigM(val = 1e9, tight = true) - new(val, tight, Dict{JuMP.VariableRef, Tuple{Float64, Float64}}()) + new(val, tight, Dict{VariableRef, Tuple{Float64, Float64}}()) end end # TODO add fields if needed @@ -342,11 +342,11 @@ constraints. """ struct Hull <: AbstractReformulationMethod # TODO add fields if needed value::Float64 - variable_bounds::Dict{JuMP.VariableRef, Tuple{Float64, Float64}} # TODO support other number types? + variable_bounds::Dict{VariableRef, Tuple{Float64, Float64}} # TODO support other number types? function Hull(ϵ::Float64 = 1e-6) - new(ϵ, Dict{JuMP.VariableRef, Tuple{Float64, Float64}}()) + new(ϵ, Dict{VariableRef, Tuple{Float64, Float64}}()) end - function Hull(ϵ::Float64, v_bounds::Dict{JuMP.VariableRef, Tuple{Float64, Float64}}) + function Hull(ϵ::Float64, v_bounds::Dict{VariableRef, Tuple{Float64, Float64}}) new(ϵ, v_bounds) end end @@ -355,15 +355,15 @@ end # temp struct to store variable disaggregations (reset for each disjunction) mutable struct _Hull <: AbstractReformulationMethod value::Float64 - variable_bounds::Dict{JuMP.VariableRef, Tuple{Float64, Float64}} # TODO support other number types? - disjunction_variables::Dict{JuMP.VariableRef, Vector{JuMP.VariableRef}} - disjunct_variables::Dict{Tuple{JuMP.VariableRef,JuMP.VariableRef}, JuMP.VariableRef} - function _Hull(method::Hull, vrefs::Set{JuMP.VariableRef}) + variable_bounds::Dict{VariableRef, Tuple{Float64, Float64}} # TODO support other number types? + disjunction_variables::Dict{VariableRef, Vector{VariableRef}} + disjunct_variables::Dict{Tuple{VariableRef,VariableRef}, VariableRef} + function _Hull(method::Hull, vrefs::Set{VariableRef}) new( method.value, method.variable_bounds, - Dict{JuMP.VariableRef, Vector{JuMP.VariableRef}}(vref => Vector{JuMP.VariableRef}() for vref in vrefs), - Dict{Tuple{JuMP.VariableRef,JuMP.VariableRef}, JuMP.VariableRef}() + Dict{VariableRef, Vector{VariableRef}}(vref => Vector{VariableRef}() for vref in vrefs), + Dict{Tuple{VariableRef,VariableRef}, VariableRef}() ) end end @@ -391,12 +391,12 @@ mutable struct GDPData disjunctions::_MOIUC.CleverDict{DisjunctionIndex, ConstraintData{Disjunction}} # Indicator variable mappings - indicator_to_binary::Dict{LogicalVariableRef, JuMP.VariableRef} + indicator_to_binary::Dict{LogicalVariableRef, VariableRef} indicator_to_constraints::Dict{LogicalVariableRef, Vector{Union{DisjunctConstraintRef, DisjunctionRef}}} # Reformulation variables and constraints - reformulation_variables::Vector{JuMP.VariableRef} - reformulation_constraints::Vector{JuMP.ConstraintRef} + reformulation_variables::Vector{VariableRef} + reformulation_constraints::Vector{ConstraintRef} # Solution data solution_method::Union{Nothing, AbstractSolutionMethod} @@ -408,10 +408,10 @@ mutable struct GDPData _MOIUC.CleverDict{LogicalConstraintIndex, ConstraintData}(), _MOIUC.CleverDict{DisjunctConstraintIndex, ConstraintData}(), _MOIUC.CleverDict{DisjunctionIndex, ConstraintData{Disjunction}}(), - Dict{LogicalVariableRef, JuMP.VariableRef}(), + Dict{LogicalVariableRef, VariableRef}(), Dict{LogicalVariableRef, Vector{Union{DisjunctConstraintRef, DisjunctionRef}}}(), - Vector{JuMP.VariableRef}(), - Vector{JuMP.ConstraintRef}(), + Vector{VariableRef}(), + Vector{ConstraintRef}(), nothing, false, ) diff --git a/src/hull.jl b/src/hull.jl index a80acd1..84afb10 100644 --- a/src/hull.jl +++ b/src/hull.jl @@ -1,47 +1,47 @@ ################################################################################ # VARIABLE DISAGGREGATION ################################################################################ -function _update_variable_bounds(vref::JuMP.VariableRef, method::Hull) - if JuMP.is_binary(vref) #not used +function _update_variable_bounds(vref::VariableRef, method::Hull) + if is_binary(vref) #not used lb, ub = 0, 1 - elseif !JuMP.has_lower_bound(vref) || !JuMP.has_upper_bound(vref) + elseif !has_lower_bound(vref) || !has_upper_bound(vref) error("Variable $vref must have both lower and upper bounds defined when using the Hull reformulation.") else - lb = min(0, JuMP.lower_bound(vref)) - ub = max(0, JuMP.upper_bound(vref)) + lb = min(0, lower_bound(vref)) + ub = max(0, upper_bound(vref)) end return lb, ub end -function _disaggregate_variables(model::JuMP.Model, lvref::LogicalVariableRef, vrefs::Set{JuMP.VariableRef}, method::_Hull) +function _disaggregate_variables(model::Model, lvref::LogicalVariableRef, vrefs::Set{VariableRef}, method::_Hull) #create disaggregated variables for that disjunct for vref in vrefs - JuMP.is_binary(vref) && continue #skip binary variables + is_binary(vref) && continue #skip binary variables _disaggregate_variable(model, lvref, vref, method) #create disaggregated var for that disjunct end end -function _disaggregate_variable(model::JuMP.Model, lvref::LogicalVariableRef, vref::JuMP.VariableRef, method::_Hull) +function _disaggregate_variable(model::Model, lvref::LogicalVariableRef, vref::VariableRef, method::_Hull) #create disaggregated vref lb, ub = method.variable_bounds[vref] - dvref = JuMP.@variable(model, base_name = "$(vref)_$(lvref)", lower_bound = lb, upper_bound = ub) + dvref = @variable(model, base_name = "$(vref)_$(lvref)", lower_bound = lb, upper_bound = ub) push!(_reformulation_variables(model), dvref) #get binary indicator variable bvref = _indicator_to_binary(model)[lvref] #temp storage if !haskey(method.disjunction_variables, vref) - method.disjunction_variables[vref] = Vector{JuMP.VariableRef}() + method.disjunction_variables[vref] = Vector{VariableRef}() end push!(method.disjunction_variables[vref], dvref) method.disjunct_variables[vref, bvref] = dvref #create bounding constraints - dvname = JuMP.name(dvref) + dvname = name(dvref) lbname = isempty(dvname) ? "" : "$(dvname)_lower_bound" ubname = isempty(dvname) ? "" : "$(dvname)_upper_bound" - new_con_lb_ref = JuMP.add_constraint(model, - JuMP.build_constraint(error, lb*bvref - dvref, _MOI.LessThan(0)), + new_con_lb_ref = add_constraint(model, + build_constraint(error, lb*bvref - dvref, _MOI.LessThan(0)), lbname ) - new_con_ub_ref = JuMP.add_constraint(model, - JuMP.build_constraint(error, dvref - ub*bvref, _MOI.LessThan(0)), + new_con_ub_ref = add_constraint(model, + build_constraint(error, dvref - ub*bvref, _MOI.LessThan(0)), ubname ) push!(_reformulation_constraints(model), new_con_lb_ref, new_con_ub_ref) @@ -51,11 +51,11 @@ end ################################################################################ # VARIABLE AGGREGATION ################################################################################ -function _aggregate_variable(model::JuMP.Model, ref_cons::Vector{JuMP.AbstractConstraint}, vref::JuMP.VariableRef, method::_Hull) - JuMP.is_binary(vref) && return #skip binary variables - con_expr = JuMP.@expression(model, -vref + sum(method.disjunction_variables[vref])) +function _aggregate_variable(model::Model, ref_cons::Vector{AbstractConstraint}, vref::VariableRef, method::_Hull) + is_binary(vref) && return #skip binary variables + con_expr = @expression(model, -vref + sum(method.disjunction_variables[vref])) push!(ref_cons, - JuMP.build_constraint(error, con_expr, _MOI.EqualTo(0)) + build_constraint(error, con_expr, _MOI.EqualTo(0)) ) return end @@ -64,22 +64,22 @@ end # CONSTRAINT DISAGGREGATION ################################################################################ # variable -function _disaggregate_expression(model::JuMP.Model, vref::JuMP.VariableRef, bvref::JuMP.VariableRef, method::_Hull) - if JuMP.is_binary(vref) || !haskey(method.disjunct_variables, (vref, bvref)) #keep any binary variables or nested disaggregated variables unchanged +function _disaggregate_expression(model::Model, vref::VariableRef, bvref::VariableRef, method::_Hull) + if is_binary(vref) || !haskey(method.disjunct_variables, (vref, bvref)) #keep any binary variables or nested disaggregated variables unchanged return vref else #replace with disaggregated form return method.disjunct_variables[vref, bvref] end end # affine expression -function _disaggregate_expression(model::JuMP.Model, aff::JuMP.AffExpr, bvref::JuMP.VariableRef, method::_Hull) - new_expr = JuMP.@expression(model, aff.constant*bvref) #multiply constant by binary indicator variable +function _disaggregate_expression(model::Model, aff::AffExpr, bvref::VariableRef, method::_Hull) + new_expr = @expression(model, aff.constant*bvref) #multiply constant by binary indicator variable for (vref, coeff) in aff.terms - if JuMP.is_binary(vref) || !haskey(method.disjunct_variables, (vref, bvref)) #keep any binary variables or nested disaggregated variables unchanged - JuMP.add_to_expression!(new_expr, coeff*vref) + if is_binary(vref) || !haskey(method.disjunct_variables, (vref, bvref)) #keep any binary variables or nested disaggregated variables unchanged + add_to_expression!(new_expr, coeff*vref) else #replace other vars with disaggregated form dvref = method.disjunct_variables[vref, bvref] - JuMP.add_to_expression!(new_expr, coeff*dvref) + add_to_expression!(new_expr, coeff*dvref) end end return new_expr @@ -87,7 +87,7 @@ end # quadratic expression # TODO review what happens when there are bilinear terms with binary variables involved since these are not being disaggregated # (e.g., complementarity constraints; though likely irrelevant)... -function _disaggregate_expression(model::JuMP.Model, quad::JuMP.QuadExpr, bvref::JuMP.VariableRef, method::_Hull) +function _disaggregate_expression(model::Model, quad::QuadExpr, bvref::VariableRef, method::_Hull) #get affine part new_expr = _disaggregate_expression(model, quad.aff, bvref, method) #get nonlinear part @@ -100,22 +100,22 @@ function _disaggregate_expression(model::JuMP.Model, quad::JuMP.QuadExpr, bvref: return new_expr end # constant in NonlinearExpr -function _disaggregate_nl_expression(model::JuMP.Model, c::Number, ::JuMP.VariableRef, method::_Hull) +function _disaggregate_nl_expression(model::Model, c::Number, ::VariableRef, method::_Hull) return c end # variable in NonlinearExpr -function _disaggregate_nl_expression(model::JuMP.Model, vref::JuMP.VariableRef, bvref::JuMP.VariableRef, method::_Hull) +function _disaggregate_nl_expression(model::Model, vref::VariableRef, bvref::VariableRef, method::_Hull) ϵ = method.value dvref = method.disjunct_variables[vref, bvref] new_var = dvref / ((1-ϵ)*bvref+ϵ) return new_var end # affine expression in NonlinearExpr -function _disaggregate_nl_expression(model::JuMP.Model, aff::JuMP.AffExpr, bvref::JuMP.VariableRef, method::_Hull) +function _disaggregate_nl_expression(model::Model, aff::AffExpr, bvref::VariableRef, method::_Hull) new_expr = aff.constant ϵ = method.value for (vref, coeff) in aff.terms - if JuMP.is_binary(vref) #keep any binary variables undisaggregated + if is_binary(vref) #keep any binary variables undisaggregated dvref = vref else #replace other vars with disaggregated form dvref = method.disjunct_variables[vref, bvref] @@ -125,7 +125,7 @@ function _disaggregate_nl_expression(model::JuMP.Model, aff::JuMP.AffExpr, bvref return new_expr end # quadratic expression in NonlinearExpr -function _disaggregate_nl_expression(model::JuMP.Model, quad::JuMP.QuadExpr, bvref::JuMP.VariableRef, method::_Hull) +function _disaggregate_nl_expression(model::Model, quad::QuadExpr, bvref::VariableRef, method::_Hull) #get affine part new_expr = _disaggregate_nl_expression(model, quad.aff, bvref, method) #get quadratic part @@ -138,12 +138,12 @@ function _disaggregate_nl_expression(model::JuMP.Model, quad::JuMP.QuadExpr, bvr return new_expr end # nonlinear expression in NonlinearExpr -function _disaggregate_nl_expression(model::JuMP.Model, nlp::JuMP.NonlinearExpr, bvref::JuMP.VariableRef, method::_Hull) +function _disaggregate_nl_expression(model::Model, nlp::NonlinearExpr, bvref::VariableRef, method::_Hull) new_args = Vector{Any}(undef, length(nlp.args)) for (i,arg) in enumerate(nlp.args) new_args[i] = _disaggregate_nl_expression(model, arg, bvref, method) end - new_expr = JuMP.NonlinearExpr(nlp.head, new_args) + new_expr = NonlinearExpr(nlp.head, new_args) return new_expr end @@ -151,95 +151,95 @@ end # HULL REFORMULATION ################################################################################ function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::_Hull -) where {T <: Union{JuMP.VariableRef, JuMP.AffExpr, JuMP.QuadExpr}, S <: Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.EqualTo}} +) where {T <: Union{VariableRef, AffExpr, QuadExpr}, S <: Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.EqualTo}} new_func = _disaggregate_expression(model, con.func, bvref, method) set_value = _set_value(con.set) new_func -= set_value*bvref - reform_con = JuMP.build_constraint(error, new_func, S(0)) + reform_con = build_constraint(error, new_func, S(0)) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S, R}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S, R}, + bvref::VariableRef, method::_Hull -) where {T <: Union{JuMP.VariableRef, JuMP.AffExpr, JuMP.QuadExpr}, S <: Union{_MOI.Nonpositives, _MOI.Nonnegatives, _MOI.Zeros}, R} - new_func = JuMP.@expression(model, [i=1:con.set.dimension], +) where {T <: Union{VariableRef, AffExpr, QuadExpr}, S <: Union{_MOI.Nonpositives, _MOI.Nonnegatives, _MOI.Zeros}, R} + new_func = @expression(model, [i=1:con.set.dimension], _disaggregate_expression(model, con.func[i], bvref, method) ) - reform_con = JuMP.build_constraint(error, new_func, con.set) + reform_con = build_constraint(error, new_func, con.set) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::_Hull -) where {T <: JuMP.GenericNonlinearExpr, S <: Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.EqualTo}} +) where {T <: GenericNonlinearExpr, S <: Union{_MOI.LessThan, _MOI.GreaterThan, _MOI.EqualTo}} con_func = _disaggregate_nl_expression(model, con.func, bvref, method) - con_func0 = JuMP.value(v -> 0.0, con.func) + con_func0 = value(v -> 0.0, con.func) if isinf(con_func0) error("Operator `$(con.func.head)` is not defined at 0, causing the perspective function on the Hull reformulation to fail.") end ϵ = method.value set_value = _set_value(con.set) - new_func = JuMP.@expression(model, ((1-ϵ)*bvref+ϵ)*con_func - ϵ*(1-bvref)*con_func0 - set_value*bvref) - reform_con = JuMP.build_constraint(error, new_func, S(0)) + new_func = @expression(model, ((1-ϵ)*bvref+ϵ)*con_func - ϵ*(1-bvref)*con_func0 - set_value*bvref) + reform_con = build_constraint(error, new_func, S(0)) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S, R}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S, R}, + bvref::VariableRef, method::_Hull -) where {T <: JuMP.GenericNonlinearExpr, S <: Union{_MOI.Nonpositives, _MOI.Nonnegatives, _MOI.Zeros}, R} - con_func = JuMP.@expression(model, [i=1:con.set.dimension], +) where {T <: GenericNonlinearExpr, S <: Union{_MOI.Nonpositives, _MOI.Nonnegatives, _MOI.Zeros}, R} + con_func = @expression(model, [i=1:con.set.dimension], _disaggregate_nl_expression(model, con.func[i], bvref, method) ) - con_func0 = JuMP.value.(v -> 0.0, con.func) + con_func0 = value.(v -> 0.0, con.func) if any(isinf.(con_func0)) error("At least of of the operators `$([func.head for func in con.func])` is not defined at 0, causing the perspective function on the Hull reformulation to fail.") end ϵ = method.value - new_func = JuMP.@expression(model, [i=1:con.set.dimension], + new_func = @expression(model, [i=1:con.set.dimension], ((1-ϵ)*bvref+ϵ)*con_func[i] - ϵ*(1-bvref)*con_func0[i] ) - reform_con = JuMP.build_constraint(error, new_func, con.set) + reform_con = build_constraint(error, new_func, con.set) return [reform_con] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::_Hull -) where {T <: Union{JuMP.VariableRef, JuMP.AffExpr, JuMP.QuadExpr}, S <: _MOI.Interval} +) where {T <: Union{VariableRef, AffExpr, QuadExpr}, S <: _MOI.Interval} new_func = _disaggregate_expression(model, con.func, bvref, method) - new_func_gt = JuMP.@expression(model, new_func - con.set.lower*bvref) - new_func_lt = JuMP.@expression(model, new_func - con.set.upper*bvref) - reform_con_gt = JuMP.build_constraint(error, new_func_gt, _MOI.GreaterThan(0)) - reform_con_lt = JuMP.build_constraint(error, new_func_lt, _MOI.LessThan(0)) + new_func_gt = @expression(model, new_func - con.set.lower*bvref) + new_func_lt = @expression(model, new_func - con.set.upper*bvref) + reform_con_gt = build_constraint(error, new_func_gt, _MOI.GreaterThan(0)) + reform_con_lt = build_constraint(error, new_func_lt, _MOI.LessThan(0)) return [reform_con_gt, reform_con_lt] end function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::_Hull -) where {T <: JuMP.GenericNonlinearExpr, S <: _MOI.Interval} +) where {T <: GenericNonlinearExpr, S <: _MOI.Interval} con_func = _disaggregate_nl_expression(model, con.func, bvref, method) - con_func0 = JuMP.value(v -> 0.0, con.func) + con_func0 = value(v -> 0.0, con.func) if isinf(con_func0) error("Operator `$(con.func.head)` is not defined at 0, causing the perspective function on the Hull reformulation to fail.") end ϵ = method.value - new_func = JuMP.@expression(model, ((1-ϵ)*bvref+ϵ) * con_func - ϵ*(1-bvref)*con_func0) - new_func_gt = JuMP.@expression(model, new_func - con.set.lower*bvref) - new_func_lt = JuMP.@expression(model, new_func - con.set.upper*bvref) - reform_con_gt = JuMP.build_constraint(error, new_func_gt, _MOI.GreaterThan(0)) - reform_con_lt = JuMP.build_constraint(error, new_func_lt, _MOI.LessThan(0)) + new_func = @expression(model, ((1-ϵ)*bvref+ϵ) * con_func - ϵ*(1-bvref)*con_func0) + new_func_gt = @expression(model, new_func - con.set.lower*bvref) + new_func_lt = @expression(model, new_func - con.set.upper*bvref) + reform_con_gt = build_constraint(error, new_func_gt, _MOI.GreaterThan(0)) + reform_con_lt = build_constraint(error, new_func_lt, _MOI.LessThan(0)) return [reform_con_gt, reform_con_lt] end \ No newline at end of file diff --git a/src/indicator.jl b/src/indicator.jl index 2aabb13..d199827 100644 --- a/src/indicator.jl +++ b/src/indicator.jl @@ -3,32 +3,32 @@ ################################################################################ #scalar disjunct constraint function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.ScalarConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::ScalarConstraint{T, S}, + bvref::VariableRef, method::Indicator ) where {T, S} - reform_con = JuMP.build_constraint(error, [1*bvref, con.func], _MOI.Indicator{_MOI.ACTIVATE_ON_ONE}(con.set)) + reform_con = build_constraint(error, [1*bvref, con.func], _MOI.Indicator{_MOI.ACTIVATE_ON_ONE}(con.set)) return [reform_con] end #vectorized disjunct constraint function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S}, + bvref::VariableRef, method::Indicator ) where {T, S} set = _vec_to_scalar_set(con.set) return [ - JuMP.build_constraint(error, [1*bvref, f], _MOI.Indicator{_MOI.ACTIVATE_ON_ONE}(set)) + build_constraint(error, [1*bvref, f], _MOI.Indicator{_MOI.ACTIVATE_ON_ONE}(set)) for f in con.func ] end #nested indicator reformulation. NOTE: the user needs to provide the appropriate linking constraint for the logical variables for this to work (e.g. w in Exactly(y[1]) to link the parent disjunct y[1] to the nested disjunction w) function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.VectorConstraint{T, S}, - bvref::JuMP.VariableRef, + model::Model, + con::VectorConstraint{T, S}, + bvref::VariableRef, method::Indicator ) where {T, S <: _MOI.Indicator} return [con] diff --git a/src/logic.jl b/src/logic.jl index 70ed443..b529093 100644 --- a/src/logic.jl +++ b/src/logic.jl @@ -13,8 +13,8 @@ for (name, alt, head) in ( ) # make operators @eval begin - const $name = JuMP.NonlinearOperator((vs...) -> _op_fallback($(Meta.quot(name))), $(Meta.quot(head))) - const $alt = JuMP.NonlinearOperator((vs...) -> _op_fallback($(Meta.quot(alt))), $(Meta.quot(head))) + const $name = NonlinearOperator((vs...) -> _op_fallback($(Meta.quot(name))), $(Meta.quot(head))) + const $alt = NonlinearOperator((vs...) -> _op_fallback($(Meta.quot(alt))), $(Meta.quot(head))) end end for (name, alt, head, func) in ( @@ -24,8 +24,8 @@ for (name, alt, head, func) in ( ) # make operators @eval begin - const $name = JuMP.NonlinearOperator($func, $(Meta.quot(head))) - const $alt = JuMP.NonlinearOperator($func, $(Meta.quot(head))) + const $name = NonlinearOperator($func, $(Meta.quot(head))) + const $alt = NonlinearOperator($func, $(Meta.quot(head))) end end @@ -218,21 +218,21 @@ end # SELECTOR REFORMULATION ################################################################################ # cardinality constraint reformulation -function _reformulate_selector(model::JuMP.Model, func, set::Union{_MOIAtLeast, _MOIAtMost, _MOIExactly}) +function _reformulate_selector(model::Model, func, set::Union{_MOIAtLeast, _MOIAtMost, _MOIExactly}) dict = _indicator_to_binary(model) bvrefs = [dict[lvref] for lvref in func[2:end]] new_set = _vec_to_scalar_set(set)(func[1].constant) - cref = JuMP.add_constraint(model, - JuMP.build_constraint(error, JuMP.@expression(model, sum(bvrefs)), new_set) + cref = add_constraint(model, + build_constraint(error, @expression(model, sum(bvrefs)), new_set) ) push!(_reformulation_constraints(model), cref) end -function _reformulate_selector(model::JuMP.Model, func::Vector{LogicalVariableRef}, set::Union{_MOIAtLeast, _MOIAtMost, _MOIExactly}) +function _reformulate_selector(model::Model, func::Vector{LogicalVariableRef}, set::Union{_MOIAtLeast, _MOIAtMost, _MOIExactly}) dict = _indicator_to_binary(model) bvref, bvrefs... = [dict[lvref] for lvref in func] new_set = _vec_to_scalar_set(set)(0) - cref = JuMP.add_constraint(model, - JuMP.build_constraint(error, JuMP.@expression(model, sum(bvrefs) - bvref), new_set) + cref = add_constraint(model, + build_constraint(error, @expression(model, sum(bvrefs) - bvref), new_set) ) push!(_reformulation_constraints(model), cref) end @@ -240,7 +240,7 @@ end ################################################################################ # PROPOSITION REFORMULATION ################################################################################ -function _reformulate_proposition(model::JuMP.Model, lexpr::_LogicalExpr) +function _reformulate_proposition(model::Model, lexpr::_LogicalExpr) expr = _to_cnf(lexpr) if expr.head == :&& for arg in expr.args @@ -258,31 +258,31 @@ _isa_literal(v::LogicalVariableRef) = true _isa_literal(v::_LogicalExpr) = (v.head == :!) && (length(v.args) == 1) && _isa_literal(v.args[1]) _isa_literal(v) = false -function _add_reformulated_proposition(model::JuMP.Model, arg::Union{LogicalVariableRef,_LogicalExpr}) +function _add_reformulated_proposition(model::Model, arg::Union{LogicalVariableRef,_LogicalExpr}) func = _reformulate_clause(model, arg) if !isempty(func.terms) && !all(iszero.(values(func.terms))) - con = JuMP.build_constraint(error, func, _MOI.GreaterThan(1)) - cref = JuMP.add_constraint(model, con) + con = build_constraint(error, func, _MOI.GreaterThan(1)) + cref = add_constraint(model, con) push!(_reformulation_constraints(model), cref) end return end -function _reformulate_clause(model::JuMP.Model, lvref::LogicalVariableRef) +function _reformulate_clause(model::Model, lvref::LogicalVariableRef) func = 1 * _indicator_to_binary(model)[lvref] return func end -function _reformulate_clause(model::JuMP.Model, lexpr::_LogicalExpr) - func = zero(JuMP.AffExpr) #initialize func expression +function _reformulate_clause(model::Model, lexpr::_LogicalExpr) + func = zero(AffExpr) #initialize func expression if _isa_literal(lexpr) - JuMP.add_to_expression!(func, 1 - _reformulate_clause(model, lexpr.args[1])) + add_to_expression!(func, 1 - _reformulate_clause(model, lexpr.args[1])) elseif lexpr.head == :|| for literal in lexpr.args if literal isa LogicalVariableRef - JuMP.add_to_expression!(func, _reformulate_clause(model, literal)) + add_to_expression!(func, _reformulate_clause(model, literal)) elseif _isa_literal(literal) - JuMP.add_to_expression!(func, 1 - _reformulate_clause(model, literal.args[1])) + add_to_expression!(func, 1 - _reformulate_clause(model, literal.args[1])) else error("Expression was not converted to proper Conjunctive Normal Form:\n$literal is not a literal.") end diff --git a/src/macros.jl b/src/macros.jl index 9565508..316009a 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -111,7 +111,7 @@ function _error_if_cannot_register( model, name::Symbol ) - if haskey(JuMP.object_dictionary(model), name) + if haskey(object_dictionary(model), name) _error("An object of name $name is already attached to this model. If ", "this is intended, consider using the anonymous construction ", "syntax, e.g., `x = @macro_name(model, ...)` where the name ", @@ -248,7 +248,7 @@ macro disjunction(model, args...) _add_kwargs(creation_code, extra_kwargs) else # we have a container of parameters - idxvars, inds = JuMP.Containers.build_ref_sets(_error, c) + idxvars, inds = Containers.build_ref_sets(_error, c) if model in idxvars _error("Index $(model) is the same symbol as the model. Use a ", "different name for the index.") @@ -257,7 +257,7 @@ macro disjunction(model, args...) disjunction_call = :( _disjunction($_error, $esc_model, $x, $name_code) ) _add_positional_args(disjunction_call, extra) _add_kwargs(disjunction_call, extra_kwargs) - creation_code = JuMP.Containers.container_code(idxvars, inds, disjunction_call, + creation_code = Containers.container_code(idxvars, inds, disjunction_call, container_type) end diff --git a/src/model.jl b/src/model.jl index 634aba1..e53984e 100644 --- a/src/model.jl +++ b/src/model.jl @@ -3,27 +3,27 @@ ################################################################################ """ - GDPModel([optimizer]; [kwargs...])::JuMP.Model + GDPModel([optimizer]; [kwargs...])::Model The core model object for building general disjunction programming models. """ function GDPModel(args...; kwargs...) - model = JuMP.Model(args...; kwargs...) + model = Model(args...; kwargs...) model.ext[:GDP] = GDPData() - JuMP.set_optimize_hook(model, _optimize_hook) + set_optimize_hook(model, _optimize_hook) return model end # Define what should happen to solve a GDPModel # See https://github.com/jump-dev/JuMP.jl/blob/9ea1df38fd320f864ab4c93c78631d0f15939c0b/src/JuMP.jl#L718-L745 function _optimize_hook( - model::JuMP.Model; + model::Model; method::AbstractSolutionMethod ) # can add more kwargs if wanted if !_ready_to_optimize(model) || _solution_method(model) != method reformulate_model(model, method) end - return JuMP.optimize!(model; ignore_optimize_hook = true) + return optimize!(model; ignore_optimize_hook = true) end ################################################################################ @@ -31,44 +31,44 @@ end ################################################################################ """ - gdp_data(model::JuMP.Model)::GDPData + gdp_data(model::Model)::GDPData Extract the [`GDPData`](@ref) from a `GDPModel`. """ -function gdp_data(model::JuMP.Model) +function gdp_data(model::Model) is_gdp_model(model) || error("Cannot access GDP data from a regular `JuMP.Model`.") return model.ext[:GDP] end """ - is_gdp_model(model::JuMP.Model)::Bool + is_gdp_model(model::Model)::Bool Return if `model` was created via the [`GDPModel`](@ref) constructor. """ -function is_gdp_model(model::JuMP.Model) +function is_gdp_model(model::Model) return haskey(model.ext, :GDP) end # Create accessors for GDP data fields -_logical_variables(model::JuMP.Model) = gdp_data(model).logical_variables -_logical_constraints(model::JuMP.Model) = gdp_data(model).logical_constraints -_disjunct_constraints(model::JuMP.Model) = gdp_data(model).disjunct_constraints -_disjunctions(model::JuMP.Model) = gdp_data(model).disjunctions -_indicator_to_binary(model::JuMP.Model) = gdp_data(model).indicator_to_binary -_indicator_to_constraints(model::JuMP.Model) = gdp_data(model).indicator_to_constraints -_reformulation_variables(model::JuMP.Model) = gdp_data(model).reformulation_variables -_reformulation_constraints(model::JuMP.Model) = gdp_data(model).reformulation_constraints -_ready_to_optimize(model::JuMP.Model) = gdp_data(model).ready_to_optimize # Determine if the model is ready to call `optimize!` without a optimize hook -_solution_method(model::JuMP.Model) = gdp_data(model).solution_method # Get the current solution method +_logical_variables(model::Model) = gdp_data(model).logical_variables +_logical_constraints(model::Model) = gdp_data(model).logical_constraints +_disjunct_constraints(model::Model) = gdp_data(model).disjunct_constraints +_disjunctions(model::Model) = gdp_data(model).disjunctions +_indicator_to_binary(model::Model) = gdp_data(model).indicator_to_binary +_indicator_to_constraints(model::Model) = gdp_data(model).indicator_to_constraints +_reformulation_variables(model::Model) = gdp_data(model).reformulation_variables +_reformulation_constraints(model::Model) = gdp_data(model).reformulation_constraints +_ready_to_optimize(model::Model) = gdp_data(model).ready_to_optimize # Determine if the model is ready to call `optimize!` without a optimize hook +_solution_method(model::Model) = gdp_data(model).solution_method # Get the current solution method # Update the ready_to_optimize field -function _set_ready_to_optimize(model::JuMP.Model, is_ready::Bool) +function _set_ready_to_optimize(model::Model, is_ready::Bool) gdp_data(model).ready_to_optimize = is_ready return end # Set the solution method -function _set_solution_method(model::JuMP.Model, method::AbstractSolutionMethod) +function _set_solution_method(model::Model, method::AbstractSolutionMethod) gdp_data(model).solution_method = method return end diff --git a/src/reformulate.jl b/src/reformulate.jl index dcef0d6..031a148 100644 --- a/src/reformulate.jl +++ b/src/reformulate.jl @@ -2,12 +2,12 @@ # REFORMULATE ################################################################################ """ - reformulate_model(model::JuMP.Model, method::AbstractSolutionMethod) + reformulate_model(model::Model, method::AbstractSolutionMethod) Reformulate a `GDPModel` using the specified `method`. Prior to reformulation, all previous reformulation variables and constraints are deleted. """ -function reformulate_model(model::JuMP.Model, method::AbstractSolutionMethod) +function reformulate_model(model::Model, method::AbstractSolutionMethod) #clear all previous reformulations _clear_reformulations(model) #reformulate @@ -19,9 +19,9 @@ function reformulate_model(model::JuMP.Model, method::AbstractSolutionMethod) _set_ready_to_optimize(model, true) end -function _clear_reformulations(model::JuMP.Model) - JuMP.delete.(model, _reformulation_constraints(model)) - JuMP.delete.(model, _reformulation_variables(model)) +function _clear_reformulations(model::Model) + delete.(model, _reformulation_constraints(model)) + delete.(model, _reformulation_variables(model)) empty!(gdp_data(model).reformulation_constraints) empty!(gdp_data(model).reformulation_variables) end @@ -30,13 +30,13 @@ end # LOGICAL VARIABLES ################################################################################ # create binary (indicator) variables for logic variables. -function _reformulate_logical_variables(model::JuMP.Model) +function _reformulate_logical_variables(model::Model) for (lv_idx, lv_data) in _logical_variables(model) lv = lv_data.variable lvref = LogicalVariableRef(model, lv_idx) - bvref = JuMP.@variable(model, base_name = lv_data.name, binary = true, start = lv.start_value) - if JuMP.is_fixed(lvref) - JuMP.fix(bvref, JuMP.fix_value(lvref)) + bvref = @variable(model, base_name = lv_data.name, binary = true, start = lv.start_value) + if is_fixed(lvref) + fix(bvref, fix_value(lvref)) end push!(_reformulation_variables(model), bvref) _indicator_to_binary(model)[lvref] = bvref @@ -47,25 +47,25 @@ end # DISJUNCTIONS ################################################################################ # disjunctions -function _reformulate_all_disjunctions(model::JuMP.Model, method::AbstractReformulationMethod) +function _reformulate_all_disjunctions(model::Model, method::AbstractReformulationMethod) for (_, disj) in _disjunctions(model) disj.constraint.nested && continue #only reformulate top level disjunctions ref_cons = reformulate_disjunction(model, disj.constraint, method) for (i, ref_con) in enumerate(ref_cons) name = isempty(disj.name) ? "" : string(disj.name,"_$i") - cref = JuMP.add_constraint(model, ref_con, name) + cref = add_constraint(model, ref_con, name) push!(_reformulation_constraints(model), cref) end end end -function _reformulate_disjunctions(model::JuMP.Model, method::AbstractReformulationMethod) +function _reformulate_disjunctions(model::Model, method::AbstractReformulationMethod) _reformulate_all_disjunctions(model, method) end -function _reformulate_disjunctions(model::JuMP.Model, method::BigM) +function _reformulate_disjunctions(model::Model, method::BigM) method.tighten && _query_variable_bounds(model, method) _reformulate_all_disjunctions(model, method) end -function _reformulate_disjunctions(model::JuMP.Model, method::Hull) +function _reformulate_disjunctions(model::Model, method::Hull) _query_variable_bounds(model, method) _reformulate_all_disjunctions(model, method) end @@ -73,7 +73,7 @@ end # disjuncts """ reformulate_disjunction( - model::JuMP.Model, + model::Model, disj::Disjunction, method::AbstractReformulationMethod ) where {T<:Disjunction} @@ -85,16 +85,16 @@ The `disj` field is the `ConstraintData` object for the disjunction, stored in t `disjunctions` field of the `GDPData` object. """ # generic fallback (e.g., BigM, Indicator) -function reformulate_disjunction(model::JuMP.Model, disj::Disjunction, method::AbstractReformulationMethod) - ref_cons = Vector{JuMP.AbstractConstraint}() #store reformulated constraints +function reformulate_disjunction(model::Model, disj::Disjunction, method::AbstractReformulationMethod) + ref_cons = Vector{AbstractConstraint}() #store reformulated constraints for d in disj.indicators _reformulate_disjunct(model, ref_cons, d, method) end return ref_cons end # hull specific -function reformulate_disjunction(model::JuMP.Model, disj::Disjunction, method::Hull) - ref_cons = Vector{JuMP.AbstractConstraint}() #store reformulated constraints +function reformulate_disjunction(model::Model, disj::Disjunction, method::Hull) + ref_cons = Vector{AbstractConstraint}() #store reformulated constraints disj_vrefs = _get_disjunction_variables(model, disj) hull = _Hull(method, disj_vrefs) for d in disj.indicators #reformulate each disjunct @@ -106,17 +106,17 @@ function reformulate_disjunction(model::JuMP.Model, disj::Disjunction, method::H end return ref_cons end -function reformulate_disjunction(model::JuMP.Model, disj::Disjunction, method::_Hull) +function reformulate_disjunction(model::Model, disj::Disjunction, method::_Hull) return reformulate_disjunction(model, disj, Hull(method.value, method.variable_bounds)) end # individual disjuncts -function _reformulate_disjunct(model::JuMP.Model, ref_cons::Vector{JuMP.AbstractConstraint}, lvref::LogicalVariableRef, method::AbstractReformulationMethod) +function _reformulate_disjunct(model::Model, ref_cons::Vector{AbstractConstraint}, lvref::LogicalVariableRef, method::AbstractReformulationMethod) #reformulate each constraint and add to the model bvref = _indicator_to_binary(model)[lvref] !haskey(_indicator_to_constraints(model), lvref) && return #skip if disjunct is empty for cref in _indicator_to_constraints(model)[lvref] - con = JuMP.constraint_object(cref) + con = constraint_object(cref) append!(ref_cons, reformulate_disjunct_constraint(model, con, bvref, method)) end return @@ -125,13 +125,13 @@ end # reformulation for nested disjunction # NOTE: name of inner disjunction (if given) is currently lost (not passed upwards) function reformulate_disjunct_constraint( - model::JuMP.Model, + model::Model, con::Disjunction, - bvref::JuMP.VariableRef, + bvref::VariableRef, method::AbstractReformulationMethod ) ref_cons = reformulate_disjunction(model, con, method) - new_ref_cons = Vector{JuMP.AbstractConstraint}() + new_ref_cons = Vector{AbstractConstraint}() for ref_con in ref_cons append!(new_ref_cons, reformulate_disjunct_constraint(model, ref_con, bvref, method)) end @@ -140,9 +140,9 @@ end # reformulation fallback for individual disjunct constraints function reformulate_disjunct_constraint( - model::JuMP.Model, - con::JuMP.AbstractConstraint, - bvref::JuMP.VariableRef, + model::Model, + con::AbstractConstraint, + bvref::VariableRef, method::AbstractReformulationMethod ) error("$(typeof(method)) reformulation for constraint $con is not supported yet.") @@ -152,15 +152,15 @@ end # LOGICAL CONSTRAINT REFORMULATION ################################################################################ # all logical constraints -function _reformulate_logical_constraints(model::JuMP.Model) +function _reformulate_logical_constraints(model::Model) for (_, lcon) in _logical_constraints(model) _reformulate_logical_constraint(model, lcon.constraint.func, lcon.constraint.set) end end # individual logical constraints -function _reformulate_logical_constraint(model::JuMP.Model, func, set::Union{_MOIAtMost, _MOIAtLeast, _MOIExactly}) +function _reformulate_logical_constraint(model::Model, func, set::Union{_MOIAtMost, _MOIAtLeast, _MOIExactly}) return _reformulate_selector(model, func, set) end -function _reformulate_logical_constraint(model::JuMP.Model, func, set::IsTrue) +function _reformulate_logical_constraint(model::Model, func, set::IsTrue) return _reformulate_proposition(model, func) end diff --git a/src/variables.jl b/src/variables.jl index 922e2cb..954e539 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -2,7 +2,7 @@ # LOGICAL VARIABLES ################################################################################ """ - JuMP.build_variable(_error::Function, info::JuMP.VariableInfo, + JuMP.build_variable(_error::Function, info::VariableInfo, ::Type{LogicalVariable})::LogicalVariable Extend `JuMP.build_variable` to work with logical variables. This in @@ -11,7 +11,7 @@ combination with `JuMP.add_variable` enables the use of """ function JuMP.build_variable( _error::Function, - info::JuMP.VariableInfo, + info::VariableInfo, tag::Type{LogicalVariable}; kwargs... ) @@ -36,14 +36,14 @@ function JuMP.build_variable( end """ - JuMP.add_variable(model::JuMP.Model, v::LogicalVariable, + JuMP.add_variable(model::Model, v::LogicalVariable, name::String = "")::LogicalVariableRef Extend `JuMP.add_variable` for [`LogicalVariable`](@ref)s. This helps enable `@variable(model, [var_expr], Logical)`. """ function JuMP.add_variable( - model::JuMP.Model, + model::Model, v::LogicalVariable, name::String = "" ) @@ -61,8 +61,8 @@ Base.length(v::LogicalVariableRef) = 1 function Base.:(==)(v::LogicalVariableRef, w::LogicalVariableRef) return v.model === w.model && v.index == w.index end -function Base.getindex(map::JuMP.ReferenceMap, vref::LogicalVariableRef) - return LogicalVariableRef(map.model, JuMP.index(vref)) +function Base.getindex(map::ReferenceMap, vref::LogicalVariableRef) + return LogicalVariableRef(map.model, index(vref)) end # JuMP extensions @@ -89,12 +89,12 @@ Return `true` if `v` and `w` refer to the same logical variable in the same JuMP.isequal_canonical(v::LogicalVariableRef, w::LogicalVariableRef) = v == w """ - JuMP.is_valid(model::JuMP.Model, vref::LogicalVariableRef) + JuMP.is_valid(model::Model, vref::LogicalVariableRef) Return `true` if `vref` refers to a valid logical variable in `GDP model`. """ -function JuMP.is_valid(model::JuMP.Model, vref::LogicalVariableRef) - return model === JuMP.owner_model(vref) +function JuMP.is_valid(model::Model, vref::LogicalVariableRef) + return model === owner_model(vref) end """ @@ -103,8 +103,8 @@ end Get a logical variable's name attribute. """ function JuMP.name(vref::LogicalVariableRef) - data = gdp_data(JuMP.owner_model(vref)) - return data.logical_variables[JuMP.index(vref)].name + data = gdp_data(owner_model(vref)) + return data.logical_variables[index(vref)].name end """ @@ -113,9 +113,9 @@ end Set a logical variable's name attribute. """ function JuMP.set_name(vref::LogicalVariableRef, name::String) - model = JuMP.owner_model(vref) + model = owner_model(vref) data = gdp_data(model) - data.logical_variables[JuMP.index(vref)].name = name + data.logical_variables[index(vref)].name = name _set_ready_to_optimize(model, false) return end @@ -126,8 +126,8 @@ end Return the start value of the logical variable `vref`. """ function JuMP.start_value(vref::LogicalVariableRef) - data = gdp_data(JuMP.owner_model(vref)) - return data.logical_variables[JuMP.index(vref)].variable.start_value + data = gdp_data(owner_model(vref)) + return data.logical_variables[index(vref)].variable.start_value end """ @@ -141,11 +141,11 @@ function JuMP.set_start_value( vref::LogicalVariableRef, value::Union{Nothing, Bool} ) - model = JuMP.owner_model(vref) + model = owner_model(vref) data = gdp_data(model) - var = data.logical_variables[JuMP.index(vref)].variable + var = data.logical_variables[index(vref)].variable new_var = LogicalVariable(var.fix_value, value) - data.logical_variables[JuMP.index(vref)].variable = new_var + data.logical_variables[index(vref)].variable = new_var _set_ready_to_optimize(model, false) return end @@ -157,8 +157,8 @@ Return `true` if `vref` is a fixed variable. If fix_value. """ function JuMP.is_fixed(vref::LogicalVariableRef) - data = gdp_data(JuMP.owner_model(vref)) - return !isnothing(data.logical_variables[JuMP.index(vref)].variable.fix_value) + data = gdp_data(owner_model(vref)) + return !isnothing(data.logical_variables[index(vref)].variable.fix_value) end """ @@ -167,8 +167,8 @@ end Return the value to which a logical variable is fixed. """ function JuMP.fix_value(vref::LogicalVariableRef) - data = gdp_data(JuMP.owner_model(vref)) - return data.logical_variables[JuMP.index(vref)].variable.fix_value + data = gdp_data(owner_model(vref)) + return data.logical_variables[index(vref)].variable.fix_value end """ @@ -179,11 +179,11 @@ constraint if one exists, otherwise create a new one. """ function JuMP.fix(vref::LogicalVariableRef, value::Bool) - model = JuMP.owner_model(vref) + model = owner_model(vref) data = gdp_data(model) - var = data.logical_variables[JuMP.index(vref)].variable + var = data.logical_variables[index(vref)].variable new_var = LogicalVariable(value, var.start_value) - data.logical_variables[JuMP.index(vref)].variable = new_var + data.logical_variables[index(vref)].variable = new_var _set_ready_to_optimize(model, false) return end @@ -194,42 +194,42 @@ end Delete the fixed value of a logical variable. """ function JuMP.unfix(vref::LogicalVariableRef) - model = JuMP.owner_model(vref) + model = owner_model(vref) data = gdp_data(model) - var = data.logical_variables[JuMP.index(vref)].variable + var = data.logical_variables[index(vref)].variable new_var = LogicalVariable(nothing, var.start_value) - data.logical_variables[JuMP.index(vref)].variable = new_var + data.logical_variables[index(vref)].variable = new_var _set_ready_to_optimize(model, false) return end """ - JuMP.delete(model::JuMP.Model, vref::LogicalVariableRef) + JuMP.delete(model::Model, vref::LogicalVariableRef) Delete the logical variable associated with `vref` from the `GDP model`. """ -function JuMP.delete(model::JuMP.Model, vref::LogicalVariableRef) - @assert JuMP.is_valid(model, vref) "Variable does not belong to model." - vidx = JuMP.index(vref) +function JuMP.delete(model::Model, vref::LogicalVariableRef) + @assert is_valid(model, vref) "Variable does not belong to model." + vidx = index(vref) dict = _logical_variables(model) #delete any disjunct constraints associated with the logical variables in the disjunction if haskey(_indicator_to_constraints(model), vref) crefs = _indicator_to_constraints(model)[vref] - JuMP.delete.(model, crefs) + delete.(model, crefs) delete!(_indicator_to_constraints(model), vref) end #delete any disjunctions that have the logical variable for (didx, ddata) in _disjunctions(model) if vref in ddata.constraint.indicators setdiff!(ddata.constraint.indicators, [vref]) - JuMP.delete(model, DisjunctionRef(model, didx)) + delete(model, DisjunctionRef(model, didx)) end end #delete any logical constraints involving the logical variables for (cidx, cdata) in _logical_constraints(model) lvars = _get_constraint_variables(model, cdata.constraint) if vref in lvars - JuMP.delete(model, LogicalConstraintRef(model, cidx)) + delete(model, LogicalConstraintRef(model, cidx)) end end #delete the logical variable @@ -243,26 +243,26 @@ end ################################################################################ # VARIABLE INTERROGATION ################################################################################ -function _query_variable_bounds(model::JuMP.Model, method::Union{Hull, BigM}) - for var in JuMP.all_variables(model) +function _query_variable_bounds(model::Model, method::Union{Hull, BigM}) + for var in all_variables(model) method.variable_bounds[var] = _update_variable_bounds(var, method) end end -function _get_disjunction_variables(model::JuMP.Model, disj::Disjunction) - vars = Set{JuMP.VariableRef}() +function _get_disjunction_variables(model::Model, disj::Disjunction) + vars = Set{VariableRef}() for lvref in disj.indicators !haskey(_indicator_to_constraints(model), lvref) && continue #skip if disjunct is empty for cref in _indicator_to_constraints(model)[lvref] - con = JuMP.constraint_object(cref) + con = constraint_object(cref) _interrogate_variables(v -> push!(vars, v), con) end end return vars end -function _get_constraint_variables(model::JuMP.Model, con::Union{JuMP.ScalarConstraint, JuMP.VectorConstraint}) - vars = Set{Union{JuMP.VariableRef, LogicalVariableRef}}() +function _get_constraint_variables(model::Model, con::Union{ScalarConstraint, VectorConstraint}) + vars = Set{Union{VariableRef, LogicalVariableRef}}() _interrogate_variables(v -> push!(vars, v), con.func) return vars end @@ -273,13 +273,13 @@ function _interrogate_variables(interrogator::Function, c::Number) end # VariableRef/LogicalVariableRef -function _interrogate_variables(interrogator::Function, var::Union{JuMP.VariableRef, LogicalVariableRef}) +function _interrogate_variables(interrogator::Function, var::Union{VariableRef, LogicalVariableRef}) interrogator(var) return end # AffExpr -function _interrogate_variables(interrogator::Function, aff::JuMP.GenericAffExpr) +function _interrogate_variables(interrogator::Function, aff::GenericAffExpr) for (var, _) in aff.terms interrogator(var) end @@ -287,7 +287,7 @@ function _interrogate_variables(interrogator::Function, aff::JuMP.GenericAffExpr end # QuadExpr -function _interrogate_variables(interrogator::Function, quad::JuMP.QuadExpr) +function _interrogate_variables(interrogator::Function, quad::QuadExpr) for (pair, _) in quad.terms interrogator(pair.a) interrogator(pair.b) @@ -296,8 +296,8 @@ function _interrogate_variables(interrogator::Function, quad::JuMP.QuadExpr) return end -# NonlinearExpr and _LogicalExpr (T <: Union{JuMP.VariableRef, LogicalVariableRef}) -function _interrogate_variables(interrogator::Function, nlp::JuMP.GenericNonlinearExpr{T}) where {T} +# NonlinearExpr and _LogicalExpr (T <: Union{VariableRef, LogicalVariableRef}) +function _interrogate_variables(interrogator::Function, nlp::GenericNonlinearExpr{T}) where {T} for arg in nlp.args _interrogate_variables(interrogator, arg) end @@ -307,7 +307,7 @@ function _interrogate_variables(interrogator::Function, nlp::JuMP.GenericNonline end # Constraint -function _interrogate_variables(interrogator::Function, con::Union{JuMP.ScalarConstraint, JuMP.VectorConstraint}) +function _interrogate_variables(interrogator::Function, con::Union{ScalarConstraint, VectorConstraint}) _interrogate_variables(interrogator, con.func) end @@ -325,7 +325,7 @@ end # Nested disjunction function _interrogate_variables(interrogator::Function, disj::Disjunction) - model = JuMP.owner_model(disj.indicators[1]) + model = owner_model(disj.indicators[1]) dvars = _get_disjunction_variables(model, disj) _interrogate_variables(interrogator, dvars) return