Can you control how indexes are printed when you print a model?

Can you control how JuMP prints indexes in print(model) beyond overriding Base.show? If not, this would be a nice, but not super-important feature.

Take as an example, an excerpt from the JuMP knapsack example, but instead we use structs:

struct Item
    name::String
    profit::Float64
    weight::Float64
end

items = [
    Item("TV", 5, 2),
    Item("Fridge", 3, 8),...
]

@variable(model, x[items], Bin)
...

If I print the model, elements of x will be printed as x[Item("TV", 5.0, 2,0)], which is ok.

I can do better by defining

function Base.show(io::IO, i::Item)
    print(io, i.name)
end

and then xs will be x[TV] in the print out of the model. The problem here is that as far as I know, there’s only one Base.show, and you can’t pass any formatting information to it, and in other situations I might wish to show an Item differently.

I work around this by doing things like putting the Items in a dictionary:

items = Dict(
    "TV" = Item("TV", 5, 2),...
)

@variable(model, x[keys(items)], Bin)

@constraint(model, sum(i.weight * x[key] for (key, i) in items) <= capacity)

But that makes the model unnecessarily complicated.

It would be really nice if there was something like a:

show_as_index(io:IO, x) = Base.show(io, x)

In JuMP so you could override show_as_index.

Are there any obvious solutions I am missing? This is a really minor gripe, but I’m working on some simple examples of using JuMP as tutorilals, and this would make the examples even simpler.

Thanks!

1 Like

There is no specific way to do this. JuMP interpolates the index as something similar to "x[$(index)]", so it depends on your show method.

However, you can write a loop calling set_name to explicitly set the name for all indices:

julia> struct Item
           name::String
       end

julia> items = [Item("a"), Item("b")]
2-element Vector{Item}:
 Item("a")
 Item("b")

julia> model = Model();

julia> @variable(model, x[items])
1-dimensional DenseAxisArray{VariableRef,1,...} with index sets:
    Dimension 1, Item[Item("a"), Item("b")]
And data, a 2-element Vector{VariableRef}:
 x[Item("a")]
 x[Item("b")]

julia> for i in items
           set_name(x[i], i.name)
       end

julia> x
1-dimensional DenseAxisArray{VariableRef,1,...} with index sets:
    Dimension 1, Item[Item("a"), Item("b")]
And data, a 2-element Vector{VariableRef}:
 a
 b

julia> 2x[items[1]] + x[items[2]]
2 a + b

Thanks Oscar!

You replied to that in between me fat-fingering the submit button (don’t hit tab when you’re writing code in Markdown…) and actually finishing my question. That works! (Also something to explain to newbies, but it does take some explanation).

It seems that the root problem is that you can’t pass formatting flags to show.

Thanks

(Fat fingered the submit again)

There are a few problems with this:

  • It wouldn’t generalize to base types like Int. So you wouldn’t be able to modify how x[1:4] printed
  • Even if you did, it would apply to all variables and constraints that those indices. So you couldn’t have x[1:3] and y[1:3] printed differently.
  • Multiple indices gets tricky. Do you want show_as_index(io, arg1, arg2, arg3) Or define the show separately for each argument?

It’s come up a few times that people would like a name keyword to use as a function that takes the indices as arguments and returns a string, but we haven’t worked through the details given calling set_name is pretty easy. (Another reason was that names in macros are already confusing with basename, the String name, and the registered name. Adding another thing would make it even more complicated.)