Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore namespace ideas #3791

Open
odow opened this issue Jul 29, 2024 · 5 comments
Open

Explore namespace ideas #3791

odow opened this issue Jul 29, 2024 · 5 comments

Comments

@odow
Copy link
Member

odow commented Jul 29, 2024

I just watched @blnicho's JuMP-dev talk https://youtu.be/G1tW68vrOBM. (Thanks Bethany! It was a very useful talk and exactly what I was after.)

It has come up in various discussions over the years, but one of the biggest differences between JuMP and Pyomo is how we handle namespacing.

JuMP

JuMP uses a single global namespace, and all objects are stored in the object_dictionary(model):

model = Model()
@variable(model, x)
model[:x] === x  # true
@variable(model, x)  # Errors. x already exists.

The downside is that for unrelated model parts to compose, you need to use anonymous variables, but this has poorer printing, and the macro syntax is more limited.

model = Model()
x = model[:x] = @variable(model, base_name = "x")
model[:x] === x  # true
x = model[:x2] = @variable(model, base_name = "x")

Pyomo

Pyomo has blocks. At a high level, blocks create a new namespace, and can be nested inside each other. You can delete/deactive entire blocks as a single component.

In Julia-land, we might do:

model = Model()
@block(model, blocks[1:2])
@variable(blocks[1], x)
blocks[1][:x] === x  # true
@variable(blocks[2], x)
blocks[1][:x] === x  # false: Julia binding `x` has been replaced in this scope
blocks[2][:x] === x  # true
@objective(model, Min, sum(b[:x] for b in blocks))

Next steps

I don't have a concrete syntax proposal, or believe that we should necessarily implement this, but I'd be open to exploring possibilities, and I'd very much like to come up with a few examples where JuMP's current syntax is limiting and the block/namespace would be beneficial.

I don't think this would change anything at the MOI level. This is strictly a JuMP-level feature.

I also don't know if this is that easy to explore in a JuMP-extension, but maybe it is. A prototype could store everything in model.ext[:block_extension] for now.

Some questions:

@metab0t
Copy link
Contributor

metab0t commented Jul 29, 2024

I think a straightforward way to implement this is to regard block as a proxy to model, otherwise JuMP needs to traverse the tree-like structure (or even a graph) to retrieve some item, which is quite complex. Maintaining model.ext[:block_extension] as a flat dict with key as block identifier is easier.

mutable struct Block
    model::Union{Model, Nothing}
    identifier::Symbol
end

@odow
Copy link
Member Author

odow commented Jul 29, 2024

otherwise JuMP needs to traverse the tree-like structure (or even a graph) to retrieve some item, which is quite complex

Pyomo keeps things as trees, and yes, they do tree-traversal operations.

@metab0t
Copy link
Contributor

metab0t commented Jul 29, 2024

I realize that maintaining a tree structure is necessary to record the hierarchical relations of blocks, but maybe their contents should still be stored in a flat dict for easier manipulation.

@odow
Copy link
Member Author

odow commented Jul 30, 2024

Another option is to explicitly be able to pass in a namespace:

struct Namespace
    data::Dict{Symbol,Any}
end
model = Model()
@variable(model, x >= 0)
@expression(model, blocks[1:2], Namespace())
@variable(model, x >= 1, namespace = blocks[1])
@variable(model, x >= 2, namespace = blocks[2])
@objective(model, Min, sum(b[:x] for b in model[:blocks]))

@odow
Copy link
Member Author

odow commented Aug 6, 2024

Maybe we don't even need to provide explicit Namespace objects, we just need a way for the user to choose where to store things in macros, and that it will use setindex!(object, value, ::Symbol)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants