Mimi Framework

Optimizing MimiDICE

Hey there,

I am trying to optimize the welfare in MimiDICE2016 using the mitigation rate with OptiMimi and stayed closely to the description of OptiMimi here:

This is the code I used (m_opti is the DICE2016 model):

using OptiMimi

setting up the optimization problem

function objective(model::Model)
m_opti[:welfare, :CUMCEMUTOTPER]
end

constraint = [model → sum(model.components[:emissions].Parameters.MIU) - 1]

optprob = problem(m_opti, [:emissions], [:MIU], [0.], [1.], objective, constraints=constraint)

solving the optimization problem

(maxf, maxx) = solution(optprob, () → [0. for i in 1:100])
println(maxf)
println(maxx)

I get an error about the constraint that I am unsure how to solve:
TypeError: in keyword argument constraints, expected Array{Function,1}, got a value of type Array{var"#83#84",1}

And when I leave out the constraint, I get this error:
Cannot set parameter :MIU, the model already has an external parameter with this name. Use update_param(m, param_name, value) to change the value, or use set_param(m, comp_name, param_name, unique_param_name, value) to set a value for only this component.

I would be glad if you could help me with this, probably it’s just a minor problem.
Or could you guide me to a paper or so where you optimized a Mimi Model possibly also with another package than OptiMimi?

Thanks again for your awesome work on making these models open source usable!

Hello @MalinW thank you for your interest and question! The Mimi.jl team does not directly oversee OptiMimi.jl, so I may suggest you reach out to @jrising on his repo with a Github Issue (you could just link to this post so you don’t have to retype it :slight_smile: ). That said, I can also reach out to him myself and I’ll take some time today to dig into this, it might be that I can solve the issue if it is an underlying problem with OptiMimi.jl being updated to the latest Mimi.jl version.

@MalinW Thanks for reaching out! I recognize this error from recent(ish) changes to Mimi. The standard optimization mechanism in OptiMimi has not be fully updated for the most recent version of Mimi, which requires that set_param only be used once, and then only update_param from then on.

I’ve been advocating for removing this restriction on the use of set_param, and it would be useful to know when this might be able to happen from @lrennels as I make changes.

But I also want to make sure that OptiMimi is useable for this case. @MalinW, do you have any other code that goes with the code you posted above, so I can run your use case?

Thanks for your answers @lrennels and @jrising. I wasn’t working the last few days, so I didn’t get to answer faster to this.

So this is the full code (the part of adding packages is commented out):

#add Mimi registry (registry with the Mimi models), Mimi, MimiDICE2016 and OptiMimi
#] registry add https://github.com/mimiframework/MimiRegistry.git
#] add Mimi
#] add https://github.com/AlexandrePavlov/MimiDICE2016.jl
#] add OptiMimi

#or update packages
#] update

# use the model
using MimiDICE2016
# access the public API of MimiDICE2016 with MimiDICE2016.get_model, which returns a copy of the default model
m_opti = MimiDICE2016.get_model() 
# run the model
run(m_opti)


using Mimi
using OptiMimi

# setting up the optimization problem

function objective(model::Model)
    m_opti[:welfare, :CUMCEMUTOTPER]
end

constraint = [model -> sum(model.components[:emissions].Parameters.MIU) - 1]
    
optprob = problem(m_opti, [:emissions], [:MIU], [0.], [1.], objective
#    , constraints=constraint
    )

# solving the optimization problem

(maxf, maxx) = solution(optprob, () -> [0. for i in 1:100])
println(maxf)
println(maxx)

setparameters(m_opti, [:emissions], [:MIU], maxx)

and as I already said there seems to be problem with my constraint (thats why it’s commented out in the optimization problem), but also with the set_param function.

Thanks @MalinW, @jrising and I will look at this, I see two probable issues although I don’t know OptiMimi very well.

First of all, the following way to dive into the parameter values isn’t going to work anymore, it accesses internal storage that isn’t organized like this anymore.

sum(model.components[:emissions].Parameters.MIU) 

Is there a reason the example doesn’t use the public API version ie. something like the following?

sum(model[:emissions, :MIU])

Second of all, as you already pointed out, the second issue is specific to calling set_param! using an existing parameter name. I can’t promise right now that we are fully getting rid of the distinction between update and set parameter, but David and I are meeting Monday to discuss how to make this easier for users since I know you’ve expressed frustration about that, so I’ll try to keep you looped in to that conversation which will be worked on here.

That said (as you probably know) in I think in the case above with the current framework one needs to make only calls to update_param! in the internals since

using MimiDICE2016
m_opti = MimiDICE2016.get_model() 
run(m_opti)

has set MIU from within get_model().

Okay, I think I’ve got it working, but it’s a lot of parameters to optimize, so I haven’t gotten a result yet.

First, there were a couple changes needed to OptiMimi:

  • I’ve currently fixed the set_param! vs. update_param! by assuming that the external parameter matches the parameter name. This means that the current fix will not work well if there are multiple components that define the same parameter-- but I don’t think that’s a problem for your case.
  • The -> operator, used in defining constraints, returns a subclass of Function, so I needed to change the expected argument there. No problem, now fixed. So you should be able to use constraints.

I haven’t published these changes to the Mimi package repository yet, but I think you can use

] develop OptiMimi

to check out the master branch. If it works for you, we’ll push it out.

There were also a couple changes needed to your objective function. I’ve defined it as:

function objective(model::Model)
    model[:welfare, :CUMCEMUTOTPER][100]
end

So, you want to use model, not m_opti, and you need to return a single value. I think that should just be the last value in this case.

Let me know how that works for you!

@jrising if you’ve pushed to the master branch but just haven’t tagged, @MalinW you may want to just use

julia >  ]
pkg  > add OptiMimi#master

that way you can use the master branch code without pulling in the whole repository as if you’re going to develop it! Or of course just wait for @jrising to tag the new version and then just call

julia> ]
pkg> up

to update all packages including OptiMimi.jl.

I’m monitoring the forum closely these days so both of you do feel free to keep commenting here if there are issues or questions I’m happy to help!

Just to follow up on @lrennels’s comment: Yes, it’s on the master branch, so definitely use the add OptiMimi#master syntax. I was planning on waiting until you (@MalinW) have a chance to test it before tagging a new release.

1 Like

Thank you both so much @lrennels and @jrising for all your effort! This already helped a lot, I got the optimization running without the constraint!

Unfortunately, I can’t get the constraint to work, but that might also be due to my not (yet) great julia skills. I just followed the example that was given on the readme file from OptiMimi.
I created an example that should run through faster with just a few timesteps:

#add Mimi registry (registry with the Mimi models), Mimi, MimiDICE2016 and OptiMimi
#] registry add https://github.com/mimiframework/MimiRegistry.git
#] add Mimi
#] add https://github.com/AlexandrePavlov/MimiDICE2016.jl
#] add OptiMimi#master

#or update packages
#] update

# use the model
using MimiDICE2016
# access the public API of MimiDICE2016 with MimiDICE2016.get_model, which returns a copy of the default model
m_opti = MimiDICE2016.get_model() 
# run the model
run(m_opti)
# make model shorter for faster computing:
using Mimi
const years = collect(2015:5:2035)
set_dimension!(m_opti, :time, years)


# setting up the optimization problem

using OptiMimi

function objective(model::Model)
    model[:welfare, :CUMCEMUTOTPER][5]
end

constraint = [model -> sum(model[:emissions,:MIU]) - 1]
    
optprob = problem(m_opti, [:emissions], [:MIU], [0.], [1.2], objective
    , constraints=constraint
    )

# solving the optimization problem

(maxf, maxx) = solution(optprob, () -> [0. for i in 1:5])
println(maxf)
println(maxx)

update_param!(m_opti, :MIU, maxx)

The error I get after the line with “optprob=…” reads:

ERROR: TypeError: in keyword argument constraints, expected Vector{Function}, got a value of type Vector{var"#1#2"}

I would be very glad if you could help me with that. I already tried looking it up and tried some different formulations for the constraint, but there remained an error. Also I would like to let the constraint hold only for a few time periods, but I guess I get that implemented after I get a constraint working.

Hi @MalinW, I’m a bit out of my element on this package since it’s mostly maintained by @jrising but looking at your code I do think you need Miu to be a symbol in this line:

constraint = [model -> sum(model[:emissions,MIU]) - 1]

so I should be

constraint = [model -> sum(model[:emissions,:MIU]) - 1]

But I know you said you tried some different formulations so you may have already caught that!

Ups, yes, this was a mistake that sneaked in after I tried some different formulations, but the mistake stays the same with the colon. But thanks a lot for pointing me towards it! :slight_smile:
So I changed the code above to include the colon.

Hi @MalinW I just wanted to check in to ask how this is going and if you still need assistance?

@lrennels that is really kind of you! I still can’t get the constraints running, but I am also just a graduate research assistat, so not working many hours and focused on updating some components (and some parameters) in the last few weeks.

If I still can’t get the constraint running with OptiMimi, I will probably try using another function to do the optimization or maybe my colleague will look into it. Are there any other resources/example codes in the Mimi context I could turn to?

Or could I also write constraints as normal functions into the components?

By the way, I would also love to contribute to this community in the future, maybe I could write something on Optimization after I get this done eventually?

Hi @MalinW alright got it, if @jrising has any advice I’ll ask him to post it here as the optimization is outside the core Mimi wheelhouse so not my expertise, but I hope hasn’t been too frustrating for you! Writing constraints as normal functions into the components may work if they’re not too complex and thus you won’t hit painful performance issues? I’m not sure how complicated the ones you are using are :slight_smile:

We always welcome collaboration on Mimi and the helper packages like OptiMimi, so certainly stay in touch about what you might like to work on and we’d be happy to talk through ideas with you etc.

Hi @MalinW, can you say more about what happens when you try to run it with the constraints? It works fine for me with

function objective(model::Model)
    model[:welfare, :CUMCEMUTOTPER][100]
end

constraint = [model -> sum(model[:emissions, :MIU]) - 1]

optprob = problem(m_opti, [:emissions], [:MIU], [0., 0.], [1., 1.], objective, constraints=constraint)

(maxf, maxx) = solution(optprob, () -> [0. for i in 1:100])

That said, it doesn’t complete for a very long time (> 24 hours). But that’s a theoretical issue with how you’ve set up your optimization problem: the search space is too big for such a slow model.

When I use your same constraint but reduce it to 2 parameters, according to the parameterized expression MIU = alpha exp(-beta (100 - i)), it takes about 2.3 seconds to solve.

1 Like

Hi @jrising,
thanks for getting back to me! First a question: why did you use two lower and two upper bounds here? Before we only used one each in the code.
I get the same error when using one or two lower and upper bounds, that I described here (also with an example with just 5 time periods that runs through quickly without the constraint):

So I always get this error:

TypeError: in keyword argument constraints, expected Array{Function,1}, got a value of type Array{var"#1#2",1}

I really do not understand what is happening here if it only produces an error for me and works for you.

I’ll let @jrising respond but @MalinW can you post a bit more of the error message here? Perhaps the full Stacktrace so we can pinpoint the error source?

The two upper and lower bounds were left-over from when was running my fast test. It should have just been [0.], [1.], but it doesn’t matter, since only the first entry is used by the code above.

Okay, so your error is happening because [model -> sum(model[:emissions, :MIU]) - 1] is not being recognized as a vector of functions by your version of Julia. It works on mine, and as far as I know should generally work. As a test, you can try:

convert(Vector{Function}, [model -> sum(model[:emissions, :MIU]) - 1])

What version of Julia are you using? I’m on 1.5.2.

We might also be able fix this within OptiMimi, by asking for a vector of things things that are related to Functions, rather than a vector of Functions themselves.

So the full error code (Sorry for my incomplete posting, I wasn’t aware that that actually could help) from Jupyter notebook:

Stacktrace:
 [1] top-level scope
   @ In[1]:32
 [2] eval
   @ .\boot.jl:360 [inlined]
 [3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
   @ Base .\loading.jl:1094

line 32 refers to the constraint inside the optimization problem (so second line here):

optprob = problem(m_opti, [:emissions], [:MIU], [0.], [1.2], objective
    , constraints=constraint
    )

which was defined like this:

constraint = [model -> sum(model[:emissions, :MIU]) - 1]

@jrising I’m using Julia 1.6.0 and I think you found the solution. When assigning your code to the constraint, it worked. I am sooo happy right now, thanks sooo much for the help! And I should definitely work on my basic Julia skills ;).

One more question: do you know or have tried a way to use these constraints only for certain time periods? If not, no worries, I will try to figure out myself/with the help of my colleague or write them directly into my adjusted model.

Thanks again to both of you @jrising and @lrennels!

Happy to hear it! Don’t worry, the concept of a vector of functions isn’t a simple one so you’re doing just fine.