Attribution
This issue and the linked technical audit were prepared by OpenAI Codex, at the request of @mickaelbegon. The proposed implementation is a Codex-generated prototype and should receive normal maintainer review before merging.
Problem
Bioptim currently dispatches FATROP constraints using scaled decision variables but physical dynamics. For a shooting defect,
$$x_{k+1} - \Phi(x_k, u_k) = 0,$$
with x = S x̄, the constraint received by FATROP is effectively
$$S x̄_{k+1} - \Phi(S x̄_k, u_k) = 0.$$
The Jacobian block with respect to the next scaled state is therefore S, not the identity. CasADi's FATROP structure detection explicitly checks that every entry of this block is exactly 1.0 and otherwise raises:
Structure mismatch: gap-closing constraints must be like this: x_{k+1}-F(xk,uk).
CasADi source: https://github.com/casadi/casadi/blob/3.7.2/casadi/interfaces/fatrop/fatrop_runtime.hpp
A separate prerequisite is that FATROP needs time-major decision-variable ordering ([x0, u0, x1, u1, ...]).
Proposed correction
Dispatch an equivalent, solver-specific form of each state gap:
$$x̄_{k+1} - S^{-1}\Phi(S x̄_k, u_k) = 0.$$
This preserves the feasible set while restoring the exact identity block required by FATROP.
The audit also found that sequential phase transitions were expressed with the opposite sign for FATROP's canonical gap form. They need both a sign change and normalization by the post-transition state scaling.
Prototype branch
A working implementation is available in @mickaelbegon's fork:
The branch currently includes:
- a solver-interface transformation hook that is a no-op for other solvers;
S⁻¹ normalization of shooting state-continuity constraints for FATROP;
- normalization of direct-collocation internal defects for
QDDOT_EQUALS_FORWARD_DYNAMICS;
- normalization of the duplicated first collocation state helper;
- sign correction and normalization for sequential
CONTINUOUS and IMPACT phase transitions;
- an early error when FATROP is used with
OrderingStrategy.VARIABLE_MAJOR;
- an
ordering_strategy argument in the variable-scaling example;
- regression tests comparing IPOPT's physical defect Jacobian (
S) with FATROP's normalized Jacobian (I).
The selected validation suite reports 18 passed, including scaled RK4, direct collocation, inverse-dynamics collocation, and a scaled two-phase problem.
For TAU_EQUALS_INVERSE_DYNAMICS, the prototype normalizes the shooting gap but deliberately leaves internal torque residuals in physical torque units. Dividing those rows by a state scaling would be dimensionally incorrect.
Constraint-tolerance implication
After normalization, FATROP's global constraint tolerance applies in scaled coordinates. A normalized tolerance τ corresponds to a component-wise physical state-gap tolerance
$$|r_i^{physical}| \le S_i τ.$$
For example, S_i = 85 and constr_viol_tol = 1e-4 permit a physical residual of 8.5e-3 for that component. Bioptim should document this explicitly and possibly provide a physical post-solve defect check. A single FATROP tolerance cannot independently preserve different physical tolerances for every constraint family.
Suggested next steps
- Review whether the generic solver transformation hook is the preferred architecture, or whether normalization should live in FATROP constraint dispatch only.
- Run the new tests in the Linux CI environment with the supported CasADi/FATROP build.
- Extend coverage to contact defects, fatigue states, nonuniform phase scalings, mapped states, and multithreaded collocation.
- Decide and document the tolerance policy in physical versus scaled coordinates; consider a post-solve physical-residual report.
- Define explicit behavior for cyclic/non-sequential/custom phase transitions, which do not fit FATROP's automatically detected stage chain.
- Benchmark representative scaled OCPs against the current unscaled FATROP path and IPOPT to quantify conditioning and performance changes.
- After maintainer review, turn the prototype branch into a focused pull request, potentially separating phase-transition support from the core scaling fix.
Attribution
This issue and the linked technical audit were prepared by OpenAI Codex, at the request of @mickaelbegon. The proposed implementation is a Codex-generated prototype and should receive normal maintainer review before merging.
Problem
Bioptim currently dispatches FATROP constraints using scaled decision variables but physical dynamics. For a shooting defect,
with
x = S x̄, the constraint received by FATROP is effectivelyThe Jacobian block with respect to the next scaled state is therefore
S, not the identity. CasADi's FATROP structure detection explicitly checks that every entry of this block is exactly1.0and otherwise raises:CasADi source: https://github.com/casadi/casadi/blob/3.7.2/casadi/interfaces/fatrop/fatrop_runtime.hpp
A separate prerequisite is that FATROP needs time-major decision-variable ordering (
[x0, u0, x1, u1, ...]).Proposed correction
Dispatch an equivalent, solver-specific form of each state gap:
This preserves the feasible set while restoring the exact identity block required by FATROP.
The audit also found that sequential phase transitions were expressed with the opposite sign for FATROP's canonical gap form. They need both a sign change and normalization by the post-transition state scaling.
Prototype branch
A working implementation is available in @mickaelbegon's fork:
The branch currently includes:
S⁻¹normalization of shooting state-continuity constraints for FATROP;QDDOT_EQUALS_FORWARD_DYNAMICS;CONTINUOUSandIMPACTphase transitions;OrderingStrategy.VARIABLE_MAJOR;ordering_strategyargument in the variable-scaling example;S) with FATROP's normalized Jacobian (I).The selected validation suite reports 18 passed, including scaled RK4, direct collocation, inverse-dynamics collocation, and a scaled two-phase problem.
For
TAU_EQUALS_INVERSE_DYNAMICS, the prototype normalizes the shooting gap but deliberately leaves internal torque residuals in physical torque units. Dividing those rows by a state scaling would be dimensionally incorrect.Constraint-tolerance implication
After normalization, FATROP's global constraint tolerance applies in scaled coordinates. A normalized tolerance
τcorresponds to a component-wise physical state-gap toleranceFor example,
S_i = 85andconstr_viol_tol = 1e-4permit a physical residual of8.5e-3for that component. Bioptim should document this explicitly and possibly provide a physical post-solve defect check. A single FATROP tolerance cannot independently preserve different physical tolerances for every constraint family.Suggested next steps