Skip to content

Support Multiple Big-M reformulation. #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ The following reformulation methods are currently supported:

3. [Indicator](https://jump.dev/JuMP.jl/stable/manual/constraints/#Indicator-constraints): This method reformulates each disjunct constraint into an indicator constraint with the Boolean reformulation counterpart of the Logical variable used to define the disjunct constraint.

4. [MBM](https://doi.org/10.1016/j.compchemeng.2015.02.013): The multiple big-m method creates multiple M values for each disjunct constraint. The 'MBM' struct is created with the following required argument:

- `optimizer`: Optimizer to use when solving subproblems to determine M values. This is a required value.

## Release Notes

Prior to `v0.4.0`, the package did not leverage the JuMP extension capabilities and was not as robust. For these earlier releases, refer to [Perez, Joshi, and Grossmann, 2023](https://arxiv.org/abs/2304.10492v1) and the following [JuliaCon 2022 Talk](https://www.youtube.com/watch?v=AMIrgTTfUkI).
Expand Down
2 changes: 1 addition & 1 deletion src/DisjunctiveProgramming.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Reexport.@reexport using JuMP

# Use Meta for metaprogramming
using Base.Meta

# Create aliases
import JuMP.MOI as _MOI
import JuMP.MOIU.CleverDicts as _MOIUC
Expand All @@ -22,6 +21,7 @@ include("macros.jl")
include("reformulate.jl")
include("bigm.jl")
include("hull.jl")
include("mbm.jl")
include("indicator.jl")
include("print.jl")

Expand Down
26 changes: 26 additions & 0 deletions src/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ _vec_to_scalar_set(::_MOIExactly) = _MOI.EqualTo
_vec_to_scalar_set(::_MOIAtLeast) = _MOI.GreaterThan
_vec_to_scalar_set(::_MOIAtMost) = _MOI.LessThan

#helper function to create variables
# Helper function to copy variable properties from an existing variable
function _copy_variable(
target_model::JuMP.AbstractModel,
original_var::JuMP.AbstractVariableRef,
)
# Create new variable
new_var = JuMP.@variable(target_model, base_name = JuMP.name(original_var))

# Copy all properties from original variable
JuMP.has_lower_bound(original_var) && JuMP.set_lower_bound(new_var, JuMP.lower_bound(original_var))
JuMP.has_upper_bound(original_var) && JuMP.set_upper_bound(new_var, JuMP.upper_bound(original_var))
JuMP.has_start_value(original_var) && JuMP.set_start_value(new_var, JuMP.start_value(original_var))
JuMP.is_integer(original_var) && JuMP.set_integer(new_var)
JuMP.is_binary(original_var) && JuMP.set_binary(new_var)

# Handle fixed values with force=true (as in original MBM code)
if JuMP.is_fixed(original_var)
JuMP.fix(new_var, JuMP.fix_value(original_var); force=true)
end

return new_var
end
Comment on lines +28 to +48
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new function differs from make_disaggregated_variable(). The main difference is that instead of passing the variable attributes, I instead pass the variable itself and check for the attributes within this new function.

Why?
The alternative to this would be to have all attributes as possible inputs and default them to nothing, but then the function would still need to check if the value is 'nothing'.
For example:

function _copy_variable(
    target_model::JuMP.AbstractModel,
    original_var::JuMP.AbstractVariableRef;
    base_name::Union{String, Nothing} = nothing,
    lower_bound::Union{Float64, Nothing} = nothing,
    upper_bound::Union{Float64, Nothing} = nothing,
    start_value::Union{Float64, Nothing} = nothing,
    is_integer::Union{Bool, Nothing} = nothing,
    is_binary::Union{Bool, Nothing} = nothing,
    fixed_value::Union{Float64, Nothing} = nothing
    )
    
    # Create new variable with base name
    var_name = base_name !== nothing ? base_name : JuMP.name(original_var)
    new_var = JuMP.@variable(target_model, base_name = var_name)

    # Set attributes if provided
    lower_bound !== nothing && JuMP.set_lower_bound(new_var, lower_bound)
    upper_bound !== nothing && JuMP.set_upper_bound(new_var, upper_bound)
    start_value !== nothing && JuMP.set_start_value(new_var, start_value)
    is_integer !== nothing && is_integer && JuMP.set_integer(new_var)
    is_binary !== nothing && is_binary && JuMP.set_binary(new_var)
    
    # Handle fixed value with force=true
    if fixed_value !== nothing
        JuMP.fix(new_var, fixed_value; force=true)
    end

    return new_var
end

This involved two checks, once before passing to the function, then one inside the function. So I thought just passing the variable to limit it to one check was better.

Let me know if there is a better way to do this, especially if I can make more akin to make_disaggregated_variable.




################################################################################
# BOILERPLATE EXTENSION METHODS
################################################################################
Expand Down
19 changes: 19 additions & 0 deletions src/datatypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,25 @@ struct BigM{T} <: AbstractReformulationMethod
end
end

"""
MBM{O} <: AbstractReformulationMethod

A type for using the multiple big-M reformulation approach for disjunctive constraints.

**Fields**
- 'optimizer::O': Optimizer to use when solving mini-models (required).
"""
struct MBM{O} <: AbstractReformulationMethod
optimizer::O

# Constructor with optimizer (required)
function MBM(optimizer::O) where {O}
new{O}(optimizer)
end
end



"""
Hull{T} <: AbstractReformulationMethod

Expand Down
Loading
Loading