PRONTO.jl is a Julia implementation of the Projection-Operator-Based Newton’s Method for Trajectory Optimization (PRONTO). PRONTO is a direct method for trajectory optimization which solves the optimal control problem directly in infinite-dimensional function space. It is capable of achieving quadratic convergence and has potential applications ranging from aerospace to quantum sensing.
At the core of the PRONTO algorithm is a nonlinear projection operator which maps curves to the system's trajectory manifold. Encoding the dynamics constraint through this operator creates an unconstrained optimization problem which is then solved via Newton descent. In the best case, PRONTO can achieve quadratic convergence. PRONTO has proven to be an effective tool for solving large quantum control problems, finding optimal satellite maneuvers, and studying the dynamics of high-performance vehicles.
In spite of its efficacy, previously existing implementations of PRONTO had a substantial learning curve. The algorithm requires the definition of Jacobians and Hessians of the dynamics and cost functions, all of which needed to be manually implemented in C++. Systems with tens of states necessitated the calculation of hundreds of derivatives - this process was rather work intensive and would often lead to mistakes hidden deep in byzantine code. Further analysis of results was typically done in MATLAB, and this two-language approach made it difficult to gain insights on some aspects of the algorithm's behavior. A pure MATLAB implementation of PRONTO was performance limited to small systems at low resolutions.
To increase the accessibility and usability of this powerful algorithm without sacrificing performance, Julia was a natural choice. Initially, we were expecting to accept a small reduction in performance in exchange for dramatically increased usability. However, thanks to insights gleaned from an array of profiling and code introspection tools created by the Julia community, we were able to not just match the performance of the C++ implementation, but substantially exceed it.
The majority of computations in the PRONTO algorithm consist of solving a series of about 10 ODEs, some forward, and some backward in time. Consequently, the performance of the internal ODE solver is extremely important. Rather than being limited to an explicit Runge-Kutta (4,5) algorithm, in PRONTO.jl we make use of the power and flexibility of DifferentialEquations.jl, and can adjust algorithms and settings to suit the requirements of the problem. Furthermore, the solver-aware interpolation methods used by DifferentialEquations.jl result in a more efficient representation of each ODE solution, storing less data while being more accurate and requiring fewer extra solution steps than a fixed-step linear interpolation.
PRONTO.jl utilizes the symbolic differentiation provided by Symbolics.jl to compute the necessary Jacobians and Hessians, and the resulting functions are then fine-tuned using Julia's metaprogramming and expression manipulation functionalities. Since PRONTO.jl can automatically generate substantial portions of its own code, the user only needs to provide 5 functions: the dynamics, stage and terminal costs, and a pair of regulator matrices. This greatly reduces the burden on the user, reduces implementation errors, and results in more readable code. Ultimately, this substantially cuts down on the time required to implement new models, which can now be spent exploring novel problems.
PRONTO.jl enables us to study a set of problems that were previously prohibitively difficult to implement, and more importantly makes this powerful tool accessible to a broader research community. We hope it will enable some exciting research!
PRONTO.jl is available at https://github.com/narijauskas/PRONTO.jl and we expect to add it to the general registry in the near future.