Skip to content

[DO NOT MERGE YET] Add InfiniteOpt as an Extension #114

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 6 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
10 changes: 9 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ 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"
Expand All @@ -16,7 +22,9 @@ julia = "1.6"
[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
InfiniteOpt = "20393b10-9daf-11e9-18c9-8db751c92c57"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

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

import InfiniteOpt, JuMP
import DisjunctiveProgramming as DP

# Extend the public API methods
function DP.InfiniteGDPModel(args...; kwargs...)
return DP.GDPModel{
InfiniteOpt.InfiniteModel,
InfiniteOpt.GeneralVariableRef,
InfiniteOpt.InfOptConstraintRef
}(args...; kwargs...)
end
DP.InfiniteLogical(prefs...) = DP.Logical(InfiniteOpt.Infinite(prefs...))

# Make necessary extensions for Hull method
function DP.requires_disaggregation(vref::InfiniteOpt.GeneralVariableRef)
if vref.index_type <: InfiniteOpt.InfOptParameter
return false
else
return true
end
end
function DP.make_disaggregated_variable(
model::InfiniteOpt.InfiniteModel,
vref::InfiniteOpt.GeneralVariableRef,
name,
lb,
ub
)
prefs = InfiniteOpt.parameter_refs(vref)
if !isempty(prefs)
return JuMP.@variable(model, base_name = name, lower_bound = lb, upper_bound = ub,
variable_type = InfiniteOpt.Infinite(prefs...))
else
return JuMP.@variable(model, base_name = name, lower_bound = lb, upper_bound = ub)
end
end

# Add necessary @constraint extensions
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

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

end
1 change: 1 addition & 0 deletions src/DisjunctiveProgramming.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ include("bigm.jl")
include("hull.jl")
include("indicator.jl")
include("print.jl")
include("extension_api.jl")

# Define additional stuff that should not be exported
const _EXCLUDE_SYMBOLS = [Symbol(@__MODULE__), :eval, :include]
Expand Down
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
5 changes: 3 additions & 2 deletions src/variables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,13 @@ function JuMP.add_variable(
lvref = LogicalVariableRef(model, idx)
_set_ready_to_optimize(model, false)
# add the associated binary variables
if isnothing(_get_variable(v).logical_complement)
extracted_var = _get_variable(v)
if isnothing(extracted_var.logical_complement)
bvref = _make_binary_variable(model, v, name)
_add_logical_info(bvref, v)
jump_expr = bvref
else
jump_expr = 1 - binary_variable(v.logical_complement)
jump_expr = 1 - binary_variable(extracted_var.logical_complement)
end
_indicator_to_binary(model)[lvref] = jump_expr
return lvref
Expand Down
32 changes: 32 additions & 0 deletions test/extensions/InfiniteDisjunctiveProgramming.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using InfiniteOpt

function test_infiniteopt_extension()
# Initialize the model
model = InfiniteGDPModel(HiGHS.Optimizer)

# Create the infinite variables
I = 1:4
@infinite_parameter(model, t ∈ [0, 1], num_supports = 100)
@variable(model, 0 <= g[I] <= 10, Infinite(t))

# Add the disjunctions and their indicator variables
@variable(model, G[I, 1:2], InfiniteLogical(t))
@test all(isa.(@constraint(model, [i ∈ I, j ∈ 1:2], 0 <= g[i], Disjunct(G[i, 1])), DisjunctConstraintRef{InfiniteModel}))
@test all(isa.(@constraint(model, [i ∈ I, j ∈ 1:2], g[i] <= 0, Disjunct(G[i, 2])), DisjunctConstraintRef{InfiniteModel}))
@test all(isa.(@disjunction(model, [i ∈ I], G[i, :]), DisjunctionRef{InfiniteModel}))

# Add the logical propositions
@variable(model, W, InfiniteLogical(t))
@test @constraint(model, G[1, 1] ∨ G[2, 1] ∧ G[3, 1] == W := true) isa LogicalConstraintRef{InfiniteModel}
@constraint(model, 𝔼(binary_variable(W), t) >= 0.95)

# Reformulate and solve
@test optimize!(model, gdp_method = Hull()) isa Nothing

# check the results
@test all(value(W))
end

@testset "InfiniteDisjunctiveProgramming" begin
test_infiniteopt_extension()
end
8 changes: 7 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using Test
include("utilities.jl")

# RUN ALL THE TESTS
include("aqua.jl")
# include("aqua.jl") # temporary ignore until compat is finalized
include("model.jl")
include("jump.jl")
include("variables/query.jl")
Expand All @@ -20,3 +20,9 @@ include("constraints/fallback.jl")
include("constraints/disjunction.jl")
include("print.jl")
include("solve.jl")

if Base.VERSION >= v"1.9" # extensions require Julia v1.9+
import Pkg
Pkg.add(url = "https://github.com/infiniteopt/InfiniteOpt.jl", rev = "master")
include("extensions/InfiniteDisjunctiveProgramming.jl")
end
Loading