Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Sep 21, 2023
1 parent 0beeb0c commit a493c68
Showing 1 changed file with 94 additions and 141 deletions.
235 changes: 94 additions & 141 deletions src/Utilities/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ end

function MOI.get(model::AbstractModel, ::MOI.ListOfVariableAttributesSet)
ret = MOI.AbstractVariableAttribute[]
if isempty(model.var_to_name)
if !isempty(model.var_to_name)
push!(ret, MOI.VariableName())
end
return ret
Expand Down Expand Up @@ -608,14 +608,12 @@ function _struct_of_constraints_type(name, subtypes, parametrized_type)
if length(subtypes) == 1
# Only one type, no need for a `StructOfConstraints`.
return subtypes[1]
else
T = esc(:T)
t = :($name{$T})
if parametrized_type
append!(t.args, subtypes)
end
return t
end
expr = Expr(:curly, name, esc(:T))
if parametrized_type
append!(expr.args, subtypes)
end
return expr
end

# This macro is for expert/internal use only. Prefer the concrete Model type
Expand All @@ -634,155 +632,96 @@ end
is_optimizer = false
)
Creates a type `model_name` implementing the MOI model interface and containing
`scalar_sets` scalar sets `typed_scalar_sets` typed scalar sets, `vector_sets`
vector sets, `typed_vector_sets` typed vector sets, `scalar_functions` scalar
functions, `typed_scalar_functions` typed scalar functions, `vector_functions`
vector functions and `typed_vector_functions` typed vector functions.
To give no set/function, write `()`, to give one set `S`, write `(S,)`.
The function [`MOI.VariableIndex`](@ref) should not be given in
`scalar_functions`. The model supports [`MOI.VariableIndex`](@ref)-in-`S`
constraints where `S` is [`MOI.EqualTo`](@ref),
[`MOI.GreaterThan`](@ref), [`MOI.LessThan`](@ref),
[`MOI.Interval`](@ref), [`MOI.Integer`](@ref),
[`MOI.ZeroOne`](@ref), [`MOI.Semicontinuous`](@ref)
or [`MOI.Semiinteger`](@ref). The sets supported
with the [`MOI.VariableIndex`](@ref) cannot be controlled from the
macro, use the [`UniversalFallback`](@ref) to support more sets.
This macro creates a model specialized for specific types of constraint,
by defining specialized structures and methods. To create a model that,
in addition to be optimized for specific constraints, also support arbitrary
constraints and attributes, use [`UniversalFallback`](@ref).
If `is_optimizer = true`, the resulting struct is a
of [`GenericOptimizer`](@ref), which is a subtype of
[`MOI.AbstractOptimizer`](@ref), otherwise, it is a
[`GenericModel`](@ref), which is a subtype of
[`MOI.ModelLike`](@ref).
### Examples
The model describing an linear program would be:
```julia
@model(LPModel, # Name of model
(), # untyped scalar sets
(MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval), # typed scalar sets
(MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives), # untyped vector sets
(), # typed vector sets
(), # untyped scalar functions
(MOI.ScalarAffineFunction,), # typed scalar functions
(MOI.VectorOfVariables,), # untyped vector functions
(MOI.VectorAffineFunction,), # typed vector functions
false
)
```
Creates a type `model_name` implementing the MOI model interface and supporting
all combinations of the provided functions and sets.
Each `typed_` `scalar`/`vector` `sets`/`functions` argument is a tuple of types.
A type is "typed" if it has a coefficient `{T}` as the first type parameter.
## Tuple syntax
To give no set/function, write `()`.
To give one set or function `X`, write `(X,)`.
## `is_optimizer`
If `is_optimizer = true`, the resulting struct is a of [`GenericOptimizer`](@ref),
which is a subtype of [`MOI.AbstractOptimizer`](@ref), otherwise, it is a
[`GenericModel`](@ref), which is a subtype of [`MOI.ModelLike`](@ref).
## VariableIndex
* The function [`MOI.VariableIndex`](@ref) must not be given in
`scalar_functions`.
* The model supports [`MOI.VariableIndex`](@ref)-in-`S` constraints where `S`
is [`MOI.EqualTo`](@ref), [`MOI.GreaterThan`](@ref), [`MOI.LessThan`](@ref),
[`MOI.Interval`](@ref), [`MOI.Integer`](@ref), [`MOI.ZeroOne`](@ref),
[`MOI.Semicontinuous`](@ref) or [`MOI.Semiinteger`](@ref).
* The sets supported with [`MOI.VariableIndex`](@ref) cannot be controlled from
the macro; use [`UniversalFallback`](@ref) to support more sets.
## Examples
Let `MOI` denote `MathOptInterface`, `MOIU` denote `MOI.Utilities`.
The macro would create the following types with
[`struct_of_constraint_code`](@ref):
The model describing a linear program would be:
```julia
struct LPModelScalarConstraints{T, C1, C2, C3, C4} <: MOIU.StructOfConstraints
moi_equalto::C1
moi_greaterthan::C2
moi_lessthan::C3
moi_interval::C4
end
struct LPModelVectorConstraints{T, C1, C2, C3} <: MOIU.StructOfConstraints
moi_zeros::C1
moi_nonnegatives::C2
moi_nonpositives::C3
end
struct LPModelFunctionConstraints{T} <: MOIU.StructOfConstraints
moi_scalaraffinefunction::LPModelScalarConstraints{
T,
MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}},
MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}},
MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}},
MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.Interval{T}}
}
moi_vectorofvariables::LPModelVectorConstraints{
T,
MOIU.VectorOfConstraints{MOI.VectorOfVariables, MOI.Zeros},
MOIU.VectorOfConstraints{MOI.VectorOfVariables, MOI.Nonnegatives},
MOIU.VectorOfConstraints{MOI.VectorOfVariables, MOI.Nonpositives}
}
moi_vectoraffinefunction::LPModelVectorConstraints{
T,
MOIU.VectorOfConstraints{MOI.VectorAffineFunction{T}, MOI.Zeros},
MOIU.VectorOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives},
MOIU.VectorOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonpositives}
}
end
const LPModel{T} = MOIU.GenericModel{T,MOIU.ObjectiveContainer{T},MOIU.VariablesContainer{T},LPModelFunctionConstraints{T}}
@model(
LPModel, # model_name
(), # untyped scalar sets
(MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval), # typed scalar sets
(MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives), # untyped vector sets
(), # typed vector sets
(), # untyped scalar functions
(MOI.ScalarAffineFunction,), # typed scalar functions
(MOI.VectorOfVariables,), # untyped vector functions
(MOI.VectorAffineFunction,), # typed vector functions
false, # is_optimizer
)
```
The type `LPModel` implements the MathOptInterface API except methods specific
to optimizers like `optimize!` or `get` with `VariablePrimal`.
"""
macro model(
model_name,
ss,
sst,
vs,
vst,
sf,
sft,
vf,
vft,
scalar_sets,
typed_scalar_sets,
vector_sets,
typed_vector_sets,
scalar_functions,
typed_scalar_functions,
vector_functions,
typed_vector_functions,
is_optimizer = false,
)
scalar_sets = [SymbolSet.(ss.args, false); SymbolSet.(sst.args, true)]
vector_sets = [SymbolSet.(vs.args, false); SymbolSet.(vst.args, true)]

scname = esc(Symbol(string(model_name) * "ScalarConstraints"))
vcname = esc(Symbol(string(model_name) * "VectorConstraints"))

esc_model_name = esc(model_name)
# TODO if there is only one function or one set, remove the layer

scalar_funs = [
SymbolFun.(sf.args, false)
SymbolFun.(sft.args, true)
]
vector_funs = [
SymbolFun.(vf.args, false)
SymbolFun.(vft.args, true)
]
scalar_sets = vcat(
SymbolSet.(scalar_sets.args, false),
SymbolSet.(typed_scalar_sets.args, true),
)
vector_sets = vcat(
SymbolSet.(vector_sets.args, false),
SymbolSet.(typed_vector_sets.args, true),
)
scalar_funs = vcat(
SymbolFun.(scalar_functions.args, false),
SymbolFun.(typed_scalar_functions.args, true),
)
vector_funs = vcat(
SymbolFun.(vector_functions.args, false),
SymbolFun.(typed_vector_functions.args, true),
)
scname = esc(Symbol("$(model_name)ScalarConstraints"))
vcname = esc(Symbol("$(model_name)VectorConstraints"))
funs = [scalar_funs; vector_funs]
set_struct_types = map(eachindex(funs)) do i
if i <= length(scalar_funs)
cname = scname
sets = scalar_sets
cname, sets = if i <= length(scalar_funs)
scname, scalar_sets
else
cname = vcname
sets = vector_sets
vcname, vector_sets
end
voc = map(sets) do set
return :(VectorOfConstraints{$(_typed(funs[i])),$(_typed(set))})
end
return _struct_of_constraints_type(cname, voc, true)
end
func_name = esc(Symbol(string(model_name) * "FunctionConstraints"))
func_typed = _struct_of_constraints_type(func_name, set_struct_types, false)
T = esc(:T)
generic = if is_optimizer
:(GenericOptimizer{
$T,
ObjectiveContainer{$T},
VariablesContainer{$T},
$func_typed,
})
else
:(GenericModel{
$T,
ObjectiveContainer{$T},
VariablesContainer{$T},
$func_typed,
})
end
model_code = :(const $esc_model_name{$T} = $generic)
expr = Expr(:block)
func_name = esc(Symbol("$(model_name)FunctionConstraints"))
expr = quote end
if length(scalar_sets) >= 2
push!(expr.args, struct_of_constraint_code(scname, scalar_sets))
end
Expand All @@ -795,12 +734,26 @@ macro model(
struct_of_constraint_code(func_name, funs, set_struct_types),
)
end
push!(expr.args, model_code)
func_typed = _struct_of_constraints_type(func_name, set_struct_types, false)
T = esc(:T)
generic = is_optimizer ? GenericOptimizer : GenericModel
push!(
expr.args,
quote
const $(esc(model_name)){$T} = $generic{
$T,
ObjectiveContainer{$T},
VariablesContainer{$T},
$func_typed,
}
end,
)
return expr
end

const LessThanIndicatorOne{T} =
MOI.Indicator{MOI.ACTIVATE_ON_ONE,MOI.LessThan{T}}

const LessThanIndicatorZero{T} =
MOI.Indicator{MOI.ACTIVATE_ON_ZERO,MOI.LessThan{T}}

Expand Down

0 comments on commit a493c68

Please sign in to comment.