jump-dev / MiniZinc.jl

A Julia interface to the MiniZinc constraint modeling language
https://www.minizinc.org/
MIT License
18 stars 4 forks source link

How to write Model to File to solve with MiniZinc-IDE #16

Closed mhechthz closed 1 year ago

mhechthz commented 1 year ago

Using

    dest = MOI.FileFormats.Model(format = MOI.FileFormats.FORMAT_MOF)
    MOI.write_to_file(dest, "file.mof.json")

on a test problem results in

{
  "name": "MathOptFormat Model",
  "version": {
    "major": 1,
    "minor": 1
  },
  "variables": [],
  "objective": {
    "sense": "feasibility"
  },
  "constraints": []
}

i.e. obviously well formatted but empty file.

Nevertheless, this is not the standard format for MiniZinc.

So how to write aproblem to file and how to write it in a format that MiniZinc-IDE understands? For a LP-problem one can write in lp or mps format for other solvers or to be visually inspected. With MiniZinc.jl it should be at least possible to write a file readable for MiniZinc (and humans).

odow commented 1 year ago

With MiniZinc.jl it should be at least possible to write a file readable for MiniZinc (and humans).

If you have a MiniZinc.Optimizer, you can write it using

https://github.com/jump-dev/MiniZinc.jl/blob/e2bce6192bd3415cc2adaef177e44bbec5d7beb2/src/write.jl#L462

open("file.mzn", "w") do io
    write(io, model)
end

We could make this friendlier.

mhechthz commented 1 year ago

Ok, looks good. But how to call/import write the right way?

ERROR: LoadError: MethodError: no method matching write(::IOStream, ...

Currently I just do import MiniZinc.

EDIT: Updated to version 0.2.0. Now I get:

ERROR: LoadError: UndefVarError: 'Reified' not defined

on running

MOI.add_constraint(
        model,
        MOI.VectorOfVariables([z[2], x[2], x[3]]),
        MiniZinc.Reified(MOI.AllDifferent(2)),
    )
odow commented 1 year ago

But how to call/import write the right way?

How are you currently calling it? You'll need something like this:

model = MOI.Utilities.CachingOptimizer(
    MiniZinc.Model{Int}(),
    MiniZinc.Optimizer{Int}(MiniZinc.Chuffed()),
)
# ...
MOI.Utilities.attach_optimizer(model)
open("file.mzn", "w") do io
    write(io, model.optimizer)
end

It's pretty clunky; we should improve this.

ERROR: LoadError: UndefVarError: 'Reified' not defined

Oops. I had a typo in the README: https://github.com/jump-dev/MiniZinc.jl/pull/19

mhechthz commented 1 year ago

Well I changed the code, but nevertheless the problem remains. I took your basic example in MiniZinc.jl 0.2.0 in Julia 1.8.3:

ENV["JULIA_LIBMINIZINC_DIR"] = "C:\\Users\\Michael\\AppData\\Local\\Programs\\MiniZinc"

import MiniZinc

const MOI = MiniZinc.MOI

function main()
    model = MOI.Utilities.CachingOptimizer(
        MiniZinc.Model{Int}(),
        MiniZinc.Optimizer{Int}(MiniZinc.Chuffed()),
    )
    # xᵢ ∈ {1, 2, 3} ∀i=1,2,3
    x = MOI.add_variables(model, 3)
    MOI.add_constraint.(model, x, MOI.Interval(1, 3))
    MOI.add_constraint.(model, x, MOI.Integer())
    # zⱼ ∈ {0, 1}    ∀j=1,2
    z = MOI.add_variables(model, 2)
    MOI.add_constraint.(model, z, MOI.ZeroOne())

    # z₁ <-> x₁ != x₂    
    # MOI.add_constraint(
    #     model,
    #     MOI.VectorOfVariables([z[1], x[1], x[2]]),
    #     MiniZinc.Reified(MOI.AllDifferent(2)),
    # )

    # z₂ <-> x₂ != x₃
    # MOI.add_constraint(
    #     model,
    #     MOI.VectorOfVariables([z[2], x[2], x[3]]),
    #     MiniZinc.Reified(MOI.AllDifferent(2)),
    # )

    # z₁ + z₂ = 1
    MOI.add_constraint(model, 1 * z[1] + x[2], MOI.EqualTo(1))

    open("file.mzn", "w") do io
        write(io, model.optimizer)
    end

    MOI.optimize!(model)
    x_star = MOI.get(model, MOI.VariablePrimal(), x)
    z_star = MOI.get(model, MOI.VariablePrimal(), z)
    return x_star, z_star
end

main()

with the part of Reified deactivated because of the error message there.

I get

ERROR: MethodError: no method matching write(::IOStream, ::MiniZinc.Optimizer{Int64})
Closest candidates are:
  write(::IO, ::Any) at io.jl:672
  write(::IO, ::Any, ::Any...) at io.jl:673
  write(::IO, ::Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}) at io.jl:686
  ...
Stacktrace:
 [1] write(io::IOStream, x::MiniZinc.Optimizer{Int64})
   @ Base .\io.jl:672
 [2] (::var"#5#6"{MathOptInterface.Utilities.CachingOptimizer{MiniZinc.Optimizer{Int64}, MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}}}})(io::IOStream)
   @ Main c:\Micha\Programming\Julia\jlMiniZinc.jl:21
 [3] open(::var"#5#6"{MathOptInterface.Utilities.CachingOptimizer{MiniZinc.Optimizer{Int64}, MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}}}}, ::String, ::Vararg{String}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Base .\io.jl:384
 [4] open(::Function, ::String, ::String)
   @ Base .\io.jl:381
 [5] main()
   @ Main c:\Micha\Programming\Julia\jlMiniZinc.jl:20
 [6] top-level scope
   @ c:\Micha\Programming\Julia\jlMiniZinc.jl:48
odow commented 1 year ago

Let me take a look. I haven't actually tested this locally.

odow commented 1 year ago

D'oh. I didn't need the Optimizer part:

julia> import MiniZinc

julia> const MOI = MiniZinc.MOI
MathOptInterface

julia> function main()
           model = MiniZinc.Model{Int}()
           # xᵢ ∈ {1, 2, 3} ∀i=1,2,3
           x = MOI.add_variables(model, 3)
           MOI.add_constraint.(model, x, MOI.Interval(1, 3))
           MOI.add_constraint.(model, x, MOI.Integer())
           # zⱼ ∈ {0, 1}    ∀j=1,2
           z = MOI.add_variables(model, 2)
           MOI.add_constraint.(model, z, MOI.ZeroOne())
           # z₁ <-> x₁ != x₂    
           MOI.add_constraint(
               model,
               MOI.VectorOfVariables([z[1], x[1], x[2]]),
               MOI.Reified(MOI.AllDifferent(2)),
           )
           # z₂ <-> x₂ != x₃
           MOI.add_constraint(
               model,
               MOI.VectorOfVariables([z[2], x[2], x[3]]),
               MOI.Reified(MOI.AllDifferent(2)),
           )
           # z₁ + z₂ = 1
           MOI.add_constraint(model, 1 * z[1] + x[2], MOI.EqualTo(1))
           open("file.mzn", "w") do io
               write(io, model)
           end
       end
main (generic function with 1 method)

julia> main()

shell> cat file.mzn
include "alldifferent.mzn";
var 1 .. 3: x1;
var 1 .. 3: x2;
var 1 .. 3: x3;
var bool: x4;
var bool: x5;
constraint 1*x2 + 1*x4 = 1;
constraint x4 <-> alldifferent([x1, x2]);
constraint x5 <-> alldifferent([x2, x3]);
solve satisfy;
mhechthz commented 1 year ago

Ok. Changed MiniZinc.Reified() to MOI.Reified() and this error is gone.

For the write statement

    open("file.mzn", "w") do io
        write(io, model)
    end

I get nevertheless

ERROR: MethodError: no method matching write(::IOStream, ::MathOptInterface.Utilities.CachingOptimizer{MiniZinc.Optimizer{Int64}, MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}}})
Closest candidates are:
  write(::IO, ::Any) at io.jl:672
  write(::IO, ::Any, ::Any...) at io.jl:673
  write(::IO, ::Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}) at io.jl:686
  ...
Stacktrace:
 [1] write(io::IOStream, x::MathOptInterface.Utilities.CachingOptimizer{MiniZinc.Optimizer{Int64}, MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}}})
   @ Base .\io.jl:672
 [2] (::var"#13#14"{MathOptInterface.Utilities.CachingOptimizer{MiniZinc.Optimizer{Int64}, MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}}}})(io::IOStream)
   @ Main c:\Micha\Programming\Julia\jlMiniZinc.jl:39
 [3] open(::var"#13#14"{MathOptInterface.Utilities.CachingOptimizer{MiniZinc.Optimizer{Int64}, MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}}}}, ::String, ::Vararg{String}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Base .\io.jl:384
 [4] open(::Function, ::String, ::String)
   @ Base .\io.jl:381
 [5] main()
   @ Main c:\Micha\Programming\Julia\jlMiniZinc.jl:38
 [6] top-level scope
   @ c:\Micha\Programming\Julia\jlMiniZinc.jl:48
odow commented 1 year ago

I changed the model to be

model = MiniZinc.Model{Int}()

mhechthz commented 1 year ago

:smiley:

Ok. So I did and it works for write(), but now no optimization is possible, because you removed optimize!() and I didn't (I usually like to do interactive debugging, like e.g. in LPsolve-IDE for lp files):

C:\Micha\Programming\Julia>julia jlMiniZinc.jl
ERROR: LoadError: MethodError: no method matching optimize!(::MathOptInterface.Utilities.GenericModel{Int64, MathOptInterface.Utilities.ObjectiveContainer{Int64}, MathOptInterface.Utilities.VariablesContainer{Int64}, MiniZinc.ModelFunctionConstraints{Int64}})
Closest candidates are:
  optimize!(::Any, ::Any) at C:\Users\Michael\.julia\packages\MathOptInterface\Ht8hE\src\MathOptInterface.jl:84
  optimize!(::MathOptInterface.Utilities.MockOptimizer) at C:\Users\Michael\.julia\packages\MathOptInterface\Ht8hE\src\Utilities\mockoptimizer.jl:205
  optimize!(::MathOptInterface.Utilities.CachingOptimizer) at C:\Users\Michael\.julia\packages\MathOptInterface\Ht8hE\src\Utilities\cachingoptimizer.jl:302
  ...
Stacktrace:
 [1] main()
   @ Main C:\Micha\Programming\Julia\jlMiniZinc.jl:45
 [2] top-level scope
   @ C:\Micha\Programming\Julia\jlMiniZinc.jl:51
in expression starting at C:\Micha\Programming\Julia\jlMiniZinc.jl:51
odow commented 1 year ago

Oh, you want both. How's this:

julia> function main()
           model = MOI.Utilities.CachingOptimizer(
               MiniZinc.Model{Int}(),
               MiniZinc.Optimizer{Int}(MiniZinc.Chuffed()),
           )
           # xᵢ ∈ {1, 2, 3} ∀i=1,2,3
           x = MOI.add_variables(model, 3)
           MOI.add_constraint.(model, x, MOI.Interval(1, 3))
           MOI.add_constraint.(model, x, MOI.Integer())
           # zⱼ ∈ {0, 1}    ∀j=1,2
           z = MOI.add_variables(model, 2)
           MOI.add_constraint.(model, z, MOI.ZeroOne())
           # z₁ <-> x₁ != x₂    
           MOI.add_constraint(
               model,
               MOI.VectorOfVariables([z[1], x[1], x[2]]),
               MOI.Reified(MOI.AllDifferent(2)),
           )
           # z₂ <-> x₂ != x₃
           MOI.add_constraint(
               model,
               MOI.VectorOfVariables([z[2], x[2], x[3]]),
               MOI.Reified(MOI.AllDifferent(2)),
           )
           # z₁ + z₂ = 1
           MOI.add_constraint(model, 1 * z[1] + x[2], MOI.EqualTo(1))
           open("file.mzn", "w") do io
               write(io, model.model_cache)
           end
           MOI.optimize!(model)
           x_star = MOI.get(model, MOI.VariablePrimal(), x)
           z_star = MOI.get(model, MOI.VariablePrimal(), z)
           return x_star, z_star
       end
main (generic function with 1 method)

julia> main()
Warning: included file "count.mzn" overrides a global constraint file from the standard library. This is deprecated. For a solver-specific redefinition of a global constraint, override "fzn_<global>.mzn" instead.

Warning: included file "global_cardinality_low_up.mzn" overrides a global constraint file from the standard library. This is deprecated. For a solver-specific redefinition of a global constraint, override "fzn_<global>.mzn" instead.

([1, 1, 3], [0, 1])

shell> cat file.mzn
include "alldifferent.mzn";
var 1 .. 3: x1;
var 1 .. 3: x2;
var 1 .. 3: x3;
var bool: x4;
var bool: x5;
constraint 1*x2 + 1*x4 = 1;
constraint x4 <-> alldifferent([x1, x2]);
constraint x5 <-> alldifferent([x2, x3]);
solve satisfy;
mhechthz commented 1 year ago

I'm now here (writing and reading works):

ENV["JULIA_LIBMINIZINC_DIR"] = "C:\\bin\\MiniZinc"

import MiniZinc

const MOI = MiniZinc.MOI

function main()
    model = MOI.Utilities.CachingOptimizer(
        MiniZinc.Model{Int}(),
        MiniZinc.Optimizer{Int}(MiniZinc.Chuffed()),
    )
    # xᵢ ∈ {1, 2, 3} ∀i=1,2,3
    x = MOI.add_variables(model, 3)
    MOI.add_constraint.(model, x, MOI.Interval(1, 3))
    MOI.add_constraint.(model, x, MOI.Integer())
    # zⱼ ∈ {0, 1}    ∀j=1,2
    z = MOI.add_variables(model, 2)
    MOI.add_constraint.(model, z, MOI.ZeroOne())
    # z₁ <-> x₁ != x₂    
    MOI.add_constraint(
        model,
        MOI.VectorOfVariables([z[1], x[1], x[2]]),
        MOI.Reified(MOI.AllDifferent(2)),
    )
    # z₂ <-> x₂ != x₃
    MOI.add_constraint(
        model,
        MOI.VectorOfVariables([z[2], x[2], x[3]]),
        MOI.Reified(MOI.AllDifferent(2)),
    )
    # z₁ + z₂ = 1
    MOI.add_constraint(model, 1 * z[1] + x[2], MOI.EqualTo(1))

    # write it to MiniZinc
    open("file.mzn", "w") do io
        write(io, model.model_cache)
    end

    # read from MiniZinc file, just to check if it works --> it works!
    open("file.mzn", "w") do io
        model = read(io)
    end

    MOI.optimize!(model)
    x_star = MOI.get(model, MOI.VariablePrimal(), x)
    z_star = MOI.get(model, MOI.VariablePrimal(), z)
    return x_star, z_star
end

main()

and get ...

EDIT: sorry had a typo, the error is

C:\Micha\Programming\Julia>julia jlMiniZinc.jl
ERROR: LoadError: MethodError: no method matching optimize!(::Vector{UInt8})
Closest candidates are:
  optimize!(::Any, ::Any) at C:\Users\Michael\.julia\packages\MathOptInterface\Ht8hE\src\MathOptInterface.jl:84
  optimize!(::MathOptInterface.Utilities.MockOptimizer) at C:\Users\Michael\.julia\packages\MathOptInterface\Ht8hE\src\Utilities\mockoptimizer.jl:205
  optimize!(::MathOptInterface.Utilities.CachingOptimizer) at C:\Users\Michael\.julia\packages\MathOptInterface\Ht8hE\src\Utilities\cachingoptimizer.jl:302
  ...
Stacktrace:
 [1] main()
   @ Main C:\Micha\Programming\Julia\jlMiniZinc.jl:45
 [2] top-level scope
   @ C:\Micha\Programming\Julia\jlMiniZinc.jl:52
in expression starting at C:\Micha\Programming\Julia\jlMiniZinc.jl:52
odow commented 1 year ago

We don't support reading mzn files.

mhechthz commented 1 year ago

We don't support reading mzn files.

Well, why it works?

odow commented 1 year ago

Well, why it works?

Your model = read(io) reads in a vector of bytes. It doesn't create a new MiniZinc.Model.

mhechthz commented 1 year ago

Ok ... :confused: my fault. I red and wrote just the text, since optimize didn't work I didn't realize ...

mhechthz commented 1 year ago

I'm now at my work computer and it works in this version:

function main()
    model = MOI.Utilities.CachingOptimizer(
        MiniZinc.Model{Int}(),
        MiniZinc.Optimizer{Int}(MiniZinc.Chuffed()),
    )
    # xᵢ ∈ {1, 2, 3} ∀i=1,2,3
    x = MOI.add_variables(model, 3)
    MOI.add_constraint.(model, x, MOI.Interval(1, 3))
    MOI.add_constraint.(model, x, MOI.Integer())
    # zⱼ ∈ {0, 1}    ∀j=1,2
    z = MOI.add_variables(model, 2)
    MOI.add_constraint.(model, z, MOI.ZeroOne())
    # z₁ <-> x₁ != x₂
    MOI.add_constraint(
        model,
        MOI.VectorOfVariables([z[1], x[1], x[2]]),
        MOI.Reified(MOI.AllDifferent(2)),
    )
    # z₂ <-> x₂ != x₃
    MOI.add_constraint(
        model,
        MOI.VectorOfVariables([z[2], x[2], x[3]]),
        MOI.Reified(MOI.AllDifferent(2)),
    )
    # z₁ + z₂ = 1
    MOI.add_constraint(model, 1 * z[1] + x[2], MOI.EqualTo(1))

    MOI.optimize!(model)

    dest = MOI.FileFormats.Model(format = MOI.FileFormats.FORMAT_MOF)

   # write as json
    MOI.write_to_file(dest, "file.mof.json")

    # write for MiniZinc-IDE
    open("file.mzn", "w") do io
        write(io, model.model_cache)
    end

    x_star = MOI.get(model, MOI.VariablePrimal(), x)
    z_star = MOI.get(model, MOI.VariablePrimal(), z)
    return x_star, z_star
end

So thank you. I'll try to figure out for myself where the problem is at my home computer.