Open PierreMartinon opened 5 months ago
Questions, remarks, suggestions. Don't be shy.
Edit: footnote does not seem to work properly
I have a stagiaire making a webapp for our toolbox. Maybe we can brainstorm.
Questions, remarks, suggestions. Don't be shy. Nice @PierreMartinon not much to say right now
Edit: footnote does not seem to work properly footnote corrected 🙂 [^1]
[^1]: works like this
Thanks for the footnote help :D
Forgot to add a nice suggestion from Olivier: have several methods for the solution constructor depending on the available info in context, such as the ocp.
Also the JSON3 version is more an export than a save since the data is not identical (vectors vs functions), so I'll probably split the two at some point.
FYI, the two formats have been split, now we have
The JLD2 part should be ready to move to CTBase.
@PierreMartinon Is it done or note? Can we close the issue?
Both the load/save
in JLD2 and the export/read
in JSON (a more limited discrete version of the solution) are in the CTDirectExt
package extension. Some tests in test_misc.jl
I propose we wait a bit for the move into CTBase
, in particular I'd like a standard function to discretize an OptimalControlSolution
for text-like formats. Ideally we would find a way to get the exact discrete solution from the DOCP if possible & wanted: currently we start by interpolating the discrete solution, so even if we re-discretize later it would not necessarily be identical.
Maybe add optional vector arguments in OptimalControlSolution
that would save the original discrete variables if available (eg direct method) and left empty if not ? Exporting a discrete solution could then choose between this original one if present, or re-discretize along a given grid.
@PierreMartinon Do you need more than:
T = sol.times # T = [t0, t1, ...]
X = sol.state.(T) # X = [x0, x1, ...]
U = sol.control.(T)
P = sol.costate.(T)
and
x = ctinterpolate(T, ...)
p = ctinterpolate(T, ...)
u = ctinterpolate(T, ...)
@ocots I guess that ctinterpolate
uses Interpolation.jl and is not exported?
ctinterpolate
:
It is exported.
Actually, it is a linear interpolation with a linear extrapolation.
ctinterpolate
:It is exported.
Actually, it is a linear interpolation with a linear extrapolation.
should be kept internal according to me. minor point, though.
@PierreMartinon Do you need more than:
T = sol.times # T = [t0, t1, ...] X = sol.state.(T) # X = [x0, x1, ...] U = sol.control.(T) P = sol.costate.(T)
and
x = ctinterpolate(T, ...) p = ctinterpolate(T, ...) u = ctinterpolate(T, ...)
You lost me there :D
What I meant is, we currently have the functions state, control, costate
, and I'd like to have additional vector state_d, control_d, costate_d
, to store discrete trajectories. This discrete part would be used when exporting to JSON text format, instead of the discrete solution struct I currently use.
In the case of direct methods these vectors would store the original discrete solution, and in other cases it would simply default to apply the functions to a given time grid.
Ah, while we're on OptimalControlSolution
, here is the list of keys for the different constraints and multipliers (other than costate), since these vectors and functions are currently saved in the infos
dictionary of the solution. Maybe we could add explicit fields in the struct instead of dumping everything in the dictionary ? Note: I chose to return functions (ie interpolate) for everything related to path constraints, including 'boxes' on state and control variables, and keep vectors for boundary conditions and optimization variables constraints.
Vectors: :boundary_constraints, :mult_boundary_constraints, :variable_constraints, :mult_variable_constraints, :mult_variable_box_lower, :mult_variable_box_upper
Functions: :control_constraints, :mult_control_constraints, :state_constraints, :mult_state_constraints, :mixed_constraints, :mult_mixed_constraints, :mult_state_box_lower, :mult_state_box_upper, :mult_control_box_lower, :mult_control_box_upper
@jbcaillau On a slightly related topic, when building the boundary constraints in the OCP model, could we set the ordering to always be initial conditions first, followed by final conditions ? I noticed this is not necessarily the case while testing the constraints/multipliers parsing.
Updated the JSON part.
At some point we'll probably move these 4 functions from CTDirectExt
to an extension for CTBase
, since this is about manipulating OCP solutions.
Note: importing a solution in JSON (text) format requires the corresponding OCP as well, in practice for dimensions, and also because our OptimalControlSolution
contains a copy of the OCP. And the OCP is too complex to be saved in text format.
There is no copy of the ocp
inside an OptimalControlSolution
:
There is no copy of the
ocp
inside anOptimalControlSolution
:
Oh you're right, I read too quickly. So we only use the ocp to retrieve the block at line 45 here: https://github.com/control-toolbox/CTBase.jl/blob/76194968f5a33cdb047e94d5eb8b99f6d3c988fd/src/optimal_control_solution-setters.jl ?
If yes we could add a method taking a named tuple or something similar for this block instead of the ocp. The constructor with the ocp would call this new one, passing the values from the ocp in the tuple. And the new method could be called when the full ocp is not available, eg when reading from a json, since this data block could be saved as text !
@ocots Can you confirm this or did I miss something ?
Yes indeed, only for that:
# data from ocp
sol.initial_time_name = ocp.initial_time_name
sol.final_time_name = ocp.final_time_name
sol.time_name = ocp.time_name
sol.control_dimension = ocp.control_dimension
sol.control_components_names = ocp.control_components_names
sol.control_name = ocp.control_name
sol.state_dimension = ocp.state_dimension
sol.state_components_names = ocp.state_components_names
sol.state_name = ocp.state_name
sol.variable_dimension = ocp.variable_dimension
sol.variable_components_names = ocp.variable_components_names
sol.variable_name = ocp.variable_name
You can make an issue (Developers), create a branch from the issue and make a PR if you want :-)
Hi @ocots @jbcaillau,
While working on a GUI and also following a request from a user (Frank), I did a first version of save / load features. The idea is simply to be able to store an OCP solution on disk after a solve, and conversely to load an existing file. The main difficulty is that an OCP solution is a rather elaborate object, containing interpolated functions in particular. This complicates the export in a generic text-like format. Also, data i/o in Julia is still evoving quite a bit. For now I have 2 formats:
I put the first draft in CTDirect but this will eventually move to CTBase since the OCP solution is there. Usage looks like this
[^1]: this may be straightforward[^2] since I had already split the OCP Solution constructor in two: one called with the standard ipopt solution, and an internal one that takes raw vectors (T,X,U, multipliers etc) from a parsed solution. I'll try to see if we can use this second constructor starting from the JSON object containing the interpolated solution.
[^2]: (footnote test) Actually a bit more work is needed on the split to make the raw constructor even more generic, since it currently still takes the DOCP. it should be feasible as it is mostly for dimensions, although some boolean indicators regarding the constraints type are also used. I guess we can go back to manually checking for 'nothing' values in unused fields. I'll need to think a bit about this.