From a493c68d0732c024bc5e84af16609ff189017322 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 22 Sep 2023 10:08:46 +1200 Subject: [PATCH] Fix --- src/Utilities/model.jl | 235 +++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 141 deletions(-) diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index b98134588d..f7b7882eb2 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -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 @@ -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 @@ -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 @@ -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}}