Private API
This page lists non-exported (internal) symbols of CTModels.Solutions.
From CTModels.Solutions
BoxDualDiff [Struct]
CTModels.Solutions.BoxDualDiff — Type
BoxDualDiff{DL,DU,I} <: FunctionCallable struct computing the net dual of a box constraint: f(t) = lb(t)[idx] - ub(t)[idx].
I = Int gives a scalar; I = Vector{Int} gives a vector.
Replaces anonymous closures t -> duals_lb(t)[i] - duals_ub(t)[i] (and the vector variant) produced by dual(sol, model, label) for state and control box constraints.
DualSlice [Struct]
CTModels.Solutions.DualSlice — Type
DualSlice{D,I} <: FunctionCallable struct extracting a slice of a time-dependent dual vector: f(t) = duals(t)[idx].
I = Int gives a scalar; I = Vector{Int} gives a vector. The scalar/vector distinction is encoded in the type parameter so the call method is fully specialised (no runtime branch).
Replaces anonymous closures t -> duals(t)[indices[1]] and t -> duals(t)[indices] produced by dual(sol, model, label).
_discretize_all_components [Function]
CTModels.Solutions._discretize_all_components — Function
_discretize_all_components(
sol::CTModels.Solutions.Solution,
T_state::Vector{Float64},
T_control::Vector{Float64},
T_costate::Vector{Float64},
T_path::Vector{Float64},
dim_x::Int64,
dim_u::Int64
) -> Dict{String, Any}
Discretize all solution components on their respective time grids for serialization.
This internal helper function extracts the common discretization logic shared by both UnifiedTimeGridModel and MultipleTimeGridModel serialization. It evaluates all trajectory functions on their associated time grids and assembles them into a dictionary.
Grid-Component Association
Each component is discretized on its semantically correct time grid:
- State trajectory →
T_stategrid - Control trajectory →
T_controlgrid - Costate trajectory →
T_costategrid - Path constraint duals →
T_pathgrid - State box constraint duals (lb/ub) →
T_stategrid - Control box constraint duals (lb/ub) →
T_controlgrid - Boundary/variable duals → Time-independent (vectors, not discretized)
Arguments
sol::Solution: Solution object containing trajectory functionsT_state::Vector{Float64}: Time grid for state discretizationT_control::Vector{Float64}: Time grid for control discretizationT_costate::Vector{Float64}: Time grid for costate discretizationT_path::Vector{Float64}: Time grid for path constraint dual discretizationdim_x::Int: State dimension (for validation)dim_u::Int: Control dimension (for validation)
Returns
Dict{String, Any}: Dictionary with all discretized components (grids not included)
Notes
This function does NOT include time grid data in the returned dictionary. The calling function (_serialize_solution for UnifiedTimeGridModel or MultipleTimeGridModel) is responsible for adding the appropriate grid keys.
See also: _serialize_solution, _discretize_function, _discretize_dual
_discretize_dual [Function]
CTModels.Solutions._discretize_dual — Function
_discretize_dual(
dual_func::Union{Nothing, Function},
T
) -> Union{Nothing, Matrix{Float64}}
_discretize_dual(
dual_func::Union{Nothing, Function},
T,
dim::Int64
) -> Union{Nothing, Matrix{Float64}}
Discretize a dual function, returning nothing if the input is nothing.
Arguments
dual_func::Union{Function,Nothing}: Dual function ornothing.T: Time grid.dim::Int: Dimension (auto-detected if -1).
Returns
Matrix{Float64}ifdual_funcis a function.nothingifdual_funcisnothing.
See also: CTModels.Solutions._discretize_function.
_discretize_function [Function]
CTModels.Solutions._discretize_function — Function
_discretize_function(
f::Function,
T::AbstractVector
) -> Matrix{Float64}
_discretize_function(
f::Function,
T::AbstractVector,
dim::Int64
) -> Matrix{Float64}
Discretize a function on a time grid.
Evaluates f at each point in T and collects the results into a matrix. If dim is -1, the output dimension is auto-detected from the first evaluation of f.
Arguments
f::Function: Function to discretize (can return a scalar or a vector).T::AbstractVector: Time grid.dim::Int: Expected dimension of the result. If -1, auto-detected from first evaluation.
Returns
Matrix{Float64}: n×dim matrix where n = length(T).
Examples
# Scalar function
f_scalar = t -> 2.0 * t
result = _discretize_function(f_scalar, [0.0, 0.5, 1.0], 1)
# result = [0.0; 1.0; 2.0]
# Vector function
f_vec = t -> [t, 2*t]
result = _discretize_function(f_vec, [0.0, 0.5, 1.0], 2)
# result = [0.0 0.0; 0.5 1.0; 1.0 2.0]
# Auto-detect dimension
result = _discretize_function(f_vec, [0.0, 0.5, 1.0])
# result = [0.0 0.0; 0.5 1.0; 1.0 2.0]See also: CTModels.Solutions._discretize_dual.
_discretize_function(
f::Function,
T::CTModels.Solutions.UnifiedTimeGridModel
) -> Matrix{Float64}
_discretize_function(
f::Function,
T::CTModels.Solutions.UnifiedTimeGridModel,
dim::Int64
) -> Matrix{Float64}
Discretize a function on a TimeGridModel by extracting the underlying time grid.
See also: CTModels.Solutions._discretize_function.
_dual_dimension [Function]
CTModels.Solutions._dual_dimension — Function
_dual_dimension(
_::Nothing,
_::CTModels.Solutions.Solution
) -> Int64
Return the dimension of a dual value, evaluating at initial time.
Arguments
dual::Union{Nothing, Function}: The dual function or nothing.sol::Solution: The optimal control solution.
Returns
Dimension: The dual dimension (0 if dual is nothing).
See also: CTModels.Solutions.dim_dual_state_constraints_box, CTModels.Solutions.dim_dual_control_constraints_box.
_extend_grid_to_match [Function]
CTModels.Solutions._extend_grid_to_match — Function
_extend_grid_to_match(
T_target::Vector{Float64},
T_reference::Vector{Float64},
component_name::String
) -> Vector{Float64}
Extend a target time grid to match a reference grid if the target is a strict prefix.
This function checks if T_target is missing only the last element of T_reference (i.e., T_target == T_reference[1:end-1]). If so, it returns T_reference to enable grid unification. Otherwise, it returns T_target unchanged.
Arguments
T_target::Vector{Float64}: Time grid to potentially extendT_reference::Vector{Float64}: Reference time grid (typically the longest grid)component_name::String: Name of the component for logging purposes
Returns
Vector{Float64}: Extended grid if extension condition met, otherwise originalT_target
Notes
- Extension condition:
length(T_target) == length(T_reference) - 1ANDT_target == T_reference[1:end-1] - Emits
@infolog when extension is performed for transparency - Does not modify trajectory data matrices (interpolation handles this via
T[1:N])
See also: _validate_and_fix_time_grid, build_solution
_interpolate_from_data [Function]
CTModels.Solutions._interpolate_from_data — Function
_interpolate_from_data(data, T, dim, type_param; allow_nothing=false,
constant_if_two_points=false, expected_dim=nothing,
interpolation=:linear)Internal helper to create an interpolated function from discrete data.
Arguments
data: Matrix{Float64}, Function, or Nothing (if allow_nothing=true)T: Time grid vectordim: Dimension to extract from matrix (nothing = take full matrix)type_param: Type parameter for dispatch (Matrix, Function, or Nothing)allow_nothing: If false, throws IncorrectArgument when data is nothingconstant_if_two_points: If true and length(T)==2, return constant functionexpected_dim: If provided, validates matrix dimension matches (via @ensure)interpolation: Interpolation type (:linearor:constant)
Returns
- Interpolated function (or nothing if data=nothing and allow_nothing=true)
Throws
IncorrectArgument: If data is nothing and allow_nothing=falseAssertionError: If expected_dim provided and doesn't match (via @ensure)
Notes
This is a low-level helper. Use build_interpolated_function for the complete workflow.
See also: CTModels.Solutions.build_interpolated_function, CTModels.Solutions._wrap_scalar_and_deepcopy.
_serialize_solution [Function]
CTModels.Solutions._serialize_solution — Function
_serialize_solution(
sol::CTModels.Solutions.Solution
) -> Dict{String, Any}
Serialize a solution into discrete data for export to persistent storage (JLD2, JSON, etc.).
This function converts a Solution object (which may contain interpolated functions) into a fully discrete, serializable representation. All trajectory functions are evaluated on their respective time grids and stored as matrices. The serialization format automatically adapts based on whether the solution uses unified or multiple time grids.
Serialization Formats
The function produces two different formats depending on the solution's time grid model:
Unified Time Grid Format (Legacy)
When all grids are identical (UnifiedTimeGridModel), produces:
Dict(
"time_grid" => T, # Single grid for all components
"state" => Matrix, # Discretized on T
"control" => Matrix, # Discretized on T
"costate" => Matrix, # Discretized on T
"path_constraints_dual" => Matrix, # Discretized on T
# ... other fields
)Multiple Time Grids Format
When grids differ (MultipleTimeGridModel), produces:
Dict(
"time_grid_state" => T_state, # State-specific grid
"time_grid_control" => T_control, # Control-specific grid
"time_grid_costate" => T_costate, # Costate-specific grid
"time_grid_path" => T_path, # Path constraints grid
"state" => Matrix, # Discretized on T_state
"control" => Matrix, # Discretized on T_control
"costate" => Matrix, # Discretized on T_costate
"path_constraints_dual" => Matrix, # Discretized on T_path
# ... other fields
)Arguments
sol::Solution: Solution object to serialize (may contain functions or matrices)
Returns
Dict{String, Any}: Complete serializable dictionary containing:- Time grids: Either single
"time_grid"or four separate grids - Trajectories:
"state","control","costate"asMatrix{Float64} - Variable:
"variable"asVector{Float64}(time-independent) - Objective:
"objective"asFloat64 - Dual variables: All constraint duals (can be
nothingif not present)"path_constraints_dual": Path constraint duals on path grid"state_constraints_lb_dual","state_constraints_ub_dual": State box duals on state grid"control_constraints_lb_dual","control_constraints_ub_dual": Control box duals on control grid"boundary_constraints_dual": Boundary duals (time-independent vector)"variable_constraints_lb_dual","variable_constraints_ub_dual": Variable duals (vectors)
- Solver info:
"iterations","message","status","successful","constraints_violation","infos"
- Time grids: Either single
Discretization Behavior
- Function trajectories: Evaluated at each point of their associated time grid
- Matrix trajectories: Copied as-is (already discrete)
- Nothing duals: Preserved as
nothingin the dictionary - Grid association: Each component is discretized on its correct grid:
- State and state box duals →
T_state - Control and control box duals →
T_control - Costate →
T_costate - Path constraint duals →
T_path
- State and state box duals →
Example
using CTModels
# Solve OCP with multiple grids
sol = solve(ocp, strategy=MyStrategy())
# Serialize to dictionary
data = _serialize_solution(sol)
# Check format
if haskey(data, "time_grid_state")
# Multiple grids format
println("State grid: ", length(data["time_grid_state"]), " points")
println("Control grid: ", length(data["time_grid_control"]), " points")
println("Costate grid: ", length(data["time_grid_costate"]), " points")
else
# Unified grid format
println("Unified grid: ", length(data["time_grid"]), " points")
end
# Export to file (handled by extensions)
export_ocp_solution(sol; filename="solution", format=:JLD)
# Reconstruct from data
sol_reconstructed = _reconstruct_solution_from_data(ocp, data)Notes
Backward Compatibility
The serialization format is designed for backward compatibility:
- Old files with single
"time_grid"can be read (costate defaults to state grid) - New files with four grids are forward-compatible with updated readers
- The
_reconstruct_solution_from_datafunction handles both formats automatically
Memory Efficiency
When all grids are identical, the unified format avoids storing redundant grid data, reducing file size and memory usage.
Round-Trip Guarantee
The serialized data is fully compatible with build_solution for exact reconstruction:
data = _serialize_solution(sol)
sol_new = build_solution(ocp, data["time_grid_state"], ...; objective=data["objective"], ...)See also: build_solution, _reconstruct_solution_from_data, export_ocp_solution, import_ocp_solution
_serialize_solution(
_::CTModels.Solutions.UnifiedTimeGridModel,
sol::CTModels.Solutions.Solution,
dim_x::Int64,
dim_u::Int64
) -> Dict{String, Any}
Serialize solution with unified time grid (legacy single-grid format).
This method handles solutions where all components share the same time grid. It produces the legacy format with a single "time_grid" key, which is backward-compatible with older versions of the package.
Format Produced
Dict(
"time_grid" => T, # Single unified grid
"state" => Matrix, # All components discretized on T
"control" => Matrix,
"costate" => Matrix,
# ... all other fields
)Arguments
::UnifiedTimeGridModel: Time grid model type (dispatch parameter)sol::Solution: Solution to serializedim_x::Int: State dimensiondim_u::Int: Control dimension
Returns
Dict{String, Any}: Serialized data with single time grid
Notes
This format is used when build_solution is called with identical grids for all components, or when using the legacy single-grid signature. It ensures backward compatibility with files created before the multi-grid feature was introduced.
See also: CTModels.Solutions._serialize_solution
_serialize_solution(
_::CTModels.Solutions.MultipleTimeGridModel,
sol::CTModels.Solutions.Solution,
dim_x::Int64,
dim_u::Int64
) -> Dict{String, Any}
Serialize solution with multiple independent time grids (modern format).
This method handles solutions where different components use different time grids. It produces the modern format with four separate grid keys (time_grid_state, time_grid_control, time_grid_costate, time_grid_path), preserving the independent discretizations.
Format Produced
Dict(
"time_grid_state" => T_state, # State-specific grid
"time_grid_control" => T_control, # Control-specific grid
"time_grid_costate" => T_costate, # Costate-specific grid
"time_grid_path" => T_path, # Path constraints grid
"state" => Matrix, # Discretized on T_state
"control" => Matrix, # Discretized on T_control
"costate" => Matrix, # Discretized on T_costate
"path_constraints_dual" => Matrix, # Discretized on T_path
# ... all other fields
)Arguments
::MultipleTimeGridModel: Time grid model type (dispatch parameter)sol::Solution: Solution to serializedim_x::Int: State dimensiondim_u::Int: Control dimension
Returns
Dict{String, Any}: Serialized data with four independent time grids
Notes
This format is used when build_solution is called with different grids for different components. It allows numerical schemes to use optimal discretizations for each component (e.g., finer grid for state, coarser for control, custom for costate).
The reconstruction function _reconstruct_solution_from_data detects this format by checking for the presence of "time_grid_state" key and handles it appropriately.
See also: CTModels.Solutions._serialize_solution, CTModels.Solutions.build_solution
_validate_and_fix_time_grid [Function]
CTModels.Solutions._validate_and_fix_time_grid — Function
_validate_and_fix_time_grid(
T::Vector{Float64},
component_name::String
) -> Vector{Float64}
Validate and fix a time grid by ensuring it is strictly increasing.
Arguments
T::Vector{Float64}: Time grid to validatecomponent_name::String: Name of the component for error messages
Returns
Vector{Float64}: Validated and potentially reordered time grid
Notes
If the grid is not strictly increasing, it is reordered and a warning is emitted.
_wrap_scalar_and_deepcopy [Function]
CTModels.Solutions._wrap_scalar_and_deepcopy — Function
_wrap_scalar_and_deepcopy(func, dim)Internal helper to wrap a function with scalar extraction and deepcopy, returning a CTModels.Components.CoercedTrajectory.
Arguments
func: Function or callable to wrap (or nothing)dim: Dimension of output (1 = scalar extraction viaonly, otherwiseidentity)
Returns
Components.CoercedTrajectory(deepcopy(func), coerce)wherecoerceisonly(dim==1) oridentity(otherwise)nothingif func is nothing
Notes
deepcopy is applied to func before storing it in the struct. This is essential for closures supplied by the user: Julia closures capture variable REFERENCES, not values, so without deepcopy, modifying external variables after solution creation would affect the solution.
Example:
param_x = 1.0
X_func = t -> [param_x * t]
sol = build_solution(...)
param_x = 999.0
# Without deepcopy: sol.state(0.5) would return [499.5] (uses new param_x)
# With deepcopy: sol.state(0.5) returns [0.5] (uses original param_x value)See also: CTModels.Solutions._interpolate_from_data, CTModels.Solutions.build_interpolated_function.
build_interpolated_function [Function]
CTModels.Solutions.build_interpolated_function — Function
build_interpolated_function(data, T, dim, type_param; allow_nothing=false,
constant_if_two_points=false, expected_dim=nothing,
interpolation=:linear)Unified function to build an interpolated function with deepcopy and scalar wrapping.
This is the main entry point that combines interpolation and wrapping in one call.
Arguments
data: Matrix{Float64}, Function, or Nothing (if allow_nothing=true)T: Time grid vectordim: Dimension to extract (nothing = take full matrix)type_param: Type parameter for dispatchallow_nothing: Allow data=nothing (for optional duals)constant_if_two_points: Return constant function if length(T)==2 (for costate)expected_dim: Validate matrix has this dimension (for robustness)interpolation: Interpolation type (:linearor:constant)
Returns
- Wrapped interpolated function ready for use in Solution
- nothing if data=nothing and allow_nothing=true
Throws
IncorrectArgument: If data is nothing and allow_nothing=falseAssertionError: If expected_dim doesn't match actual dimension
Examples
# State interpolation (required, with validation)
fx = build_interpolated_function(X, T, dim_x, TX; expected_dim=dim_x)
# Control with piecewise-constant interpolation
fu = build_interpolated_function(U, T, dim_u, TU; expected_dim=dim_u, interpolation=:constant)
# Costate with special 2-point handling
fp = build_interpolated_function(P, T, dim_x, TP;
constant_if_two_points=true, expected_dim=dim_x)
# Optional dual (can be nothing)
fscbd = build_interpolated_function(state_constraints_lb_dual, T, dim_x,
Union{Matrix{Float64},Nothing};
allow_nothing=true)See also: CTModels.Solutions._interpolate_from_data, CTModels.Solutions._wrap_scalar_and_deepcopy.
dual_model [Function]
CTModels.Solutions.dual_model — Function
dual_model(
sol::CTModels.Solutions.Solution{<:CTModels.Solutions.AbstractTimeGridModel, <:CTModels.Components.AbstractTimesModel, <:CTModels.Components.AbstractStateModel, <:CTModels.Components.AbstractControlModel, <:CTModels.Components.AbstractVariableModel, <:CTModels.Models.AbstractModel, <:Function, <:Real, DM<:CTModels.Solutions.AbstractDualModel}
) -> CTModels.Solutions.AbstractDualModel
Return the dual model containing all constraint multipliers.