meco-group / fatrop

Fatrop is a nonlinear optimal control problem solver that aims to be fast, support a broad class of optimal control problems and achieve a high numerical robustness.
https://meco-group.github.io/fatrop/
GNU Lesser General Public License v3.0
133 stars 16 forks source link

Is it not possible to apply different constraints at each timestep? #12

Open kr-jschoi opened 4 months ago

kr-jschoi commented 4 months ago

I checked the examples using rockit and spectool that you provided, and it seems that unlike what is described in the paper, it is not possible to apply different constraints at each timestep (ustage). (It only allows applying the same constraints at t0, tf, and across all timesteps.) Is the ability to apply different constraints at each timestep a feature that is planned for development later?

lvanroye commented 4 months ago

Hello,

In rockit this is not possible, but in spectool it is.

Spectool is designed in such a way that the specification maps directly to the fatrop problem specification (of the paper). What you are looking for is ustages. A ustage contains all the functional quantities (dynamics, costf, constraints) that are related to a single time step. Any problem that fits into the fatrop formulation can be build up as a sequence of ustages.

consider the following python example which first defines a list of dynamics, costfs and constraints and then later builds up the spectool problem.

import casadi as cs
import fatropy.spectool as sp

ocp = sp.Ocp()

x = ocp.state(2)
u = ocp.control()
dt = 0.01
N = 100

xp1 = x + dt*cs.vertcat(x[1], u)

dyns = [xp1 for i in range(N-1)]
costs = [cs.sumsqr(x) + cs.sum1(x)*0.001*i for i in range(N)]
constr = [u>0.001*i for i in range(N)]

for dyni, costi, contri in zip(dyns[:-1], costs[:-1], constr[:-1]):
    ustagei = ocp.new_ustage()
    ustagei.set_next(x, dyni)
    ustagei.add_objective(costi)
    ustagei.subject_to(contri)

ustageN = ocp.new_ustage()
ustageN.subject_to(constr[-1])
ustageN.add_objective(costs[-1])

ocp.solver("fatrop", {"expand":True, "jit":True})  
ocp_fun = ocp.to_function("ocp", [], [ocp.sample(x)[1]])

print(ocp_fun())

For reference, in addition to the ustage (u means mu from micro) fatrop also has the notion of a stage. A stage (of length N) is inspired by the rockit framework and it is basically implemented as a sequence of three microstages: