odow / SDDP.jl

A JuMP extension for Stochastic Dual Dynamic Programming
https://sddp.dev
Other
309 stars 61 forks source link

Anonymous JuMP Variables #250

Closed yakyak462 closed 4 years ago

yakyak462 commented 5 years ago

I want to store my state variables in dictionaries. In normal JuMP, you can store variables in dictionaries using anonymous variables. However, when I try this with SDDP, I think it is trying to interpret SDDP as a variable.

varDict[name] = @variable(subproblem, SDDP.State, lower_bound = 0, initial_value = initial_value[name])

ERROR: LoadError: UndefVarError: SDDP not defined

Am I misunderstanding something/is there a workaround?

odow commented 5 years ago

Anonymous state variables aren't supported. There is an issue for a better error message though: https://github.com/JuliaOpt/JuMP.jl/issues/1938.

The bigger question is why you need a dict? Does it not suffice to go

@variable(subproblem, x[name=NAMES], SDDP.State, lower_bound=0, initial_value=0)

@blegat, is there a way to have anonymous JuMP extension variables?

yakyak462 commented 5 years ago

The reason I was trying to use dictionaries is that I wanted "triangular indexing" (to use the language of the JuMP manual) as well as string indices. In my non-SDDP version of the program in Python, I used multi-layer dictionaries, e.g. varDict["A"]["B"]["X"]. It wasn't clear to me how to do this with SparseAxisArrays. I also considered using tuple indices ("A", "B", "X"), but this is also inconvenient because I frequently need to sum over some level [e.g. all ("A", "B", i) variables must sum to something].

By the way -- thank you for your ongoing help.

odow commented 5 years ago

Triangular indexing works with strings as well, so it should be possible.

using JuMP
m = Model()
A = ["a", "b"]
@variable(m, x[a = A, b=1:4, c=b:4])
JuMP.Containers.SparseAxisArray{VariableRef,3,Tuple{Any,Any,Any}} with 20 entries:
  [a, 1, 1]  =  x[a,1,1]
  [b, 1, 2]  =  x[b,1,2]
  [a, 1, 2]  =  x[a,1,2]
  [a, 3, 3]  =  x[a,3,3]
  [a, 2, 4]  =  x[a,2,4]
  [b, 2, 3]  =  x[b,2,3]
  [b, 2, 2]  =  x[b,2,2]
  [b, 2, 4]  =  x[b,2,4]
  [a, 2, 3]  =  x[a,2,3]
  [a, 1, 3]  =  x[a,1,3]
  [b, 4, 4]  =  x[b,4,4]
  [a, 2, 2]  =  x[a,2,2]
  [b, 3, 3]  =  x[b,3,3]
  [b, 3, 4]  =  x[b,3,4]
  [a, 4, 4]  =  x[a,4,4]
  [b, 1, 3]  =  x[b,1,3]
  [b, 1, 1]  =  x[b,1,1]
  [a, 1, 4]  =  x[a,1,4]
  [b, 1, 4]  =  x[b,1,4]
  [a, 3, 4]  =  x[a,3,4]
yakyak462 commented 5 years ago

For reference: @https://github.com/JuliaOpt/JuMP.jl/issues/2052 I got above to mostly work by defining separate functions f, g @variable(m, x[a = A, b=f(a), c=g(a, b)]).

The only other downside of this approach is that I need to do

@variable(m, x[a = A, b=f(a), c=g(a, b)], SDDP.State, initial_value = 0)
@variable(m, y[a = A, b=f(a), c=g(a, b)], SDDP.State, initial_value = 0)
@variable(m, z[a = A, b=f(a), c=g(a, b)], SDDP.State, initial_value = 0)
# Different constraints for x, y, z

Not the end of the world, but I was planning with the dictionaries to create a function to encapsulate this logic, which I don't know how to do with named JuMP variables. Namely, something like

function createVar(m, varName::String)
    @variable(m, varName[a = A, b=f(a), c=g(a, b)], SDDP.State, initial_value = 0)
end
odow commented 5 years ago

Does this work?

function create_variable(model, var_name)
    x = @variable(
        model, 
        [a = A, b=f(a), c=g(a, b)], SDDP.State, 
        initial_value = 0, base_name = "$var_name_$a_$b_$c")
    model[Symbol(var_name)] = x
    return x
end

It's not obvious that you need an encapsulating function for the sake of the three lines you have above. One reason that I haven't developed better support is that the number of state variables should be modest. Having x, y, and z each be indexed by three things is starting to look a little scary in terms of what the algorithm can handle.

yakyak462 commented 5 years ago

Thanks! A good to know about state variable constraints, may have to lower scope of problem. Will try it out.

odow commented 5 years ago

A good to know about state variable constraints, may have to lower scope of problem. Will try it out.

Yes, try it out. It can be problem-specific as hit depends on a few things. The current library is only single-threaded. It's possible to write a parallel version which gives big speed-ups, but I don't have the development time at the moment.

A useful thing to remember is that SDDP works by approximating a convex function of the state-variables. If you have a lot of state variables, then we're approximating a high-dimensional function and that's computationally expensive.

odow commented 4 years ago

Closing since this is an upstream issue.

davide-f commented 3 years ago

Dear developers,

I reopen this issue because I would like to reuse the above but for a single variable only, such as: x = @variable( model, ?, SDDP.State, initial_value = 0, base_name = "$var_name")

the trick I found is to use x = @variable( model, _, SDDP.State, initial_value = 0, base_name = "$varname") unregister(model, :)

Are there more "clean ways"?

odow commented 3 years ago

Why do you need anonymous state variables?

davide-f commented 3 years ago

To create a flexible model where the model of each component is constructed "on-demand" based on external files, but the type of model is not known in advance.

odow commented 3 years ago

Please open a new issue with a minimal working example of what you are trying to achieve and we can scope out what to do.