maptor.solution#

Classes for working with optimization results.

class maptor.solution.Solution(raw_solution, problem, auto_summary=True)[source]#

Bases: object

Optimal control solution with comprehensive data access and analysis capabilities.

Provides unified interface for accessing optimization results, trajectories, solver diagnostics, mesh information, and adaptive refinement data. Supports both single-phase and multiphase problems with automatic data concatenation.

Data Access Patterns:

Mission-wide access (concatenates all phases): - solution[“variable_name”] - Variable across all phases - solution[“time_states”] - State time points across all phases - solution[“time_controls”] - Control time points across all phases

Phase-specific access: - solution[(phase_id, “variable_name”)] - Variable in specific phase - solution[(phase_id, “time_states”)] - State times in specific phase - solution[(phase_id, “time_controls”)] - Control times in specific phase

Existence checking: - “variable_name” in solution - Check mission-wide variable - (phase_id, “variable”) in solution - Check phase-specific variable

Examples:

Basic solution workflow:

>>> solution = mtor.solve_adaptive(problem)
>>> if solution.status["success"]:
...     print(f"Objective: {solution.status['objective']:.6f}")
...     solution.plot()

Mission-wide data access:

>>> altitude_all = solution["altitude"]       # All phases concatenated
>>> velocity_all = solution["velocity"]       # All phases concatenated
>>> state_times_all = solution["time_states"] # All phase state times

Phase-specific data access:

>>> altitude_p1 = solution[(1, "altitude")]   # Phase 1 only
>>> velocity_p2 = solution[(2, "velocity")]   # Phase 2 only
>>> state_times_p1 = solution[(1, "time_states")]

Data extraction patterns:

>>> # Final/initial values
>>> final_altitude = solution["altitude"][-1]
>>> initial_velocity = solution["velocity"][0]
>>> final_mass_p1 = solution[(1, "mass")][-1]
>>>
>>> # Extrema
>>> max_altitude = max(solution["altitude"])
>>> min_thrust_p2 = min(solution[(2, "thrust")])

Variable existence checking:

>>> if "altitude" in solution:
...     altitude_data = solution["altitude"]
>>> if (2, "thrust") in solution:
...     thrust_p2 = solution[(2, "thrust")]

Phase information access:

>>> for phase_id, phase_data in solution.phases.items():
...     duration = phase_data["times"]["duration"]
...     state_names = phase_data["variables"]["state_names"]

Solution validation:

>>> status = solution.status
>>> if status["success"]:
...     objective = status["objective"]
...     mission_time = status["total_mission_time"]
... else:
...     print(f"Failed: {status['message']}")
Parameters:
  • raw_solution (OptimalControlSolution | None)

  • problem (ProblemProtocol | None)

  • auto_summary (bool)

__init__(raw_solution, problem, auto_summary=True)[source]#

Initialize solution wrapper from raw multiphase optimization results.

Args:

raw_solution: Raw optimization results from solver problem: Problem protocol instance auto_summary: Whether to automatically display comprehensive summary (default: True)

Parameters:
  • raw_solution (OptimalControlSolution | None)

  • problem (ProblemProtocol | None)

  • auto_summary (bool)

Return type:

None

property status: dict[str, Any]#

Complete solution status and optimization results.

Provides comprehensive optimization outcome information including success status, objective value, and mission timing. Essential for solution validation and performance assessment.

Returns:

Dictionary containing complete status information:

  • success (bool): Optimization success status

  • message (str): Detailed solver status message

  • objective (float): Final objective function value

  • total_mission_time (float): Sum of all phase durations

Examples:

Success checking:

>>> if solution.status["success"]:
...     print("Optimization successful")

Objective extraction:

>>> objective = solution.status["objective"]
>>> mission_time = solution.status["total_mission_time"]

Error handling:

>>> status = solution.status
>>> if not status["success"]:
...     print(f"Failed: {status['message']}")
...     print(f"Objective: {status['objective']}")  # May be NaN

Status inspection:

>>> print(f"Success: {solution.status['success']}")
>>> print(f"Message: {solution.status['message']}")
>>> print(f"Objective: {solution.status['objective']:.6e}")
>>> print(f"Mission time: {solution.status['total_mission_time']:.3f}")
property phases: dict[int, dict[str, Any]]#

Comprehensive phase information and data organization.

Provides detailed data for each phase including timing, variables, mesh configuration, and trajectory arrays. Essential for understanding multiphase structure and accessing phase-specific information.

Returns:

Dictionary mapping phase IDs to phase data:

Phase data structure:

  • times (dict): Phase timing
    • initial (float): Phase start time

    • final (float): Phase end time

    • duration (float): Phase duration

  • variables (dict): Variable information
    • state_names (list): State variable names

    • control_names (list): Control variable names

    • num_states (int): Number of states

    • num_controls (int): Number of controls

  • mesh (dict): Mesh configuration
    • polynomial_degrees (list): Polynomial degree per interval

    • mesh_nodes (FloatArray): Mesh node locations

    • num_intervals (int): Total intervals

  • time_arrays (dict): Time coordinates
    • states (FloatArray): State time points

    • controls (FloatArray): Control time points

  • integrals (float | FloatArray | None): Integral values

Examples:

Phase iteration:

>>> for phase_id, phase_data in solution.phases.items():
...     print(f"Phase {phase_id}")

Timing information:

>>> phase_1 = solution.phases[1]
>>> duration = phase_1["times"]["duration"]
>>> start_time = phase_1["times"]["initial"]
>>> end_time = phase_1["times"]["final"]

Variable information:

>>> variables = solution.phases[1]["variables"]
>>> state_names = variables["state_names"]     # ["x", "y", "vx", "vy"]
>>> control_names = variables["control_names"] # ["thrust_x", "thrust_y"]
>>> num_states = variables["num_states"]       # 4
>>> num_controls = variables["num_controls"]   # 2

Mesh information:

>>> mesh = solution.phases[1]["mesh"]
>>> degrees = mesh["polynomial_degrees"]       # [6, 8, 6]
>>> intervals = mesh["num_intervals"]          # 3
>>> nodes = mesh["mesh_nodes"]                 # [-1, -0.5, 0.5, 1]

Time arrays:

>>> time_arrays = solution.phases[1]["time_arrays"]
>>> state_times = time_arrays["states"]        # State time coordinates
>>> control_times = time_arrays["controls"]    # Control time coordinates

Integral values:

>>> integrals = solution.phases[1]["integrals"]
>>> if isinstance(integrals, float):
...     single_integral = integrals             # Single integral
>>> else:
...     multiple_integrals = integrals          # Array of integrals
property parameters: dict[str, Any] | None#

Static parameter optimization results and information.

Provides access to optimized static parameters with comprehensive parameter information. Returns None if no parameters were defined.

Returns:

Parameter information dictionary or None:

  • values (FloatArray): Optimized parameter values

  • names (list[str] | None): Parameter names if available

  • count (int): Number of static parameters

Examples:

Parameter existence check:

>>> if solution.parameters is not None:
...     print("Problem has static parameters")

Parameter access:

>>> params = solution.parameters
>>> if params:
...     values = params["values"]        # [500.0, 1500.0, 0.1]
...     count = params["count"]          # 3
...     names = params["names"]          # ["mass", "thrust", "drag"] or None

Named parameter access:

>>> params = solution.parameters
>>> if params and params["names"]:
...     for name, value in zip(params["names"], params["values"]):
...         print(f"{name}: {value:.6f}")

Unnamed parameter access:

>>> params = solution.parameters
>>> if params:
...     for i, value in enumerate(params["values"]):
...         print(f"Parameter {i}: {value:.6f}")

No parameters case:

>>> if solution.parameters is None:
...     print("No static parameters in problem")
property adaptive: dict[str, Any] | None#

Adaptive mesh refinement algorithm results and convergence diagnostics.

Provides comprehensive adaptive algorithm performance data including convergence status, error estimates, and refinement statistics. Only available for adaptive solver solutions.

Returns:

Adaptive algorithm data dictionary or None:

  • converged (bool): Algorithm convergence status

  • iterations (int): Refinement iterations performed

  • target_tolerance (float): Target error tolerance

  • phase_converged (dict): Per-phase convergence status

  • final_errors (dict): Final error estimates per phase

  • gamma_factors (dict): Normalization factors per phase

Examples:

Adaptive solution check:

>>> if solution.adaptive:
...     print("Adaptive solution available")

Convergence assessment:

>>> adaptive_info = solution.adaptive
>>> if adaptive_info:
...     converged = adaptive_info["converged"]
...     iterations = adaptive_info["iterations"]
...     tolerance = adaptive_info["target_tolerance"]

Per-phase convergence:

>>> if solution.adaptive:
...     for phase_id, converged in solution.adaptive["phase_converged"].items():
...         status = "✓" if converged else "✗"
...         print(f"Phase {phase_id}: {status}")

Error analysis:

>>> if solution.adaptive:
...     for phase_id, errors in solution.adaptive["final_errors"].items():
...         max_error = max(errors) if errors else 0.0
...         print(f"Phase {phase_id} max error: {max_error:.2e}")

Algorithm statistics:

>>> adaptive = solution.adaptive
>>> if adaptive:
...     print(f"Converged: {adaptive['converged']}")
...     print(f"Iterations: {adaptive['iterations']}")
...     print(f"Target tolerance: {adaptive['target_tolerance']:.1e}")

Fixed mesh solution:

>>> if solution.adaptive is None:
...     print("Fixed mesh solution - no adaptive data")
plot(phase_id=None, *variable_names, figsize=(12.0, 8.0), show_phase_boundaries=True)[source]#

Plot solution trajectories with comprehensive customization options.

Creates trajectory plots with automatic formatting, phase boundaries, and flexible variable selection. Supports both single-phase and multiphase visualization with professional styling.

Return type:

None

Parameters:
Args:
phase_id: Phase selection:
  • None: Plot all phases (default)

  • int: Plot specific phase only

variable_names: Variable selection:
  • Empty: Plot all variables

  • Specified: Plot only named variables

figsize: Figure size tuple (width, height)

show_phase_boundaries: Display vertical lines at phase transitions

Examples:

Basic plotting:

>>> solution.plot()  # All variables, all phases

Specific phase:

>>> solution.plot(phase_id=1)  # Phase 1 only

Selected variables:

>>> solution.plot(phase_id=None, "altitude", "velocity", "thrust")

Custom formatting:

>>> solution.plot(
...     figsize=(16, 10),
...     show_phase_boundaries=True
... )

Phase-specific variables:

>>> solution.plot(1, "x_position", "y_position")  # Phase 1 positions

No phase boundaries:

>>> solution.plot(show_phase_boundaries=False)
summary(comprehensive=True)[source]#

Display solution summary with comprehensive details and diagnostics.

Prints detailed overview including solver status, phase information, mesh details, and adaptive algorithm results. Essential for solution validation and performance analysis.

Return type:

None

Parameters:

comprehensive (bool)

Args:
comprehensive: Summary detail level:
  • True: Full detailed summary (default)

  • False: Concise key information only

Examples:

Full summary:

>>> solution.summary()  # Comprehensive details

Concise summary:

>>> solution.summary(comprehensive=False)  # Key information only

Manual summary control:

>>> # Solve without automatic summary
>>> solution = mtor.solve_adaptive(problem, show_summary=False)
>>> # Display summary when needed
>>> solution.summary()

Conditional summary:

>>> if solution.status["success"]:
...     solution.summary()
... else:
...     solution.summary(comprehensive=False)  # Brief failure info