OpenSim Moco
0.4.0
|
This solver uses the CasADi library (https://casadi.org) to convert the MocoProblem into a generic nonlinear programming problem.
CasADi efficiently calculcates the derivatives required to solve MocoProblems, and may solve your MocoProblem more quickly that MocoTropterSolver. In general, we hope that the feature sets of MocoCasADiSolver and MocoTropterSolver are the same. Note, however, that parameter optimization problems are implemented much less efficiently in this solver; for parameter optimization, first try MocoTropterSolver.
Direct collocation is fast because the derivative matrices (Jacobian and Hessian) in the optimization problem are extremely sparse. By default, CasADi determines the sparsity pattern of these matrices to be block patterns: the individual functions that invoke OpenSim are treated as dense, but this dense pattern is repeated in a sparse way. This is conservative because we ensure that no "nonzeros" are accidentally treated as "zeros." However, the problem may solve faster if we discover more "zeros."
See the optim_sparsity_detection setting for more information. In the case of "random", we use 3 random trajectories and combine the resulting sparsity patterns. The seed used for these 3 random trajectories is always exactly the same, ensuring that the sparsity pattern is deterministic.
To explore the sparsity pattern for your problem, set optim_write_sparsity and run the resulting files with the plot_casadi_sparsity.py Python script.
The "central" finite difference is more accurate but can be 2 times slower than "forward" (tested on exampleSlidingMass). Sometimes, problems may struggle to converge with "forward".
By default, CasADi evaluate the integral cost integrand and the differential-algebraic equations in parallel. This should work fine for almost all models, but if you have custom model components, ensure they are threadsafe. Make sure that threads do not access shared resources like files or global variables at the same time.
You can turn off or change the number of cores used for individual problems via either the OPENSIM_MOCO_PARALLEL environment variable (see getMocoParallelEnvironmentVariable()) or the parallel
property of this class. For example, if you plan to solve two problems at the same time on a machine with 4 cores, you could set OPENSIM_MOCO_PARALLEL to 2 to use all 4 cores.
Note that there is overhead in the parallelization; if you plan to solve many problems, it is better to turn off parallelization here and parallelize the solving of your multiple problems using your system (e.g., invoke the opensim-moco command-line tool in multiple Terminals or Command Prompts).
Note that the parallel
property overrides the environment variable, allowing more granular control over parallelization. However, the parallelization setting does not logically belong as a property, as it does not affect the solution. We encourage you to use the environment variable instead, as this allows different users to solve the same problem in their preferred way.
By default, MocoCasADiSolver is much slower than MocoTroperSolver at handling problems with MocoParameters. Many parameters require invoking Model::initSystem() to take effect, and this function is expensive (for CasADi, we must invoke this function for every time point, while in Tropter, we can invoke the function only once for every NLP iterate). However, if you know that all parameters in your problem do not require Model::initSystem(), you can substantially speed up your optimization by setting the parameters_require_initsystem property to false. Be careful, though: you will end up with incorrect results if your parameter does indeed require Model::initSystem(). To protect against this, ensure that you obtain the same results whether this setting is true or false.
#include <Moco/Moco/MocoCasADiSolver/MocoCasADiSolver.h>
Public Member Functions | |
OpenSim_DECLARE_PROPERTY (parameters_require_initsystem, bool, "Do some MocoParameters in the problem require invoking " "initSystem() to take effect properly? " "This substantialy slows down problems with parameter variables " "(default: true).") | |
OpenSim_DECLARE_PROPERTY (optim_sparsity_detection, std::string, "Detect the sparsity pattern of derivatives; 'none' " "(for safe block sparsity; default), 'random', or " "'initial-guess'.") | |
OpenSim_DECLARE_PROPERTY (optim_write_sparsity, std::string, "Write files for the sparsity pattern of the gradient, Jacobian, " "and Hessian to the working directory using this as a prefix; " "empty (default) to not write such files.") | |
OpenSim_DECLARE_PROPERTY (optim_finite_difference_scheme, std::string, "The finite difference scheme CasADi will use to calculate problem " "derivatives (default: 'central').") | |
OpenSim_DECLARE_OPTIONAL_PROPERTY (parallel, int, "Evaluate integral costs and the differential-algebraic " "equations in parallel across grid points? " "0: not parallel; 1: use all cores (default); greater than 1: use" "this number of threads. This overrides the OPENSIM_MOCO_PARALLEL " "environment variable.") | |
OpenSim_DECLARE_PROPERTY (output_interval, int, "Write intermediate trajectories to file. 0, the default, " "indicates no intermediate trajectories are saved, 1 indicates " "each iteration is saved, 5 indicates every fifth iteration is " "saved, etc.") | |
OpenSim_DECLARE_PROPERTY (minimize_implicit_multibody_accelerations, bool, "Minimize the integral of the squared acceleration continuous " "variables when using the implicit multibody mode. " "Default: false.") | |
OpenSim_DECLARE_PROPERTY (implicit_multibody_accelerations_weight, double, "The weight on the cost term added if " "'minimize_implicit_multibody_accelerations' is enabled." "Default: 1.0.") | |
OpenSim_DECLARE_PROPERTY (minimize_implicit_auxiliary_derivatives, bool, "Minimize the integral of the squared derivative continuous " "variables for components with implicit auxiliary dynamics. " "Default: false.") | |
OpenSim_DECLARE_PROPERTY (implicit_auxiliary_derivatives_weight, double, "The weight on the cost term added if " "'minimize_implicit_auxiliary_derivatives' is enabled." "Default: 1.0.") | |
Specifying an initial guess | |
MocoTrajectory | createGuess (const std::string &type="bounds") const |
Create a guess that you can edit and then set using setGuess(). More... | |
void | setGuess (MocoTrajectory guess) |
The number of time points in the trajectory does not need to match num_mesh_intervals ; the trajectory will be interpolated to the correct size. More... | |
void | setGuess (const std::string &type) |
Use this convenience function if you want to choose the type of guess used, but do not want to modify it first. More... | |
void | setGuessFile (const std::string &file) |
This clears any previously-set guess, if any. More... | |
void | clearGuess () |
Clear the stored guess and the guess_file if any. | |
const MocoTrajectory & | getGuess () const |
Access the guess, loading it from the guess_file if necessary. More... | |
Public Member Functions inherited from OpenSim::MocoDirectCollocationSolver | |
OpenSim_DECLARE_PROPERTY (num_mesh_intervals, int, "The number of uniformly-sized mesh intervals for the problem " "(default: " "100). If a non-uniform mesh exists, the non-uniform mesh is used " "instead.") | |
OpenSim_DECLARE_PROPERTY (verbosity, int, "0 for silent. 1 for only Moco's own output. " "2 for output from CasADi and the underlying solver (default: 2).") | |
OpenSim_DECLARE_PROPERTY (transcription_scheme, std::string, "'trapezoidal' for trapezoidal transcription, or 'hermite-simpson' " "(default) for separated Hermite-Simpson transcription.") | |
OpenSim_DECLARE_PROPERTY (interpolate_control_midpoints, bool, "If the transcription scheme is set to 'hermite-simpson', then " "enable this property to constrain the control values at mesh " "interval midpoints to be linearly interpolated from the control " "values at the mesh interval endpoints. Default: true.") | |
OpenSim_DECLARE_PROPERTY (multibody_dynamics_mode, std::string, "Multibody dynamics are expressed as 'explicit' (default) or " "'implicit' differential equations.") | |
OpenSim_DECLARE_PROPERTY (optim_solver, std::string, "The optimization solver to use (default: ipopt).") | |
OpenSim_DECLARE_PROPERTY (optim_max_iterations, int, "Maximum number of iterations in the optimization solver " "(-1 for solver's default).") | |
OpenSim_DECLARE_PROPERTY (optim_convergence_tolerance, double, "Tolerance used to determine if the objective is minimized " "(-1 for solver's default)") | |
OpenSim_DECLARE_PROPERTY (optim_constraint_tolerance, double, "Tolerance used to determine if the constraints are satisfied " "(-1 for solver's default)") | |
OpenSim_DECLARE_PROPERTY (optim_hessian_approximation, std::string, "When using IPOPT, 'limited-memory' (default) for quasi-Newton, or " "'exact' for full " "Newton.") | |
OpenSim_DECLARE_PROPERTY (optim_ipopt_print_level, int, "IPOPT's verbosity (see IPOPT documentation).") | |
OpenSim_DECLARE_OPTIONAL_PROPERTY (enforce_constraint_derivatives, bool, "'true' (default) or 'false', whether or not derivatives of " "kinematic constraints are enforced as path constraints in the " "optimal control problem.") | |
OpenSim_DECLARE_PROPERTY (minimize_lagrange_multipliers, bool, "If enabled, a term minimizing the weighted, squared sum of " "any existing Lagrange multipliers is added to the optimal control " "problem. This may be useful for imposing uniqueness in the " "Lagrange multipliers when not enforcing model kinematic " "constraint derivatives or when the constraint Jacobian is " "singular. To set the weight for this term use the " "'lagrange_multiplier_weight' property. Default: false") | |
OpenSim_DECLARE_PROPERTY (lagrange_multiplier_weight, double, "If the 'minimize_lagrange_multipliers' property is enabled, this " "defines the weight for the cost term added to the optimal control " "problem. Default: 1") | |
OpenSim_DECLARE_PROPERTY (velocity_correction_bounds, MocoBounds, "For problems where model kinematic constraint derivatives are " "enforced, set the bounds on the slack variables performing the " "velocity correction to project the model coordinates back onto " "the constraint manifold. Default: [-0.1, 0.1]") | |
OpenSim_DECLARE_PROPERTY (implicit_multibody_acceleration_bounds, MocoBounds, "Bounds on acceleration variables in implicit dynamics mode. " "Default: [-1000, 1000]") | |
OpenSim_DECLARE_PROPERTY (implicit_auxiliary_derivative_bounds, MocoBounds, "Bounds on derivative variables for components with auxiliary " "dynamics in implicit form. Default: [-1000, 1000]") | |
void | setMesh (const std::vector< double > &mesh) |
Sets the mesh to a, usually non-uniform, user-defined list of mesh points to sample. More... | |
Public Member Functions inherited from OpenSim::MocoSolver | |
MocoSolver (const MocoProblem &problem) | |
This calls resetProblem() with the provided problem. | |
Protected Member Functions | |
MocoSolution | solveImpl () const override |
std::unique_ptr< MocoCasOCProblem > | createCasOCProblem () const |
std::unique_ptr< CasOC::Solver > | createCasOCSolver (const MocoCasOCProblem &) const |
void | checkGuess (const MocoTrajectory &guess) const |
Check that the provided guess is compatible with the problem and this solver. More... | |
Protected Member Functions inherited from OpenSim::MocoDirectCollocationSolver | |
OpenSim_DECLARE_PROPERTY (guess_file, std::string, "A MocoTrajectory file storing an initial guess.") | |
OpenSim_DECLARE_LIST_PROPERTY (mesh, double, "Usually non-uniform, user-defined list of mesh points to sample. " "Takes precedence over uniform mesh with num_mesh_intervals.") | |
void | constructProperties () |
|
protected |
Check that the provided guess is compatible with the problem and this solver.
MocoTrajectory OpenSim::MocoCasADiSolver::createGuess | ( | const std::string & | type = "bounds" | ) | const |
Create a guess that you can edit and then set using setGuess().
The types of guesses available are:
const MocoTrajectory& OpenSim::MocoCasADiSolver::getGuess | ( | ) | const |
Access the guess, loading it from the guess_file if necessary.
This throws an exception if you have not set a guess (or guess file). If you have not set a guess (or guess file), this returns an empty guess, and when solving, we will generate a guess using bounds.
void OpenSim::MocoCasADiSolver::setGuess | ( | MocoTrajectory | guess | ) |
The number of time points in the trajectory does not need to match num_mesh_intervals
; the trajectory will be interpolated to the correct size.
If you have updated the problem since the solver was initialized, you may need to invoke MocoSolver::resetProblem() for the provided guess to be recognized as compatible with the problem. This clears the guess_file
, if one exists.
|
inline |
Use this convenience function if you want to choose the type of guess used, but do not want to modify it first.
void OpenSim::MocoCasADiSolver::setGuessFile | ( | const std::string & | file | ) |
This clears any previously-set guess, if any.
The file is not loaded until solving or if you call getGuess(). Set to an empty string to clear the guess file.