diff --git a/src/MiniZinc.jl b/src/MiniZinc.jl index 625fa04..c98cd47 100644 --- a/src/MiniZinc.jl +++ b/src/MiniZinc.jl @@ -15,11 +15,20 @@ const ReifiedEqualTo{T} = MOI.Reified{MOI.EqualTo{T}} const ReifiedBinPacking{T} = MOI.Reified{MOI.BinPacking{T}} const ReifiedTable{T} = MOI.Reified{MOI.Table{T}} +struct MiniZincSet <: MOI.AbstractVectorSet + name::String + fields::Vector{Union{Int,UnitRange{Int}}} +end + +MOI.dimension(set::MiniZincSet) = maximum(maximum, set.fields) +Base.copy(set::MiniZincSet) = set + MOI.Utilities.@model( Model, (MOI.ZeroOne, MOI.Integer, MOI.EqualTo{Bool}), (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval), ( + MiniZincSet, MOI.AllDifferent, MOI.Circuit, MOI.CountAtLeast, diff --git a/src/write.jl b/src/write.jl index 61f5982..88ab631 100644 --- a/src/write.jl +++ b/src/write.jl @@ -149,9 +149,17 @@ function _to_string( return ret end -struct MiniZincSet <: MOI.AbstractSet - name::String - fields::Vector{Union{Int,UnitRange{Int}}} +function _write_constraint( + io::IO, + predicates::Set, + variables::Dict, + f::MOI.VectorOfVariables, + mzn::MiniZincSet, +) + strs = [_to_string(variables, f.variables[field]) for field in mzn.fields] + println(io, "constraint $(mzn.name)(", join(strs, ", "), ");") + push!(predicates, mzn.name) + return end function _write_constraint( @@ -167,10 +175,7 @@ function _write_constraint( MOI.Circuit, }, ) - mzn = MiniZincSet(s) - strs = [_to_string(variables, f.variables[field]) for field in mzn.fields] - println(io, "constraint $(mzn.name)(", join(strs, ", "), ");") - push!(predicates, mzn.name) + _write_constraint(io, predicates, variables, f, MiniZincSet(s)) return end diff --git a/test/examples/packing.jl b/test/examples/packing.jl new file mode 100644 index 0000000..cb664c1 --- /dev/null +++ b/test/examples/packing.jl @@ -0,0 +1,49 @@ +# Copyright (c) 2022 MiniZinc.jl contributors +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +# Inspired from the square packing tutorial in https://www.minizinc.org/ +function test_packing() + n = 6 + sizes = collect(1:n) + upper_bound = sum(sizes) + model = MOI.instantiate( + () -> MiniZinc.Optimizer{Int}("chuffed"); + with_cache_type = Int, + with_bridge_type = Int, + ) + MOI.set(model, MOI.RawOptimizerAttribute("model_filename"), "test.mzn") + # We need this `s` variable that is trivially equal to `sizes` + # because `MiniZincSet` supports only VectorOfVariables + s = [MOI.add_constrained_variable(model, MOI.Integer())[1] for i in 1:n] + x = [MOI.add_constrained_variable(model, MOI.Integer())[1] for i in 1:n] + y = [MOI.add_constrained_variable(model, MOI.Integer())[1] for i in 1:n] + max_x, _ = MOI.add_constrained_variable(model, MOI.Integer()) + max_y, _ = MOI.add_constrained_variable(model, MOI.Integer()) + MOI.add_constraint.(model, s, MOI.EqualTo.(sizes)) + MOI.add_constraint.(model, x, MOI.Interval(1, upper_bound)) + MOI.add_constraint.(model, y, MOI.Interval(1, upper_bound)) + MOI.add_constraint(model, max_x, MOI.Interval(1, upper_bound)) + MOI.add_constraint(model, max_y, MOI.Interval(1, upper_bound)) + MOI.add_constraint.(model, 1max_x .- 1x, MOI.GreaterThan.(sizes)) + MOI.add_constraint.(model, 1max_y .- 1y, MOI.GreaterThan.(sizes)) + MOI.add_constraint( + model, + MOI.VectorOfVariables([x; y; s; s]), + MiniZinc.MiniZincSet( + "diffn", + [1:n, n .+ (1:n), 2n .+ (1:n), 3n .+ (1:n)], + ), + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + obj = (1max_x) * max_y + MOI.set(model, MOI.ObjectiveFunction{typeof(obj)}(), obj) + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) === MOI.OPTIMAL + @test MOI.get(model, MOI.PrimalStatus()) === MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.ResultCount()) == 1 + @test MOI.get(model, MOI.ObjectiveValue()) == 120 + rm("test.mzn") + return +end diff --git a/test/runtests.jl b/test/runtests.jl index a4502a6..96d6515 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1696,6 +1696,14 @@ function test_integer_bounds_less_than_than() index_map, _ = MOI.optimize!(mzn, model) @test MOI.get(mzn, MOI.TerminationStatus()) == MOI.OPTIMAL @test MOI.get(mzn, MOI.VariablePrimal(), index_map[x]) == 1 +end + +function test_minizincset() + set = MiniZinc.MiniZincSet("diffn", [1:2, 3:4, 5:6, 7:8]) + @test MOI.dimension(set) == 8 + model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + x, _ = MOI.add_constrained_variables(model, set) + @test length(x) == 8 return end