Skip to content

Skip Symbolic Differentiation

Although symbolic differentiation is convenient for most problems, you may want to skip it for the following reasons:

  • The problem cannot be simply described as symbolic expressions
  • There are other more suitable automatic differentiation methods
  • Functions need to call pre-compiled external libraries
  • You want to call compiled functions directly for better performance

Setup Method

If you need to skip symbolic differentiation for certain functions, the method is similar to using the cache feature (see Compilation Is Very Slow). Simply place the manually edited function files in a specified folder, then set the cache parameter to that folder path in the set_* functions. Pockit will directly load these functions, thereby skipping symbolic differentiation.

For example, to replace the first state dynamics, prepare cache/dynamic_0.py and pass cache="cache" to set_dynamics. Pockit loads that file instead of generating it from the supplied symbolic expression. The filenames are:

Function Filename
Phase.set_dynamics dynamic_{i}.py
Phase.set_integral integral_{i}.py
Phase.set_phase_constraint phase_constraint_{i}.py
Phase.set_boundary_condition boundary_condition_0_{i}.py,
boundary_condition_f_{i}.py,
boundary_condition_t_0.py,
boundary_condition_t_f.py
System.set_objective objective.py
System.set_system_constraint system_constraint_{i}.py

File Format

The provided Python files should contain vector functions F, G, H and variables G_index, H_index_row, H_index_col. These functions and variables are defined as follows (also see the API documentation):

  • Functions F, G, and H take a one-dimensional NumPy array x and an integer l. For a scalar function \(f(z_0,\ldots,z_{m-1})\), x stores l evaluation points in parameter-major order:

$$ x=[z_0^{(0)},\ldots,z_0^{(l-1)},z_1^{(0)},\ldots,z_{m-1}^{(l-1)}]. $$

The value function therefore satisfies

$$ F(x,l)j=f!\left(z_0^{(j)},\ldots,z\right), \qquad j=0,\ldots,l-1. $$}^{(j)

  • F returns a one-dimensional array of length l. G returns an array with shape (len(G_index), l), one row for each stored gradient entry. H returns an array with shape (len(H_index_row), l), one row for each stored Hessian entry.
  • G_index, H_index_row, and H_index_col are integer NumPy arrays that identify the nonzero derivative entries returned by G and H. H_index_row and H_index_col have equal length and contain only lower-triangular indices because the Hessian is symmetric. G_index must be strictly increasing. H_index_row must be nondecreasing, and H_index_col must be strictly increasing among entries that share the same row.

Example files can be found in the generated cache files.

Given a system with \(n_x\) state variables, \(n_u\) control variables, \(n_i\) integrals, and \(n_s\) static parameters, the input parameters and dimensions for different functions are shown in the following table:

Function Input Parameters Input Dimension \(m\)
Phase.set_dynamics \(x\), \(u\), \(t\), \(s\) \(n_x + n_u + 1 + n_s\)
Phase.set_integral \(x\), \(u\), \(t\), \(s\) \(n_x + n_u + 1 + n_s\)
Phase.set_phase_constraint \(x\), \(u\), \(t\), \(s\) \(n_x + n_u + 1 + n_s\)
Phase.set_boundary_condition \(s\) \(n_s\)
System.set_objective \(I\), \(s\) \(n_i + n_s\)
System.set_system_constraint \(I\), \(s\) \(n_i + n_s\)