Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c222384
Initial commit adding psplit datatype for methods (similar to BigM an…
dnguyen227 Jul 3, 2025
a969de4
psplit.jl working for quad and affine expressions inside of constraints.
dnguyen227 Jul 3, 2025
fff06f2
Initial nonlinear dispatch for _build_partitioned_expression
dnguyen227 Jul 4, 2025
927019d
More dispatch of reformulate_disjunct_constraint
dnguyen227 Jul 8, 2025
8b1da77
Vector dispatch
dnguyen227 Jul 11, 2025
34cec19
Only NLE is WIP
dnguyen227 Jul 15, 2025
70c57a8
Nonlinear works for less than constraints
dnguyen227 Jul 15, 2025
0d9cc9e
GreaterThan nonlinear works
dnguyen227 Jul 30, 2025
8fd6bbf
Initial test file commit
dnguyen227 Aug 10, 2025
f62a213
Type change + Intial test commit
dnguyen227 Aug 12, 2025
2c286e1
Vector excluding NL works.
dnguyen227 Aug 22, 2025
d59c9ce
GT,LT, EQ work. nn for vectors works. (NL tested for these cases)
dnguyen227 Aug 22, 2025
5dbcde8
.
dnguyen227 Aug 22, 2025
39c71d7
Fixed Zeros.
dnguyen227 Aug 22, 2025
2816090
NLE not working with negated expressions.
dnguyen227 Aug 23, 2025
cf0915c
Additional tests.
dnguyen227 Aug 25, 2025
f32f05e
Added tests for reformulate_disjunct_constraint
dnguyen227 Aug 25, 2025
f56c2cb
WIP NLE. Nested detection is not working.
dnguyen227 Aug 26, 2025
3b37d01
Nonlinear works. Need to add error message.
dnguyen227 Aug 27, 2025
3807421
Tests made with 100% code coverage.
dnguyen227 Aug 28, 2025
5603964
Merge remote-tracking branch 'upstream/master' into p-split-aff-and-quad
dnguyen227 Sep 30, 2025
7ce16a3
p-split tests, generalized datatypes and mentions to 0, styling for b…
dnguyen227 Oct 2, 2025
9b5c823
solve.jl change
dnguyen227 Oct 2, 2025
2bdeeab
solve.jl change
dnguyen227 Oct 2, 2025
c97dfce
readme update
dnguyen227 Oct 3, 2025
065ce84
Update solve.jl
dnguyen227 Oct 3, 2025
be2b33a
update Ipopt type.
dnguyen227 Oct 3, 2025
5184784
Fixed datatypes
dnguyen227 Oct 8, 2025
8b6ef04
Initial commit to change to extended hull reformulation
dnguyen227 Oct 9, 2025
7431a3c
revision to fix hull
dnguyen227 Oct 10, 2025
1111cc7
extended hull p-split works with selective dissaggregation
dnguyen227 Oct 16, 2025
597dc4d
added tests
dnguyen227 Oct 17, 2025
0d4fd13
Merge branch 'p-split-extended-hull' into p-split-aff-and-quad
dnguyen227 Oct 17, 2025
9f801b5
fixed parameter typing for zero(T)
dnguyen227 Oct 17, 2025
870c1e9
removed old P-Split datatype, added test for nonlinera expression det…
dnguyen227 Oct 17, 2025
ad787b4
code coverage 100%
dnguyen227 Oct 17, 2025
575eb78
project.toml edit to compat section, stylistic edits
dnguyen227 Oct 19, 2025
5032348
revert hull aggregate_variables function
dnguyen227 Oct 20, 2025
ade26c9
Correct default_M field description formatting
dnguyen227 Oct 20, 2025
a0fb70c
Fix typo in documentation for MBM struct
dnguyen227 Oct 20, 2025
354d1c1
Fix requires_disaggregation function definition
dnguyen227 Oct 20, 2025
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
6 changes: 5 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ Aqua = "0.8"
JuMP = "1.18"
Reexport = "1"
julia = "1.6"
Juniper = "0.9.3"
Ipopt = "1.9.0"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
Juniper = "2ddba703-00a4-53a7-87a5-e8b9971dde84"

[targets]
test = ["Aqua", "HiGHS", "Test"]
test = ["Aqua", "HiGHS", "Test", "Juniper", "Ipopt"]
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ The following reformulation methods are currently supported:
- `optimizer`: Optimizer to use when solving subproblems to determine M values. This is a required value.
- `default_M`: Default big-M value to use if no big-M is specified for a logical variable (1e9).

5. [P-Split](https://arxiv.org/abs/2202.05198): This method reformulates each disjunct constraint into P constraints, each with a partitioned group defined by the user. This method requires that terms in the constraint be convex additively seperable with respect to each variable. The `PSplit` struct is created with the following required arguments:

- `partition`: Partition of the variables to be split. All variables must be in exactly one partition. (e.g., The variables `x[1:4]` can be partitioned into two groups ` partition = [[x[1], x[2]], [x[3], x[4]]]`)
- `PSplit(n_parts, model)`: Automatically partition all variables in the model into `n_parts` groups

All variables must be included in exactly one partition. For manual partitioning, ensure each variable appears in exactly one group. For automatic partitioning, variables are divided as evenly as possible among the specified number of partitions.


## 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 All @@ -192,7 +200,7 @@ using HiGHS
m = GDPModel(HiGHS.Optimizer)
@variable(m, 0 ≤ x[1:2] ≤ 20)
@variable(m, Y[1:2], Logical)
@constraint(m, [i = 1:2], [2,5][i] ≤ x[i] ≤ [6,9][i], Disjunct(Y[1]))
@constraint(m, [i = 1:2], [2,5][i] ≤ x[i] ≤ [6,9][i], Disjunct(Y[1]))
@constraint(m, [i = 1:2], [8,10][i] ≤ x[i] ≤ [11,15][i], Disjunct(Y[2]))
@disjunction(m, Y)
@objective(m, Max, sum(x))
Expand Down
1 change: 1 addition & 0 deletions src/DisjunctiveProgramming.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ include("mbm.jl")
include("indicator.jl")
include("print.jl")
include("utilities.jl")
include("psplit.jl")

# Define additional stuff that should not be exported
const _EXCLUDE_SYMBOLS = [Symbol(@__MODULE__), :eval, :include]
Expand Down
68 changes: 67 additions & 1 deletion src/datatypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,72 @@ mutable struct _Hull{V <: JuMP.AbstractVariableRef, T} <: AbstractReformulationM
end
end

"""
PSplit <: AbstractReformulationMethod

A type for using the P-split reformulation approach for disjunctive constraints.
This method partitions variables into groups and handles each group separately.

# Constructors
- `PSplit(partition::Vector{Vector{V}})`: Create a PSplit with the given
partition of variables
- `PSplit(n_parts::Int, model::JuMP.AbstractModel)`: Automatically partition
model variables into `n_parts` groups

# Fields
- `partition::Vector{Vector{V}}`: The partition of variables, where each inner
vector represents a group of variables that will be handled together
"""
struct PSplit{V <: JuMP.AbstractVariableRef} <: AbstractReformulationMethod
partition::Vector{Vector{V}}

function PSplit(partition::Vector{Vector{V}}) where
{V <: JuMP.AbstractVariableRef}
new{V}(partition)
end

function PSplit(n_parts::Int, model::JuMP.AbstractModel)
n_parts > 0 || error("Number of partitions must be
positive, got $n_parts")
variables = collect(JuMP.all_variables(model))
n_vars = length(variables)

n_parts = min(n_parts, n_vars)
n_parts > 0 || error("No variables found in the model")

base_size = n_vars ÷ n_parts
remaining = n_vars % n_parts

partition = Vector{Vector{eltype(variables)}}()
start_idx = 1

for i in 1:n_parts
part_size = i <= remaining ? base_size + 1 : base_size
end_idx = start_idx + part_size - 1
push!(partition, variables[start_idx:end_idx])
start_idx = end_idx + 1
end

return PSplit(partition)
end
end

# temp struct to store variable disaggregations (reset for each disjunction)
mutable struct _PSplit{V <: JuMP.AbstractVariableRef, M <: JuMP.AbstractModel, T} <: AbstractReformulationMethod
partition::Vector{Vector{V}}
sum_constraints::Dict{LogicalVariableRef{M}, Vector{<:AbstractConstraint}}
hull::_Hull{V, T}
function _PSplit(method::PSplit{V}, model::M) where
{V <: JuMP.AbstractVariableRef, M <: JuMP.AbstractModel}
T = JuMP.value_type(M)
new{V, M, T}(
method.partition,
Dict{LogicalVariableRef{M}, Vector{<:AbstractConstraint}}(),
_Hull(Hull(), Set{V}())
)
end
end

"""
Indicator <: AbstractReformulationMethod

Expand Down Expand Up @@ -539,4 +605,4 @@ function VariableProperties(vref::JuMP.GenericVariableRef{T}) where T
name = JuMP.name(vref)
set = JuMP.is_variable_in_set(vref) ? JuMP.moi_set(JuMP.constraint_object(JuMP.VariableInSetRef(vref))) : nothing
return VariableProperties(info, name, set, nothing)
end
end
6 changes: 3 additions & 3 deletions src/hull.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ function _disaggregate_variables(
end
end
function _disaggregate_variable(
model::JuMP.AbstractModel,
model::M,
lvref::LogicalVariableRef,
vref::JuMP.AbstractVariableRef,
method::_Hull
)
) where {M <: JuMP.AbstractModel}
#create disaggregated vref
lb, ub = variable_bound_info(vref)
T = JuMP.value_type(typeof(model))
T = JuMP.value_type(M)
info = JuMP.VariableInfo(
true, # has_lb = true
lb, # lower_bound = lb
Expand Down
Loading
Loading