Skip to content

ptr

PTR_step(params, settings: Config, state: SolverState, prob: cp.Problem, discretization_solver: callable, cpg_solve, emitter_function, jax_constraints: LoweredJaxConstraints) -> bool

Performs a single SCP iteration.

Parameters:

Name Type Description Default
params

Problem parameters

required
settings Config

Configuration object

required
state SolverState

Solver state (mutated in place)

required
prob Problem

CVXPy problem

required
discretization_solver callable

Discretization solver function

required
cpg_solve

CVXPyGen solver (if enabled)

required
emitter_function

Function to emit iteration data

required
jax_constraints LoweredJaxConstraints

JAX-lowered non-convex constraints

required

Returns:

Name Type Description
bool bool

True if converged, False otherwise

Source code in openscvx/algorithms/ptr.py
def PTR_step(
    params,
    settings: Config,
    state: SolverState,
    prob: cp.Problem,
    discretization_solver: callable,
    cpg_solve,
    emitter_function,
    jax_constraints: "LoweredJaxConstraints",
) -> bool:
    """Performs a single SCP iteration.

    Args:
        params: Problem parameters
        settings: Configuration object
        state: Solver state (mutated in place)
        prob: CVXPy problem
        discretization_solver: Discretization solver function
        cpg_solve: CVXPyGen solver (if enabled)
        emitter_function: Function to emit iteration data
        jax_constraints: JAX-lowered non-convex constraints

    Returns:
        bool: True if converged, False otherwise
    """
    # Run the subproblem
    (
        x_sol,
        u_sol,
        cost,
        J_total,
        J_vb_vec,
        J_vc_vec,
        J_tr_vec,
        prob_stat,
        V_multi_shoot,
        subprop_time,
        dis_time,
    ) = PTR_subproblem(
        params.items(),
        cpg_solve,
        state,
        discretization_solver,
        prob,
        settings,
        jax_constraints,
    )

    # Update state in place by appending to history
    # The x_guess/u_guess properties will automatically return the latest entry
    state.V_history.append(V_multi_shoot)
    state.X.append(x_sol)
    state.U.append(u_sol)

    state.J_tr = np.sum(np.array(J_tr_vec))
    state.J_vb = np.sum(np.array(J_vb_vec))
    state.J_vc = np.sum(np.array(J_vc_vec))

    # Update weights in state
    update_scp_weights(state, settings, state.k)

    # Emit data
    emitter_function(
        {
            "iter": state.k,
            "dis_time": dis_time * 1000.0,
            "subprop_time": subprop_time * 1000.0,
            "J_total": J_total,
            "J_tr": state.J_tr,
            "J_vb": state.J_vb,
            "J_vc": state.J_vc,
            "cost": cost[-1],
            "prob_stat": prob_stat,
        }
    )

    # Increment iteration counter
    state.k += 1

    # Return convergence status
    return (
        (state.J_tr < settings.scp.ep_tr)
        and (state.J_vb < settings.scp.ep_vb)
        and (state.J_vc < settings.scp.ep_vc)
    )

format_result(problem, state: SolverState, converged: bool) -> OptimizationResults

Formats the solver state as an OptimizationResults object.

Directly passes trajectory arrays from solver state to results - no object construction needed. Results store pure arrays, settings store metadata.

Parameters:

Name Type Description Default
problem

The Problem instance (for symbolic metadata and settings).

required
state SolverState

The SolverState to extract results from.

required
converged bool

Whether the optimization converged.

required

Returns:

Type Description
OptimizationResults

OptimizationResults containing the solution data.

Source code in openscvx/algorithms/ptr.py
def format_result(problem, state: "SolverState", converged: bool) -> OptimizationResults:
    """Formats the solver state as an OptimizationResults object.

    Directly passes trajectory arrays from solver state to results - no object
    construction needed. Results store pure arrays, settings store metadata.

    Args:
        problem: The Problem instance (for symbolic metadata and settings).
        state: The SolverState to extract results from.
        converged: Whether the optimization converged.

    Returns:
        OptimizationResults containing the solution data.
    """
    # Build nodes dictionary with all states and controls
    nodes_dict = {}

    # Add all states (user-defined and augmented)
    for sym_state in problem.symbolic.states:
        nodes_dict[sym_state.name] = state.x[:, sym_state._slice]

    # Add all controls (user-defined and augmented)
    for control in problem.symbolic.controls:
        nodes_dict[control.name] = state.u[:, control._slice]

    return OptimizationResults(
        converged=converged,
        t_final=state.x[:, problem.settings.sim.time_slice][-1],
        nodes=nodes_dict,
        trajectory={},  # Populated by post_process
        _states=problem.symbolic.states_prop,  # Use propagation states for trajectory dict
        _controls=problem.symbolic.controls,
        X=state.X,  # Single source of truth - x and u are properties
        U=state.U,
        discretization_history=state.V_history,
        J_tr_history=state.J_tr,
        J_vb_history=state.J_vb,
        J_vc_history=state.J_vc,
    )