jump-dev / MathOptInterface.jl

A data structure for mathematical optimization problems
http://jump.dev/MathOptInterface.jl/
Other
395 stars 87 forks source link

[FileFormats.MOF] Allow MOF to read/write SAF/SQF{Int} #2246

Closed chriscoey closed 1 year ago

chriscoey commented 1 year ago

I have a model parametrized by Int, and I cannot write it using the MOF JSON writer - I get:

  MethodError: no method matching head_name(::Type{MathOptInterface.ScalarAffineFunction{Int64}})
  Closest candidates are:
    head_name(::Type{<:MathOptInterface.SOS1}) at ~/.julia/packages/MathOptInterface/864xP/src/FileFormats/MOF/write.jl:326
    head_name(::Type{<:MathOptInterface.PowerCone}) at ~/.julia/packages/MathOptInterface/864xP/src/FileFormats/MOF/write.jl:324
    head_name(::Type{<:MathOptInterface.Semicontinuous}) at ~/.julia/packages/MathOptInterface/864xP/src/FileFormats/MOF/write.jl:277
    ...
  Stacktrace:
    [1] moi_to_object(set::MathOptInterface.ScalarAffineFunction{Int64}, #unused#::Dict{MathOptInterface.VariableIndex, String})
      @ MathOptInterface.FileFormats.MOF ~/.julia/packages/MathOptInterface/864xP/src/FileFormats/MOF/write.jl:260
    [2] moi_to_object(index::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Int64}, MathOptInterface.EqualTo{Int64}}, model::MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.MOF.InnerModelFunctionConstraints{Float64}}}, name_map::Dict{MathOptInterface.VariableIndex, String})
      @ MathOptInterface.FileFormats.MOF ~/.julia/packages/MathOptInterface/864xP/src/FileFormats/MOF/write.jl:110
    [3] write_constraints(object::OrderedCollections.OrderedDict{String, Any}, model::MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.GenericModel{Float64, MathOptInterface.Utilities.ObjectiveContainer{Float64}, MathOptInterface.Utilities.VariablesContainer{Float64}, MathOptInterface.FileFormats.MOF.InnerModelFunctionConstraints{Float64}}}, name_map::Dict{MathOptInterface.VariableIndex, String})
      @ MathOptInterface.FileFormats.MOF ~/.julia/packages/MathOptInterface/864xP/src/FileFormats/MOF/write.jl:70

so it would be great to generalize the MOF (and other file formats!) writers/readers. cc @odow

odow commented 1 year ago

We could add write support, but read support is tricky because existing file formats do not have the concept of a value type.

How would MOF know the difference between ScalarAffineFunction{Int64} and ScalarAffineFunction{Float64} if the coefficient was an integer?

Ditto for LP/MPS/CBF/etc.

We could add a new field like value_type, but this would need some careful thought. MOF is meant to be language agnostic, and it isn't obvious how you could generalize to arbitrary value types.

"function": {
    "type": "ScalarAffineFunction",
    "terms": [],
    "constant": 0,
    "value_type": "Int64",
}
chriscoey commented 1 year ago

I didn't really think about the read case - actually all I need personally is the write case.

If it was necessary to support reading, then maybe the type T should just be given once in the model, and numbers in the JSON should all be converted to that type when read (if possible, else error). But yeah I don't really need this.

odow commented 1 year ago

What's the point of write if you can't read?

chriscoey commented 1 year ago

We return model files to the user so they can inspect them manually 😆

odow commented 1 year ago

MOF files aren't that easy to inspect manually. Why not just pipe them the output of print(model)?