I am using InfiniteOpt and I would like to have a parameter time-dependent.
For example, in their “eating the cake” optimal consumption model, I would like to define the utility function with a multiplier that change with time:
using InfiniteOpt, Ipopt, Plots
ρ = 0.025 # discount rate
k = 100.0 # utility bliss point
T = 10.0 # life horizon
r = 0.05 # interest rate
B0 = 100.0 # endowment
mult1 = 1000 # multiplier of the first period
mult2 = 10 # multiplier of the second period
tswitch = 8 # time switch between multipliers
u(c,t; k=k) = t < tswitch ? mult1 * -(c - k)^2 : mult2 * -(c - k)^2 # utility function with the switch
discount(t; ρ=ρ) = exp(-ρ*t) # discount function
BC(B, c; r=r) = r*B - c # budget constraint
opt = Ipopt.Optimizer # desired solver
ns = 1_000; # number of points in the time grid
m = InfiniteModel(opt)
@infinite_parameter(m, t in [0, T], num_supports = ns)
@variable(m, B, Infinite(t)) ## state variables
@variable(m, c, Infinite(t)) ## control variables
@objective(m, Max, integral(u(c,t), t, weight_func = discount))
@constraint(m, B(0) == B0)
@constraint(m, B(T) == 0)
@constraint(m, c1, deriv(B, t) == BC(B, c; r=r))
optimize!(m)
termination_status(m)
c_opt = value(c)
B_opt = value(B)
ts = supports(t)
opt_obj = objective_value(m) # V(B0, 0)
ix = 2:(length(ts)-1) # index for plotting
plot(ts[ix], B_opt[ix], lab = "B: wealth balance")
plot!(ts[ix], c_opt[ix], lab = "c: consumption")
This snippet however returns me an TypeError: non-boolean (NLPExpr) used in boolean context. I have tried with other solutions (the time-dependent exogenous parameter is a vector) but with no success :-/
function u(c, t; k = k)
InfiniteOpt.ifelse(t < tswitch, mult1 * -(c - k)^2, mult2 * -(c - k)^2)
end
This is not particularly well documented.
I got there by recognizing that we probably wanted ifelse(t < tswitch, ...), and then I got an error WARNING: both InfiniteOpt and Base export "ifelse"; uses of it in module Main must be qualified that pointed me to InfiniteOpt.ifelse.
The recommended way to define time-dependent parameters in InfiniteOpt is via the use of parameter functions. See Expressions · InfiniteOpt.jl. These can only take infinite parameters as arguments, not variables.
For your objective, I would do the following:
using InfiniteOpt, Ipopt
# Parameters
ρ = 0.025 # discount rate
k = 100.0 # utility bliss point
mult1 = 1000 # multiplier of the first period
mult2 = 10 # multiplier of the second period
tswitch = 8 # time switch between multipliers
discount(t; ρ=ρ) = exp(-ρ*t) # discount function
# Model basics
m = InfiniteModel(Ipopt.Optimizer)
@infinite_parameter(m, t in [0, 10], num_supports = 1000)
@variable(m, c, Infinite(t))
# Time dependent coefficient
mult_func(t) = t < tswitch ? mult1 : mult2
@parameter_function(m, mult == mult_func(t))
# Objective
@objective(m, Max, integral(mult * -(c - k)^2, t, weight_func = discount))
Any opinions on how well this problem is solved with a discrete switch in the multiplier? I would naively expect the multi-stage approach of @Ronis_BR should work better, since the sudden change in multiplier should cause a jump in the states or their derivatives. A multi-stage problem can explicitly allow for that at the transitions between stages.
It is of course quite convenient to implement the switching function as part of the (continuous-time) objective. But infinite variables are continuous, and I’d expect them to have trouble representing a corner. Should be less accurate near the corner and/or require more support points. Is this expectation correct, or perhaps outweighed by other disadvantages to the multi-stage approach?
Yes! That’s was one of the problems I was facing. In my case, at some point, the rocket dynamics become completely different due to a stage separation and the new propellant behavior of the new stage. The multi-stage approach allowed me to model very complex situations.
Now I realize I haven’t understood what InfiniteOpt does.
It was my opinion that it was “just” automatically discretize some continuous variable, but doing that was breaking any relation between one value and the other, e.g. a “time” dimension that is discretized in time1, time2 and time3 would have been considered by the solver engine as a “set” with the 3 variables and treat them independently, without the need of a concept of derivative with respect to this variable. So, what else InfiniteOpt does other than the discretization ?
InfinieOpt does discretize the continuous problem, but those points are still related to each other through state derivatives. It’s not efficient otherwise.
For example, it could treat the states as unrelated, in a stair step pattern: a jump at each time point. The approximation would be poor unless the mesh very fine.
Much more efficient is to approximate the functions (infinite variables) with smooth curves like polynomials. Now many fewer points (supports) are needed, and the optimization tries to make sure the derivatives agree with the dynamics and with neighboring time points. But that suggests a jump in the dynamics is difficult to represent with smooth curves, hence my concern.
It’s conceivable that InfiniteOpt detects discontinuous dynamics. It could insert a corner point when necessary to avoid this problem. However IMO that’s hard to do, and not mentioned in the documentation.
OTOH DifferentialEquations has demos with steps in the inputs, which i believe introduces corners or jumps in the state variables. Naively I would expect this also to be poorly represented by smooth polynomials as well, but nobody seems to object. So i wonder if my understanding is wrong.