InexactError on write_to_file

Hi, I encountered a problem with writing model specs to an LP file. I was replicating the Diet Problem example on JuMP’s page.

https://jump.dev/JuMP.jl/stable/tutorials/linear/diet/

Everything went fine until the end. Then I tried to export the model to an lp file with the following command.

write_to_file(model,"juliamodel.lp")

The reply I got was the following.

(UPDATE: It writes the file but still gives the error)

ERROR: InexactError: trunc(Int64, Inf)
Stacktrace:
  [1] trunc
    @ ./float.jl:805 [inlined]
  [2] round
    @ ./float.jl:369 [inlined]
  [3] _print_shortest(io::IOStream, x::Float64)
    @ MathOptInterface.FileFormats.LP ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/LP/LP.jl:19
  [4] _write_constraint_suffix(io::IOStream, set::MathOptInterface.Interval{Float64})
    @ MathOptInterface.FileFormats.LP ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/LP/LP.jl:148
  [5] _write_constraint(io::IOStream, model::MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.LP.ModelFunctionConstraints{Float64}}, index::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.Interval{Float64}}, variable_names::Dict{MathOptInterface.VariableIndex, String}; write_name::Bool)
    @ MathOptInterface.FileFormats.LP ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/LP/LP.jl:175
  [6] _write_constraints(io::IOStream, model::MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.LP.ModelFunctionConstraints{Float64}}, S::Type, variable_names::Dict{MathOptInterface.VariableIndex, String})
    @ MathOptInterface.FileFormats.LP ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/LP/LP.jl:231
  [7] write(io::IOStream, model::MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.LP.ModelFunctionConstraints{Float64}})
    @ MathOptInterface.FileFormats.LP ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/LP/LP.jl:310
  [8] #9
    @ ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/FileFormats.jl:115 [inlined]
  [9] open(::MathOptInterface.FileFormats.var"#9#10"{MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.LP.ModelFunctionConstraints{Float64}}}, ::String, ::Vararg{String}; kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Base ./io.jl:330
 [10] open(::Function, ::String, ::String)
    @ Base ./io.jl:328
 [11] compressed_open(f::Function, filename::String, mode::String, #unused#::MathOptInterface.FileFormats.NoCompression)
    @ MathOptInterface.FileFormats ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/utils.jl:213
 [12] compressed_open(f::Function, filename::String, mode::String, #unused#::MathOptInterface.FileFormats.AutomaticCompression)
    @ MathOptInterface.FileFormats ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/utils.jl:251
 [13] write_to_file(model::MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.LP.ModelFunctionConstraints{Float64}}, filename::String)
    @ MathOptInterface.FileFormats ~/.julia/packages/MathOptInterface/ajp5T/src/FileFormats/FileFormats.jl:114
 [14] write_to_file(model::Model, filename::String; format::MathOptInterface.FileFormats.FileFormat)
    @ JuMP ~/.julia/packages/JuMP/0C6kd/src/file_formats.jl:36
 [15] write_to_file(model::Model, filename::String)
    @ JuMP ~/.julia/packages/JuMP/0C6kd/src/file_formats.jl:33
 [16] top-level scope
    @ REPL[43]:1

The model print is as follows.

julia> print(model)
Min 2.49 x[hamburger] + 2.89 x[chicken] + 1.5 x[hot dog] + 1.89 x[fries] + 2.09 x[macaroni] + 1.99 x[pizza] + 2.49 x[salad] + 0.89 x[milk] + 1.59 x[ice cream]
Subject to
 x[milk] + x[ice cream] ≤ 6.0
 410 x[hamburger] + 420 x[chicken] + 560 x[hot dog] + 380 x[fries] + 320 x[macaroni] + 320 x[pizza] + 320 x[salad] + 100 x[milk] + 330 x[ice cream] ∈ [1800.0, 2200.0]
 24 x[hamburger] + 32 x[chicken] + 20 x[hot dog] + 4 x[fries] + 12 x[macaroni] + 15 x[pizza] + 31 x[salad] + 8 x[milk] + 8 x[ice cream] ∈ [91.0, Inf]
 26 x[hamburger] + 10 x[chicken] + 32 x[hot dog] + 19 x[fries] + 10 x[macaroni] + 12 x[pizza] + 12 x[salad] + 2.5 x[milk] + 10 x[ice cream] ∈ [0.0, 65.0]
 730 x[hamburger] + 1190 x[chicken] + 1800 x[hot dog] + 270 x[fries] + 930 x[macaroni] + 820 x[pizza] + 1230 x[salad] + 125 x[milk] + 180 x[ice cream] ∈ [0.0, 1779.0]
 x[hamburger] ≥ 0.0
 x[chicken] ≥ 0.0
 x[hot dog] ≥ 0.0
 x[fries] ≥ 0.0
 x[macaroni] ≥ 0.0
 x[pizza] ≥ 0.0
 x[salad] ≥ 0.0
 x[milk] ≥ 0.0
 x[ice cream] ≥ 0.0

My settings are as follows.

julia> versioninfo()
Julia Version 1.7.0
Commit 3bf9d17731 (2021-11-30 12:12 UTC)
Platform Info:
  OS: macOS (arm64-apple-darwin21.1.0)
  CPU: Apple M1 Max
  WORD_SIZE: 64
  LIBM: libopenlibm

Package versions

(@v1.7) pkg> status JuMP HiGHS
      Status `~/.julia/environments/v1.7/Project.toml`
  [87dc4568] HiGHS v1.1.3
  [4076af6c] JuMP v1.0.0

I am fairly new to Julia, so I could not infer any meaningful information from the error message. Perhaps I am doing something horribly wrong :slight_smile:

1 Like

Ooops. That’s a bug because of the Inf in the RHS. (It’s also present in the MPS file writer.) We should strip those out before writing the file because some solvers won’t like infinite bounds.

I’ve opened an issue: FileFormats: handle infinite bounds and coefficients · Issue #1857 · jump-dev/MathOptInterface.jl · GitHub. I’ll make the fix, but we probably won’t release a new version of MOI for another week or so.

As a temporary fix, just use something smaller than Inf to represent a large upper bound.

1 Like

Thanks a lot! Bugs happen all the time but I’m just glad that I did not mess it up with that clear tutorial :slight_smile:

1 Like

I did not mess it up with that clear tutorial

No, this was a clear oversight on my part. Sorry for that.

I wonder if I should change the tutorial to:

for limit in eachrow(limits)
    intake = @expression(
        model,
        sum(food[limit["name"]] * x[food["name"]] for food in eachrow(foods)),
    )
    if limit.min > -Inf
        @constraint(model, intake >= limit.min)
    end
    if limit.max < Inf
        @constraint(model, intake <= limit.max)
    end
end

In general, adding Inf into models increases the likelihood of bugs and some solvers might complain. But for an introductory tutorial, more lines of code increase the complications…

p.s., here’s the fix that will be included in the next release of MathOptInterface: [FileFormats] fix support of infinite constraint bounds by odow · Pull Request #1858 · jump-dev/MathOptInterface.jl · GitHub

1 Like

In my opinion, a tutorial should be as focused and clean as possible. In this case, it also shows a neat trick of adding upper and lower bounds (two constraints) in the same line.

On the other hand if a bound is Inf, it is actually not a bound. Therefore, there should not be a constraint at all. A simple solution would be to implicitly ignore Inf bounds (e.g. if <= and RHS is Inf, ignore constraint) but I doubt its implications would be so simple (I could not come with a clear example immediately but some funny representations might be found).

I think both methods are fine to show :slight_smile:

1 Like