Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
15803de
Add InfiniteOpt as an extension
pulsipher Mar 14, 2024
ebf603c
Update tests
pulsipher Mar 14, 2024
06a0b4f
add pkg
pulsipher Mar 14, 2024
3786845
fix extension versioning
pulsipher Mar 14, 2024
1886c29
Merge branch 'master' into infiniteopt_ext
pulsipher Oct 4, 2024
19f620d
bug fix
pulsipher Oct 7, 2024
c803d51
Merge remote-tracking branch 'upstream/infiniteopt_ext' into infiniteext
dnguyen227 Nov 5, 2025
68b1bb9
merged with main WIP
dnguyen227 Nov 10, 2025
b9fbc36
wip
dnguyen227 Nov 12, 2025
0caadbf
hull and bigm working version
dnguyen227 Dec 2, 2025
afec911
psplit working
dnguyen227 Dec 11, 2025
ad6423f
Tests added.
dnguyen227 Dec 15, 2025
c7f5b6a
julia version
dnguyen227 Dec 16, 2025
6e9b625
version change
dnguyen227 Dec 16, 2025
ef789bf
update CI
dnguyen227 Dec 16, 2025
36e3f77
added docstrings
dnguyen227 Dec 17, 2025
6f701ad
docstring for create_blank_variable
dnguyen227 Dec 17, 2025
5f2e4b7
version revert, dispatch on VariableProperties to create free variables
dnguyen227 Dec 21, 2025
beb5045
CI.yml change
dnguyen227 Dec 21, 2025
a0babfc
removed InfiniteOpt frolm targets (testing)
dnguyen227 Dec 21, 2025
b3c87af
CI to 1.9 and 1.6
dnguyen227 Dec 21, 2025
92e2407
docstring update, renamed all_variables to collect_all_vars (avoid Ju…
dnguyen227 Dec 22, 2025
bfcd3a7
additional tests, versoin change
dnguyen227 Dec 22, 2025
e301188
additional tests
dnguyen227 Dec 22, 2025
d62c7c6
Update runtests.jl
dnguyen227 Dec 22, 2025
9314bd7
Uncomment includes and update package management
dnguyen227 Dec 22, 2025
a99d183
additional tests
dnguyen227 Dec 22, 2025
950ee3d
.
dnguyen227 Dec 22, 2025
ecc322c
.
dnguyen227 Dec 22, 2025
9bb4249
simplify runtest.jl
dnguyen227 Dec 28, 2025
3df02ed
edited dependences, refactored InfiniteDisjunctiveProgramming.jl
dnguyen227 Jan 3, 2026
cbcb936
80 character edit
dnguyen227 Jan 4, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
version:
- '1.6'
- '1.10'
os:
- ubuntu-latest
- macOS-latest
Expand Down
11 changes: 9 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ version = "0.5.0"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"

[weakdeps]
InfiniteOpt = "20393b10-9daf-11e9-18c9-8db751c92c57"

[extensions]
InfiniteDisjunctiveProgramming = "InfiniteOpt"

[compat]
Aqua = "0.8"
JuMP = "1.18"
Reexport = "1"
julia = "1.6"
julia = "1.10"
Juniper = "0.9.3"
Ipopt = "1.9.0"
InfiniteOpt = "0.6"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Expand All @@ -23,4 +30,4 @@ Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
Juniper = "2ddba703-00a4-53a7-87a5-e8b9971dde84"

[targets]
test = ["Aqua", "HiGHS", "Test", "Juniper", "Ipopt"]
test = ["Aqua", "HiGHS", "Test", "Juniper", "Ipopt","InfiniteOpt"]
191 changes: 191 additions & 0 deletions ext/InfiniteDisjunctiveProgramming.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
module InfiniteDisjunctiveProgramming

import JuMP.MOI as _MOI
import InfiniteOpt, JuMP
import DisjunctiveProgramming as DP

################################################################################
# MODEL
################################################################################
function DP.InfiniteGDPModel(args...; kwargs...)
return DP.GDPModel{
InfiniteOpt.InfiniteModel,
InfiniteOpt.GeneralVariableRef,
InfiniteOpt.InfOptConstraintRef
}(args...; kwargs...)
end

function DP.collect_all_vars(model::InfiniteOpt.InfiniteModel)
vars = JuMP.all_variables(model)
derivs = InfiniteOpt.all_derivatives(model)
return append!(vars, derivs)
end

################################################################################
# VARIABLES
################################################################################
DP.InfiniteLogical(prefs...) = DP.Logical(InfiniteOpt.Infinite(prefs...))

_is_parameter(vref::InfiniteOpt.GeneralVariableRef) =
_is_parameter(InfiniteOpt.dispatch_variable_ref(vref))
_is_parameter(::InfiniteOpt.DependentParameterRef) = true
_is_parameter(::InfiniteOpt.IndependentParameterRef) = true
_is_parameter(::InfiniteOpt.ParameterFunctionRef) = true
_is_parameter(::InfiniteOpt.FiniteParameterRef) = true
_is_parameter(::Any) = false

function DP.requires_disaggregation(vref::InfiniteOpt.GeneralVariableRef)
return !_is_parameter(vref)
end

function DP.VariableProperties(vref::InfiniteOpt.GeneralVariableRef)
info = DP.get_variable_info(vref)
name = JuMP.name(vref)
set = nothing
prefs = InfiniteOpt.parameter_refs(vref)
var_type = !isempty(prefs) ? InfiniteOpt.Infinite(prefs...) : nothing
return DP.VariableProperties(info, name, set, var_type)
end

# Extract parameter refs from expression and return VariableProperties with Infinite type
function DP.VariableProperties(
expr::JuMP.GenericAffExpr{C, InfiniteOpt.GeneralVariableRef}
) where C
prefs = InfiniteOpt.parameter_refs(expr)
info = DP._free_variable_info()
var_type = !isempty(prefs) ? InfiniteOpt.Infinite(prefs...) : nothing
return DP.VariableProperties(info, "", nothing, var_type)
end

function DP.VariableProperties(
expr::JuMP.GenericQuadExpr{C, InfiniteOpt.GeneralVariableRef}
) where C
prefs = InfiniteOpt.parameter_refs(expr)
info = DP._free_variable_info()
var_type = !isempty(prefs) ? InfiniteOpt.Infinite(prefs...) : nothing
return DP.VariableProperties(info, "", nothing, var_type)
end

function DP.VariableProperties(
exprs::Vector{<:Union{
InfiniteOpt.GeneralVariableRef,
JuMP.GenericAffExpr{<:Any, InfiniteOpt.GeneralVariableRef},
JuMP.GenericQuadExpr{<:Any, InfiniteOpt.GeneralVariableRef}
}}
)
all_prefs = Set{InfiniteOpt.GeneralVariableRef}()
for expr in exprs
for pref in InfiniteOpt.parameter_refs(expr)
push!(all_prefs, pref)
end
end
prefs = Tuple(all_prefs)
info = DP._free_variable_info()
var_type = !isempty(prefs) ? InfiniteOpt.Infinite(prefs...) : nothing
return DP.VariableProperties(info, "", nothing, var_type)
end

function JuMP.value(vref::DP.LogicalVariableRef{InfiniteOpt.InfiniteModel})
return JuMP.value(DP.binary_variable(vref)) .>= 0.5
end

################################################################################
# CONSTRAINTS
################################################################################
function JuMP.add_constraint(
model::InfiniteOpt.InfiniteModel,
c::JuMP.VectorConstraint{F, S},
name::String = ""
) where {F, S <: DP.AbstractCardinalitySet}
return DP._add_cardinality_constraint(model, c, name)
end

function JuMP.add_constraint(
model::M,
c::JuMP.ScalarConstraint{DP._LogicalExpr{M}, S},
name::String = ""
) where {S, M <: InfiniteOpt.InfiniteModel}
return DP._add_logical_constraint(model, c, name)
end

function JuMP.add_constraint(
model::M,
c::JuMP.ScalarConstraint{DP.LogicalVariableRef{M}, S},
name::String = ""
) where {M <: InfiniteOpt.InfiniteModel, S}
error("Cannot define constraint on single logical variable, use `fix` instead.")
end

function JuMP.add_constraint(
model::M,
c::JuMP.ScalarConstraint{
JuMP.GenericAffExpr{C, DP.LogicalVariableRef{M}}, S
},
name::String = ""
) where {M <: InfiniteOpt.InfiniteModel, S, C}
error("Cannot add, subtract, or multiply with logical variables.")
end

function JuMP.add_constraint(
model::M,
c::JuMP.ScalarConstraint{
JuMP.GenericQuadExpr{C, DP.LogicalVariableRef{M}}, S
},
name::String = ""
) where {M <: InfiniteOpt.InfiniteModel, S, C}
error("Cannot add, subtract, or multiply with logical variables.")
end

################################################################################
# METHODS
################################################################################
function DP.get_constant(
expr::JuMP.GenericAffExpr{T, InfiniteOpt.GeneralVariableRef}
) where {T}
constant = JuMP.constant(expr)
param_expr = zero(typeof(expr))
for (var, coeff) in expr.terms
if _is_parameter(var)
JuMP.add_to_expression!(param_expr, coeff, var)
end
end
return constant + param_expr
end

function DP.disaggregate_expression(
model::M,
aff::JuMP.GenericAffExpr,
bvref::Union{JuMP.AbstractVariableRef, JuMP.GenericAffExpr},
method::DP._Hull
) where {M <: InfiniteOpt.InfiniteModel}
terms = Any[aff.constant * bvref]
for (vref, coeff) in aff.terms
if JuMP.is_binary(vref)
push!(terms, coeff * vref)
elseif vref isa InfiniteOpt.GeneralVariableRef && _is_parameter(vref)
push!(terms, coeff * vref * bvref)
elseif !haskey(method.disjunct_variables, (vref, bvref))
push!(terms, coeff * vref)
else
dvref = method.disjunct_variables[vref, bvref]
push!(terms, coeff * dvref)
end
end
return JuMP.@expression(model, sum(terms))
end

################################################################################
# ERROR MESSAGES
################################################################################
function DP.reformulate_model(::InfiniteOpt.InfiniteModel, ::DP.MBM)
error("The `MBM` method is not supported for `InfiniteModel`." *
"Please use `BigM`, `Hull`, `Indicator`, or `PSplit` instead.")
end

function DP.reformulate_model(::InfiniteOpt.InfiniteModel, ::DP.cutting_planes)
error("The `cutting_planes` method is not supported for `InfiniteModel`." *
"Please use `BigM`, `Hull`, `Indicator`, or `PSplit` instead.")
end

end

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("hull.jl")
include("mbm.jl")
include("indicator.jl")
include("print.jl")
include("extension_api.jl")
include("utilities.jl")
include("psplit.jl")

Expand Down
10 changes: 5 additions & 5 deletions src/cuttingplanes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ function reformulate_model(
rBM, rBM_ref_map, _ = copy_gdp_model(model)
reformulate_model(rBM, BigM(method.M_value))
reformulate_model(SEP, Hull())
main_to_SEP_map = Dict(v => sep_ref_map[v] for v in all_variables(model))
main_to_rBM_map = Dict(v => rBM_ref_map[v] for v in all_variables(model))
main_to_SEP_map = Dict(v => sep_ref_map[v] for v in collect_all_vars(model))
main_to_rBM_map = Dict(v => rBM_ref_map[v] for v in collect_all_vars(model))
JuMP.set_optimizer(SEP, method.optimizer)
JuMP.set_optimizer(rBM, method.optimizer)
JuMP.set_silent(rBM)
Expand Down Expand Up @@ -55,7 +55,7 @@ function _solve_rBM(
) where {M <: JuMP.AbstractModel}
T = JuMP.value_type(M)
optimize!(rBM, ignore_optimize_hook = true)
rBM_vars = JuMP.all_variables(rBM)
rBM_vars = collect_all_vars(rBM)

#Solution to be passed to SEP model.
sol = Dict{JuMP.AbstractVariableRef,T}(var => zero(T) for var in rBM_vars)
Expand All @@ -73,7 +73,7 @@ function _solve_SEP(
rBM_to_SEP_map::Dict{<:JuMP.AbstractVariableRef,<:JuMP.AbstractVariableRef}
) where {M <: JuMP.AbstractModel, T <: Number}

SEP_vars = [rBM_to_SEP_map[rBM_var] for rBM_var in JuMP.all_variables(rBM)]
SEP_vars = [rBM_to_SEP_map[rBM_var] for rBM_var in collect_all_vars(rBM)]

#Modified objective function for SEP.
obj_expr = sum(
Expand All @@ -98,7 +98,7 @@ function _cutting_planes(
rBM_sol::Dict{<:JuMP.AbstractVariableRef,T},
SEP_sol::Dict{<:JuMP.AbstractVariableRef,T},
) where {M <: JuMP.AbstractModel, T <: Number}
main_vars = JuMP.all_variables(model)
main_vars = collect_all_vars(model)

#Cutting plane generation
ξ_sep = Dict{JuMP.AbstractVariableRef,T}(var =>zero(T) for var in main_vars)
Expand Down
39 changes: 26 additions & 13 deletions src/datatypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ struct PSplit{V <: JuMP.AbstractVariableRef} <: AbstractReformulationMethod
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))
variables = collect_all_vars(model)
n_vars = length(variables)

n_parts = min(n_parts, n_vars)
Expand Down Expand Up @@ -620,19 +620,32 @@ mutable struct VariableProperties{L, U, F, S, SET, T}
end

function VariableProperties(vref::JuMP.GenericVariableRef{T}) where T
info = JuMP.VariableInfo(
JuMP.has_lower_bound(vref),
JuMP.has_lower_bound(vref) ? JuMP.lower_bound(vref) : zero(T),
JuMP.has_upper_bound(vref),
JuMP.has_upper_bound(vref) ? JuMP.upper_bound(vref) : zero(T),
JuMP.is_fixed(vref),
JuMP.is_fixed(vref) ? JuMP.fix_value(vref) : zero(T),
!isnothing(JuMP.start_value(vref)),
JuMP.start_value(vref),
JuMP.is_binary(vref),
JuMP.is_integer(vref)
)
info = get_variable_info(vref)
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

function VariableProperties(vref::JuMP.AbstractVariableRef)
info = get_variable_info(vref)
name = JuMP.name(vref)
return VariableProperties(info, name, nothing, nothing)
end

"""
VariableProperties(expr)::VariableProperties

Creates a `VariableProperties` object with blank variable info (no bounds, not fixed,
not binary/integer) from an expression. The `expr` argument is provided for
extensions to infer additional properties (e.g., parameter dependencies in InfiniteOpt).

## Arguments
- `expr`: Expression for extensions to extract metadata from

## Returns
A `VariableProperties` object with blank info.
"""
function VariableProperties(expr)
info = _free_variable_info()
return VariableProperties(info, "", nothing, nothing)
end
40 changes: 40 additions & 0 deletions src/extension_api.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""
InfiniteGDPModel(args...; kwargs...)

Creates an `InfiniteOpt.InfiniteModel` that is compatible with the
capabiltiies provided by DisjunctiveProgramming.jl. This requires
that InfiniteOpt be imported first.

**Example**
```julia
julia> using DisjunctiveProgramming, InfiniteOpt

julia> InfiniteGDPModel()

```
"""
function InfiniteGDPModel end

"""
InfiniteLogical(prefs...)

Allows users to create infinite logical variables. This is a tag
for the `@variable` macro that is a combination of `InfiniteOpt.Infinite`
and `DisjunctiveProgramming.Logical`. This requires that InfiniteOpt be
first imported.

**Example**
```julia
julia> using DisjunctiveProgramming, InfiniteOpt

julia> model = InfiniteGDPModel();

julia> @infinite_parameter(model, t in [0, 1]);

julia> @infinite_parameter(model, x[1:2] in [-1, 1]);

julia> @variable(model, Y, InfiniteLogical(t, x)) # creates Y(t, x) in {True, False}
Y(t, x)
```
"""
function InfiniteLogical end
Loading
Loading