Skip to content

Commit

Permalink
Merge pull request #84 from JuliaOpt/bl/JuMPv0.21
Browse files Browse the repository at this point in the history
Update to JuMP v0.21
  • Loading branch information
blegat authored Apr 4, 2020
2 parents 21c0245 + e113f58 commit 135b2e4
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 74 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ julia:
- 1.1
- 1.2
- 1.3
- 1.4
os:
- linux
after_success:
Expand All @@ -28,4 +29,4 @@ jobs:
- julia --project=docs/ -e 'using Pkg; Pkg.instantiate();
Pkg.develop(PackageSpec(path=pwd()))'
- julia --project=docs/ docs/make.jl
after_success: skip
after_success: skip
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"

[compat]
JuMP = "~0.20.0"
MathOptInterface = "~0.9.9"
JuMP = "0.21"
MathOptInterface = "0.9.12"
julia = "1"

[extras]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dual_model = dualize(model)
* The `DualOptimizer` that will pass the dual representation of the model to the solver of your choice.

```julia
model = Model(with_optimizer(() -> DualOptimizer(SOLVER.Optimizer(options...)))
model = Model(dual_optimizer(SOLVER.Optimizer))
```

## Common use cases
Expand Down
22 changes: 11 additions & 11 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Examples
# Examples

Here we discuss some useful examples of usage.

Expand Down Expand Up @@ -48,28 +48,28 @@ And you should get the model
\end{align}
```

Note that if you declare the model with an optimizer attached you will lose the optimizer during the dualization.
To dualize the model and attach the optimizer to the dual model you should do `dualize(dual_model; with_optimizer(...))`
Note that if you declare the model with an optimizer attached you will lose the optimizer during the dualization.
To dualize the model and attach the optimizer to the dual model you should do `dualize(dual_model, SolverName.Optimizer)`

```julia
using JuMP, Dualization, ECOS
model = Model(with_optimizer(ECOS.Optimizer))
model = Model(ECOS.Optimizer)
@variable(model, x)
@variable(model, y)
@variable(model, z)
@constraint(model, soccon, [x; y; z] in SecondOrderCone())
@constraint(model, eqcon, x == 1)
@objective(model, Min, y + z)

dual_model = dualize(model, with_optimizer(ECOS.Optimizer))
dual_model = dualize(model, ECOS.Optimizer)
```

## Naming the dual variables and dual constraints

You can provide prefixes for the name of the variables and the name of the constraints using the a `DualNames` variable.
Everytime you use the dualize function you can provide a `DualNames` as keyword argument. Consider the following example.

You want to dualize this JuMP problem and add a prefix to the name of each constraint to be more clear on what the variables
You want to dualize this JuMP problem and add a prefix to the name of each constraint to be more clear on what the variables
represent. For instance you want to put `"dual"` before the name of the constraint.

```julia
Expand All @@ -87,7 +87,7 @@ model = Model()
dual_model = dualize(model; dual_names = DualNames("dual", ""))
```

The dual_model will be registered as
The dual_model will be registered as

```math
\begin{align}
Expand All @@ -111,7 +111,7 @@ To solve the problem via its dual formulation can be done using the `DualOptimiz
using JuMP, Dualization, ECOS

# Solving a problem the standard way
model = Model(with_optimizer(ECOS.Optimizer))
model = Model(ECOS.Optimizer)
@variable(model, x)
@variable(model, y)
@variable(model, z)
Expand All @@ -120,16 +120,16 @@ model = Model(with_optimizer(ECOS.Optimizer))
@objective(model, Min, y + z)

# Solving a problem by providing its dual representation
model = Model(with_optimizer(DualOptimizer, ECOS.Optimizer()))
model = Model(dual_optimizer(ECOS.Optimizer))
@variable(model, x)
@variable(model, y)
@variable(model, z)
@constraint(model, soccon, [x; y; z] in SecondOrderCone())
@constraint(model, eqcon, x == 1)
@objective(model, Min, y + z)

# You can pass arguments to the solver by putting them as arguments inside the solver `Optimizer`
model = Model(with_optimizer(DualOptimizer, ECOS.Optimizer(maxit = 5)))
# You can pass arguments to the solver by attaching them to the solver constructor.
model = Model(dual_optimizer(optimizer_with_attributes(ECOS.Optimizer, "maxit" => 5)))
@variable(model, x)
@variable(model, y)
@variable(model, z)
Expand Down
46 changes: 24 additions & 22 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
export DualOptimizer
export DualOptimizer, dual_optimizer

dual_optimizer(optimizer_constructor) = () -> DualOptimizer(MOI.instantiate(optimizer_constructor))

# Supported Functions
const SF = Union{MOI.SingleVariable,
MOI.ScalarAffineFunction{Float64},
MOI.VectorOfVariables,
const SF = Union{MOI.SingleVariable,
MOI.ScalarAffineFunction{Float64},
MOI.VectorOfVariables,
MOI.VectorAffineFunction{Float64}}

# Supported Sets
const SS = Union{MOI.EqualTo{Float64}, MOI.GreaterThan{Float64}, MOI.LessThan{Float64},
MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
const SS = Union{MOI.EqualTo{Float64}, MOI.GreaterThan{Float64}, MOI.LessThan{Float64},
MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
MOI.SecondOrderCone, MOI.RotatedSecondOrderCone,
MOI.ExponentialCone, MOI.DualExponentialCone,
MOI.PowerCone, MOI.DualPowerCone,
Expand Down Expand Up @@ -38,7 +40,7 @@ Example:
```julia
julia> using Dualization, JuMP, GLPK
julia> model = Model(with_optimizer(DualOptimizer, GLPK.Optimizer()))
julia> model = Model(dual_optimizer(GLPK.Optimizer))
A JuMP Model
Feasibility problem with:
Variables: 0
Expand All @@ -49,14 +51,14 @@ Solver name: Dual model with GLPK attached
"""
function DualOptimizer(dual_optimizer::OT) where {OT <: MOI.ModelLike}
return DualOptimizer{Float64}(dual_optimizer)
end
end

function DualOptimizer{T}(dual_optimizer::OT) where {T, OT <: MOI.ModelLike}
dual_problem = DualProblem{T}(MOIB.full_bridge_optimizer(MOIU.CachingOptimizer(DualizableModel{T}(), dual_optimizer), T))
# discover the type of MOIU.CachingOptimizer(DualizableModel{T}(), dual_optimizer)
OptimizerType = typeof(dual_problem.dual_model)
return DualOptimizer{T, OptimizerType}(dual_problem)
end
end

function DualOptimizer()
return error("DualOptimizer must have a solver attached")
Expand All @@ -76,7 +78,7 @@ function MOI.supports_constraint(optimizer::DualOptimizer, F::Type{<:SF}, S::Typ
end

function MOI.supports_constraint(::DualOptimizer, ::Type{MOI.AbstractFunction}, ::Type{MOI.AbstractSet})
return false
return false
end

function MOI.copy_to(dest::DualOptimizer, src::MOI.ModelLike; kwargs...)
Expand All @@ -99,7 +101,7 @@ function MOI.copy_to(dest::DualOptimizer, src::MOI.ModelLike; kwargs...)
end

function MOI.optimize!(optimizer::DualOptimizer)
return MOI.optimize!(optimizer.dual_problem.dual_model)
return MOI.optimize!(optimizer.dual_problem.dual_model)
end

function MOI.is_empty(optimizer::DualOptimizer)
Expand Down Expand Up @@ -142,23 +144,23 @@ function MOI.get(optimizer::DualOptimizer, ::MOI.SolverName)
end

function MOI.get(optimizer::DualOptimizer, ::MOI.VariablePrimal, vi::VI)
return -MOI.get(optimizer.dual_problem.dual_model,
return -MOI.get(optimizer.dual_problem.dual_model,
MOI.ConstraintDual(), get_ci_dual_problem(optimizer, vi))
end

function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintDual,
function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintDual,
ci::CI{F,S}) where {F <: MOI.AbstractScalarFunction, S <: MOI.AbstractScalarSet}
return MOI.get(optimizer.dual_problem.dual_model,
return MOI.get(optimizer.dual_problem.dual_model,
MOI.VariablePrimal(), get_vi_dual_problem(optimizer, ci))
end

function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintDual,
function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintDual,
ci::CI{F,S}) where {F <: MOI.AbstractVectorFunction, S <: MOI.AbstractVectorSet}
return MOI.get.(optimizer.dual_problem.dual_model,
return MOI.get.(optimizer.dual_problem.dual_model,
MOI.VariablePrimal(), get_vis_dual_problem(optimizer, ci))
end

function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
ci::CI{F,S}) where {F <: MOI.AbstractScalarFunction, S <: MOI.AbstractScalarSet}
primal_ci_constant = get_primal_ci_constant(optimizer, ci)
# If it has no key than there is no dual constraint
Expand All @@ -169,7 +171,7 @@ function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
return MOI.get(optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual_problem) - primal_ci_constant
end

function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
ci::CI{F,S}) where {F <: MOI.AbstractVectorFunction, S <: MOI.AbstractVectorSet}
# If it has no key than there is no dual constraint
if !haskey(optimizer.dual_problem.primal_dual_map.primal_con_dual_con, ci)
Expand All @@ -181,15 +183,15 @@ function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
return MOI.get(optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual_problem)
end

function MOI.get(optimizer::DualOptimizer, ::MOI.TerminationStatus)
function MOI.get(optimizer::DualOptimizer, ::MOI.TerminationStatus)
return dual_status(MOI.get(optimizer.dual_problem.dual_model, MOI.TerminationStatus()))
end

function dual_status(term::MOI.TerminationStatusCode)
if term == MOI.INFEASIBLE
if term == MOI.INFEASIBLE
return MOI.DUAL_INFEASIBLE
elseif term == MOI.DUAL_INFEASIBLE
return MOI.INFEASIBLE
return MOI.INFEASIBLE
elseif term == MOI.ALMOST_INFEASIBLE
return MOI.ALMOST_DUAL_INFEASIBLE
elseif term == MOI.ALMOST_DUAL_INFEASIBLE
Expand All @@ -216,4 +218,4 @@ end

function MOI.get(optimizer::DualOptimizer, attr::Union{MOI.AbstractModelAttribute, MOI.AbstractOptimizerAttribute})
return MOI.get(optimizer.dual_problem.dual_model, attr)
end
end
9 changes: 4 additions & 5 deletions src/dualize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ function dualize(model::JuMP.Model; dual_names::DualNames = EMPTY_DUAL_NAMES)
return JuMP_model
end

function dualize(model::JuMP.Model, factory::OptimizerFactory; dual_names::DualNames = EMPTY_DUAL_NAMES)
function dualize(model::JuMP.Model, optimizer_constructor; dual_names::DualNames = EMPTY_DUAL_NAMES)
# Dualize the JuMP model
dual_JuMP_model = dualize(model; dual_names = dual_names)
# Set the optimizer
JuMP.set_optimizer(dual_JuMP_model, factory)
JuMP.set_optimizer(dual_JuMP_model, optimizer_constructor)
return dual_JuMP_model
end

Expand All @@ -106,11 +106,10 @@ from the original primal model into the respective objects of the dual model.
The function will return a JuMP model with the dual representation of the problem.
* A `JuMP.Model` and an optimizer factory
* A `JuMP.Model` and an optimizer constructor
The function will return a JuMP model with the dual representation of the problem with
the `OptimizerFactory` attached. The `OptimizerFactory` is the solver and its key arguments
that users provide in JuMP models, i.e. `with_optimizer(GLPK.Optimizer)`.
the optimizer constructor attached.
On each of these methods, the user can provide the following keyword arguments:
Expand Down
10 changes: 5 additions & 5 deletions test/Solvers/csdp_test.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using CSDP
const CSDP_PRIMAL_OPT = CSDP.Optimizer(printlevel = 0)
const CSDP_DUAL_OPT = DualOptimizer(CSDP.Optimizer(printlevel = 0))
const CSDP_PRIMAL_FACTORY = with_optimizer(CSDP.Optimizer, printlevel = 0)
const CSDP_DUAL_FACTORY = with_optimizer(DualOptimizer, CSDP.Optimizer(printlevel = 0))
const CSDP_PRIMAL_FACTORY = MOI.OptimizerWithAttributes(CSDP.Optimizer, MOI.Silent() => true)
const CSDP_DUAL_FACTORY = dual_optimizer(CSDP_PRIMAL_FACTORY)
const CSDP_PRIMAL_OPT = MOI.instantiate(CSDP_PRIMAL_FACTORY)
const CSDP_DUAL_OPT = MOI.instantiate(CSDP_DUAL_FACTORY)

push!(primal_conic_factory, CSDP_PRIMAL_FACTORY)
push!(dual_conic_factory, CSDP_DUAL_FACTORY)
Expand All @@ -17,4 +17,4 @@ push!(primal_conic_optimizer, CSDP_PRIMAL_OPT)
sdpt4_test
]
test_strong_duality(list_of_sdp_triang_problems, CSDP_PRIMAL_FACTORY)
end
end
10 changes: 5 additions & 5 deletions test/Solvers/ecos_test.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using ECOS
const ECOS_PRIMAL_OPT = ECOS.Optimizer(verbose = 0)
const ECOS_DUAL_OPT = DualOptimizer(ECOS.Optimizer(verbose = 0))
const ECOS_PRIMAL_FACTORY = with_optimizer(ECOS.Optimizer, verbose = 0)
const ECOS_DUAL_FACTORY = with_optimizer(DualOptimizer, ECOS.Optimizer(verbose = 0))
const ECOS_PRIMAL_FACTORY = MOI.OptimizerWithAttributes(ECOS.Optimizer, MOI.Silent() => true)
const ECOS_DUAL_FACTORY = dual_optimizer(ECOS_PRIMAL_FACTORY)
const ECOS_PRIMAL_OPT = MOI.instantiate(ECOS_PRIMAL_FACTORY)
const ECOS_DUAL_OPT = MOI.instantiate(ECOS_DUAL_FACTORY)

# push!(dual_linear_optimizer, DualOptimizer(ECOS.Optimizer(verbose = 0)))

Expand Down Expand Up @@ -36,4 +36,4 @@ const ECOS_DUAL_FACTORY = with_optimizer(DualOptimizer, ECOS.Optimizer(verbose =
]
test_strong_duality(list_of_rsoc_problems, ECOS_PRIMAL_FACTORY)
end
end
end
10 changes: 5 additions & 5 deletions test/Solvers/glpk_test.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using GLPK
const GLPK_PRIMAL_OPT = GLPK.Optimizer()
const GLPK_DUAL_OPT = DualOptimizer(GLPK.Optimizer())
const GLPK_PRIMAL_FACTORY = with_optimizer(GLPK.Optimizer)
const GLPK_DUAL_FACTORY = with_optimizer(DualOptimizer, GLPK.Optimizer())
const GLPK_PRIMAL_FACTORY = MOI.OptimizerWithAttributes(GLPK.Optimizer, MOI.Silent() => true)
const GLPK_DUAL_FACTORY = dual_optimizer(GLPK_PRIMAL_FACTORY)
const GLPK_PRIMAL_OPT = MOI.instantiate(GLPK_PRIMAL_FACTORY)
const GLPK_DUAL_OPT = MOI.instantiate(GLPK_DUAL_FACTORY)

push!(primal_linear_factory, GLPK_PRIMAL_FACTORY)
push!(dual_linear_factory, GLPK_DUAL_FACTORY)
Expand All @@ -26,4 +26,4 @@ push!(primal_linear_optimizer, GLPK_PRIMAL_OPT)
lp13_test
]
test_strong_duality(list_of_linear_problems, GLPK_PRIMAL_FACTORY)
end
end
10 changes: 5 additions & 5 deletions test/Solvers/scs_test.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using SCS
const SCS_PRIMAL_OPT = SCS.Optimizer(verbose = false)
const SCS_DUAL_OPT = DualOptimizer(SCS.Optimizer(verbose = false))
const SCS_PRIMAL_FACTORY = with_optimizer(SCS.Optimizer, verbose = false)
const SCS_DUAL_FACTORY = with_optimizer(DualOptimizer, SCS.Optimizer(verbose = false))
const SCS_PRIMAL_FACTORY = MOI.OptimizerWithAttributes(SCS.Optimizer, MOI.Silent() => true)
const SCS_DUAL_FACTORY = dual_optimizer(SCS_PRIMAL_FACTORY)
const SCS_PRIMAL_OPT = MOI.instantiate(SCS_PRIMAL_FACTORY)
const SCS_DUAL_OPT = MOI.instantiate(SCS_DUAL_FACTORY)

push!(primal_power_cone_factory, SCS_PRIMAL_FACTORY)
push!(dual_power_cone_factory, SCS_DUAL_FACTORY)
Expand All @@ -23,4 +23,4 @@ end
pow2_test
]
test_strong_duality(list_of_pow_problems, SCS_PRIMAL_FACTORY; atol = 1e-3, rtol = 1e-3)
end
end
6 changes: 3 additions & 3 deletions test/Tests/test_JuMP_dualize.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# For testing bug found on issue # 52
function solve_vaf_sdp(factory::JuMP.OptimizerFactory)
model = Model(factory)
function solve_vaf_sdp(optimizer_constructor)
model = Model(optimizer_constructor)
@variable(model, x)
@constraint(model, [4x x; x 4x] - ones(2, 2) in PSDCone())
@objective(model, Min, x)
Expand Down Expand Up @@ -39,4 +39,4 @@ end
@test isapprox(primal_csdp, dual_csdp; atol = 1e-6)
end
end
end
end
Loading

0 comments on commit 135b2e4

Please sign in to comment.