Cart-pole: stabilizing an unstable equilibrium
The model is the declared cart-pole from models/cart_pole.py:
an inverted pendulum on a cart, four states, one force input, starting from a
40-degree tilt. The upright target is an unstable equilibrium, the regime
where short-horizon NMPC visibly fails and terminal machinery earns its keep.
Two cases of the same model. Case 1 runs five sampling steps with the
infinite-horizon terminal segment (drto.infinite_horizon,
Dinh et al. 2025) carrying
the tail, which deactivates the declared terminal cost since the tail owns
the cost-to-go. Case 2 runs fifty steps of plain finite horizon with the
terminal cost live. The pole is fast relative to the one-second sampling, so
both cases discretize with five collocation points per element.
Case 1: five samples plus the infinite-horizon tail
********************************************************************************
#### ### / # /# #/ #### #####
# # # # /# #/ ## / # #
#### # #/ # / # #/# # ####
# # / # /# # /## # #
# ##/ #/# #/ # #### #####
********************************************************************************
This program contains POUNCE, a pure-Rust interior-point optimization solver
for nonlinear, conic, and global problems (its NLP core is ported from Ipopt).
Released under the Eclipse Public License (EPL) — drop-in compatible with Ipopt.
For more information visit https://github.com/jkitchin/pounce
********************************************************************************
This is POUNCE version 0.10.0, running with linear solver FERAL.
Reading C:\Users\Devin\AppData\Local\Temp\tmplgs4iuwv\unknown9kmwopo0.nl...
Parsed 287 vars, 267 cons, jac_nnz=1606, h_nnz=266 in 0.02s
Problem class: NLP. Selected solver: NLP filter line-search interior-point (pounce-nlp) [solver_selection=auto].
Number of nonzeros in equality constraint Jacobian...: 1586
Number of nonzeros in inequality constraint Jacobian.: 20
Number of nonzeros in Lagrangian Hessian.............: 266
Total number of variables............................: 287
variables with only lower bounds: 8
variables with lower and upper bounds: 149
variables with only upper bounds: 0
Total number of equality constraints.................: 263
Total number of inequality constraints...............: 4
inequality constraints with only lower bounds: 0
inequality constraints with lower and upper bounds: 4
inequality constraints with only upper bounds: 0
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 3.9040898e+05 1.60e+03 7.73e-01 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 2.1641846e+04 2.34e+03 1.27e+02 -1.0 4.49e+03 - 9.38e-02 5.21e-01f 1
2 1.8445620e+04 2.17e+03 1.18e+02 -1.0 2.31e+03 - 4.43e-01 7.19e-02h 1
3 2.9852072e+03 5.86e+02 1.04e+01 -1.0 2.16e+03 - 6.39e-01 8.72e-01h 1
4 5.6215611e+03 1.08e+01 2.06e+00 -1.0 5.78e+02 - 9.04e-01 1.00e+00h 1
5 5.8060678e+03 3.15e+00 9.75e-02 -1.0 4.70e+01 - 9.75e-01 1.00e+00h 1
6 5.4919846e+03 9.59e+01 2.17e-02 -1.0 2.14e+02 - 9.98e-01 1.00e+00f 1
7 5.6103968e+03 1.16e+01 3.33e-03 -1.0 9.89e+01 - 1.00e+00 1.00e+00h 1
8 3.7358229e+03 8.45e-01 9.84e-04 -1.7 1.30e+01 - 1.00e+00 1.00e+00h 1
9 2.8649417e+03 3.01e+00 7.57e-04 -2.5 2.05e+01 - 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
10 2.7200894e+03 9.27e-01 2.09e-05 -2.5 1.27e+01 - 1.00e+00 1.00e+00h 1
11 2.6170314e+03 1.82e-01 3.86e-05 -3.8 5.19e+00 - 1.00e+00 1.00e+00h 1
12 2.6137740e+03 3.48e-03 6.69e-08 -3.8 7.88e-01 - 1.00e+00 1.00e+00h 1
13 2.6078132e+03 9.50e-04 1.53e-07 -5.7 3.89e-01 - 1.00e+00 1.00e+00h 1
14 2.6078026e+03 6.31e-08 1.85e-11 -5.7 3.12e-03 - 1.00e+00 1.00e+00h 1
15 2.6077284e+03 1.50e-07 2.39e-11 -8.6 4.92e-03 - 1.00e+00 1.00e+00h 1
pounce: wrote C:\Users\Devin\AppData\Local\Temp\tmplgs4iuwv\unknown9kmwopo0.sol
Number of Iterations....: 15
(scaled) (unscaled)
Objective...............: 5.8259174334481034e-01 2.6077284235805473e+03
Dual infeasibility......: 2.3896653507485739e-11 1.0696338094692884e-07
Constraint violation....: 3.2771265228348642e-09 1.5021241495105642e-07
Variable bound violation: 0.0000000000000000e+00 0.0000000000000000e+00
Complementarity.........: 2.6974195762806260e-09 1.2073871248165611e-05
Overall NLP error.......: 3.2771265228348642e-09 1.2073871248165611e-05
Number of objective function evaluations = 16
Number of objective gradient evaluations = 16
Number of equality constraint evaluations = 16
Number of inequality constraint evaluations = 16
Number of equality constraint Jacobian evaluations = 16
Number of inequality constraint Jacobian evaluations = 16
Number of Lagrangian Hessian evaluations = 16
Total seconds in POUNCE = 0.022
EXIT: Optimal Solution Found.
POUNCE 0.10.0: Optimal Solution Found.
Status: Solve_Succeeded
<pyomo.contrib.solver.common.results.Results at 0x1b24ce67390>
drto registry| states: 4, controls: 1 |
| horizon | t (ContinuousSet, 26 points) |
| states | x (free), x_dot (free), theta (free), theta_dot (free) |
| dynamics | dx[t] == x_dot[t] for t in t |
| dynamics | dx_dot[t] == (m_p*g*sin(theta[t])*cos(theta[t]) - (1 + kJ)*(F[t] + m_p*l*theta_dot[t]**2*sin(theta[t]) - b*x_dot[t]))/(m_p*cos(theta[t])**2 - (1 + kJ)*m_c) for t in t |
| dynamics | dtheta[t] == theta_dot[t] for t in t |
| dynamics | dtheta_dot[t] == (m_c*g*sin(theta[t]) - cos(theta[t])*(F[t] + m_p*l*theta_dot[t]**2*sin(theta[t])))/((1 + kJ)*m_c*l - m_p*l*cos(theta[t])**2) for t in t |
| controls | F (piecewise_constant, free) |
| tracking stage cost | cost[t] == (theta[t] - theta_ss)**2/theta_scale**2 + 0.01*(x[t] - x_ss)**2 + 0.01*(x_dot[t] - x_dot_ss)**2 + 0.01*(theta_dot[t] - theta_dot_ss)**2/theta_scale**2 + 0.1*(F[t] - F_ss)**2 for t in sorted(t)[:-1] |
| terminal cost | term == (theta[5] - theta_ss)**2/theta_scale**2 + 0.01*(x[5] - x_ss)**2 + 0.01*(x_dot[5] - x_dot_ss)**2 + 0.01*(theta_dot[5] - theta_dot_ss)**2/theta_scale**2 |
| initial conditions | x[0] == x_hat |
| initial conditions | x_dot[0] == x_dot_hat |
| initial conditions | theta[0] == theta_hat |
| initial conditions | theta_dot[0] == theta_dot_hat |
| steady-state targets | x_ss (of x), x_dot_ss (of x_dot), theta_ss (of theta), theta_dot_ss (of theta_dot) |
| steady-state control targets | F_ss (of F) |
transformationsdrto.infinite_horizon: segment=3 elements x 5 Legendre points, beta=1.2, gamma=0.01563827, profile=collocation, horizon=kept, infinite tail appended, terminal_cost=terminal deactivated (the tail owns it), terminal=soft pin z(tau=1)=z_s on 4 states, mu=1000.0drto.parameterize: profiles=F (piecewise_constant)drto.build_objective: objective=sum of 36 weighted cost terms
Case 2: fifty samples, finite horizon with the terminal cost
********************************************************************************
#### ### / # /# #/ #### #####
# # # # /# #/ ## / # #
#### # #/ # / # #/# # ####
# # / # /# # /## # #
# ##/ #/# #/ # #### #####
********************************************************************************
This program contains POUNCE, a pure-Rust interior-point optimization solver
for nonlinear, conic, and global problems (its NLP core is ported from Ipopt).
Released under the Eclipse Public License (EPL) — drop-in compatible with Ipopt.
For more information visit https://github.com/jkitchin/pounce
********************************************************************************
This is POUNCE version 0.10.0, running with linear solver FERAL.
Reading C:\Users\Devin\AppData\Local\Temp\tmpcnu0y99m\unknownqnjk4oo9.nl...
Parsed 1603 vars, 1553 cons, jac_nnz=9535, h_nnz=1400 in 0.03s
Problem class: NLP. Selected solver: NLP filter line-search interior-point (pounce-nlp) [solver_selection=auto].
Number of nonzeros in equality constraint Jacobian...: 9535
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 1400
Total number of variables............................: 1603
variables with only lower bounds: 0
variables with lower and upper bounds: 800
variables with only upper bounds: 0
Total number of equality constraints.................: 1553
Total number of inequality constraints...............: 0
inequality constraints with only lower bounds: 0
inequality constraints with lower and upper bounds: 0
inequality constraints with only upper bounds: 0
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 0.0000000e+00 1.60e+03 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 -4.7831606e+03 1.62e+03 1.65e+03 -1.0 1.60e+03 -2.0 1.66e-01 1.97e-01f 1
2 -4.9386073e+03 1.62e+03 1.64e+03 -1.0 1.29e+03 -2.5 3.00e-01 5.26e-03f 1
3 -8.1285305e+03 1.56e+03 1.57e+03 -1.0 3.15e+03 -3.0 2.46e-01 3.76e-02f 1
4 -1.0147716e+04 1.45e+03 1.45e+03 -1.0 1.23e+03 -2.5 5.17e-01 7.34e-02f 1
5 -1.1328805e+04 1.44e+03 1.54e+03 -1.0 5.98e+04 -3.0 7.33e-03 3.58e-03f 1
6 -1.1918021e+04 1.41e+03 1.50e+03 -1.0 1.50e+03 -2.6 4.80e-01 2.14e-02f 1
7 -1.4883800e+04 1.40e+03 1.48e+03 -1.0 1.48e+04 -3.1 9.28e-03 1.17e-02f 1
8 -1.5327527e+04 1.38e+03 1.45e+03 -1.0 1.38e+03 -2.6 5.19e-02 1.77e-02f 1
9 -1.5878497e+04 1.29e+03 1.37e+03 -1.0 1.38e+03 -2.2 4.79e-04 6.35e-02f 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
10 -2.3601865e+04 1.09e+03 1.36e+03 -1.0 1.85e+03 -2.7 3.81e-02 1.94e-01f 1
11 -2.3464372e+04 1.06e+03 1.42e+03 -1.0 3.56e+03 -2.3 7.84e-03 2.66e-02h 1
12 -2.2478682e+04 9.79e+02 1.08e+03 -1.0 1.00e+03 -1.8 8.92e-02 8.15e-02h 1
13 -2.2250073e+04 9.61e+02 1.06e+03 -1.0 9.47e+02 -1.4 2.62e-02 1.86e-02h 1
14 -1.8890541e+04 7.58e+02 2.12e+03 -1.0 9.32e+02 -1.9 7.86e-04 3.27e-01h 1
15 -1.9692594e+04 8.34e+02 2.12e+03 -1.0 2.21e+05 - 2.04e-03 2.21e-03f 1
16 -2.0589133e+04 1.84e+03 2.11e+03 -1.0 1.98e+05 - 7.22e-04 3.06e-03f 1
17 -2.2951726e+04 2.94e+03 2.07e+03 -1.0 8.08e+04 - 1.48e-04 1.80e-02f 1
18 -2.4464508e+04 3.49e+03 1.98e+03 -1.0 8.91e+03 - 1.56e-02 4.42e-02f 1
19 -2.4259463e+04 3.48e+03 1.88e+03 -1.0 4.28e+03 - 2.56e-03 5.61e-02h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
20 -2.3674020e+04 3.38e+03 1.82e+03 -1.0 3.38e+03 - 8.84e-02 2.79e-02h 1
21 -2.2546039e+04 3.20e+03 1.73e+03 -1.0 3.31e+03 - 5.40e-02 5.32e-02h 1
22 -1.6865403e+04 2.39e+03 1.31e+03 -1.0 3.14e+03 - 8.02e-02 2.56e-01h 1
pounce: wrote C:\Users\Devin\AppData\Local\Temp\tmpcnu0y99m\unknownqnjk4oo9.sol
23 -4.5444712e+03 9.48e+02 4.67e+02 -1.0 2.31e+03 - 1.49e-01 6.09e-01h 1
24 -5.6057708e+02 5.35e+02 3.90e+02 -1.0 9.41e+02 - 4.00e-02 4.37e-01h 1
25 1.9198174e+03 2.57e+02 3.13e+02 -1.0 5.33e+02 - 2.45e-01 5.20e-01h 1
26 3.5975233e+03 3.55e+01 4.58e+02 -1.0 2.57e+02 - 6.23e-02 1.00e+00h 1
27 3.1856407e+03 2.55e+01 4.07e+02 -1.0 6.92e+01 - 1.18e-01 1.00e+00f 1
28 2.4749926e+03 4.28e+01 3.22e+02 -1.0 9.43e+01 - 2.05e-01 1.00e+00f 1
29 2.4245947e+03 3.87e+01 2.11e+02 -1.0 7.68e+01 - 3.44e-01 1.00e+00f 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
30 2.5874731e+03 5.00e-01 1.47e+01 -1.0 3.84e+01 - 9.30e-01 1.00e+00h 1
31 2.5894820e+03 5.03e-04 1.26e-05 -1.0 4.89e-01 - 1.00e+00 1.00e+00h 1
32 2.5893878e+03 2.43e-05 1.60e-05 -2.5 5.88e-02 - 1.00e+00 1.00e+00f 1
33 2.5893851e+03 2.00e-08 1.26e-08 -3.8 1.70e-03 - 1.00e+00 1.00e+00f 1
34 2.5893850e+03 5.72e-11 3.74e-11 -5.7 9.01e-05 - 1.00e+00 1.00e+00f 1
35 2.5893850e+03 1.14e-13 6.57e-13 -8.6 1.12e-06 - 1.00e+00 1.00e+00h 1
Number of Iterations....: 35
(scaled) (unscaled)
Objective...............: 2.5893849586465190e+03 2.5893849586465190e+03
Dual infeasibility......: 6.5728632969498408e-13 6.5728632969498408e-13
Constraint violation....: 1.1368683772161603e-13 1.1368683772161603e-13
Variable bound violation: 0.0000000000000000e+00 0.0000000000000000e+00
Complementarity.........: 2.5059503395178417e-09 2.5059503395178417e-09
Overall NLP error.......: 2.5059503395178417e-09 2.5059503395178417e-09
Number of objective function evaluations = 36
Number of objective gradient evaluations = 36
Number of equality constraint evaluations = 36
Number of inequality constraint evaluations = 36
Number of equality constraint Jacobian evaluations = 36
Number of inequality constraint Jacobian evaluations = 36
Number of Lagrangian Hessian evaluations = 36
Total seconds in POUNCE = 0.231
EXIT: Optimal Solution Found.
POUNCE 0.10.0: Optimal Solution Found.
Status: Solve_Succeeded
<pyomo.contrib.solver.common.results.Results at 0x1b24fe35270>
drto registry| states: 4, controls: 1 |
| horizon | t (ContinuousSet, 251 points) |
| states | x (free), x_dot (free), theta (free), theta_dot (free) |
| dynamics | dx[t] == x_dot[t] for t in t |
| dynamics | dx_dot[t] == (m_p*g*sin(theta[t])*cos(theta[t]) - (1 + kJ)*(F[t] + m_p*l*theta_dot[t]**2*sin(theta[t]) - b*x_dot[t]))/(m_p*cos(theta[t])**2 - (1 + kJ)*m_c) for t in t |
| dynamics | dtheta[t] == theta_dot[t] for t in t |
| dynamics | dtheta_dot[t] == (m_c*g*sin(theta[t]) - cos(theta[t])*(F[t] + m_p*l*theta_dot[t]**2*sin(theta[t])))/((1 + kJ)*m_c*l - m_p*l*cos(theta[t])**2) for t in t |
| controls | F (piecewise_constant, free) |
| tracking stage cost | cost[t] == (theta[t] - theta_ss)**2/theta_scale**2 + 0.01*(x[t] - x_ss)**2 + 0.01*(x_dot[t] - x_dot_ss)**2 + 0.01*(theta_dot[t] - theta_dot_ss)**2/theta_scale**2 + 0.1*(F[t] - F_ss)**2 for t in sorted(t)[:-1] |
| terminal cost | term == (theta[50] - theta_ss)**2/theta_scale**2 + 0.01*(x[50] - x_ss)**2 + 0.01*(x_dot[50] - x_dot_ss)**2 + 0.01*(theta_dot[50] - theta_dot_ss)**2/theta_scale**2 |
| initial conditions | x[0] == x_hat |
| initial conditions | x_dot[0] == x_dot_hat |
| initial conditions | theta[0] == theta_hat |
| initial conditions | theta_dot[0] == theta_dot_hat |
| steady-state targets | x_ss (of x), x_dot_ss (of x_dot), theta_ss (of theta), theta_dot_ss (of theta_dot) |
| steady-state control targets | F_ss (of F) |
transformationsdrto.parameterize: profiles=F (piecewise_constant)drto.build_objective: objective=sum of 51 weighted cost terms