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]#
Static parameter optimization results and information.
Always returns valid parameter dictionary with empty structure if no parameters were defined
- Returns:
Parameter information dictionary:
values (FloatArray): Optimized parameter values (empty array if no parameters)
names (list[str] | None): Parameter names if available
count (int): Number of static parameters (0 if no parameters)
- Examples:
Parameter existence check:
>>> if solution.parameters["count"] > 0: ... print("Problem has static parameters")
Parameter access:
>>> params = solution.parameters >>> if params["count"] > 0: ... 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["names"] and params["count"] > 0: ... for name, value in zip(params["names"], params["values"]): ... print(f"{name}: {value:.6f}")
Unnamed parameter access:
>>> params = solution.parameters >>> for i in range(params["count"]): ... value = params["values"][i] ... print(f"Parameter {i}: {value:.6f}")
No parameters case:
>>> if solution.parameters["count"] == 0: ... print("No static parameters in problem")
- property adaptive: dict[str, Any]#
Comprehensive adaptive algorithm performance data and benchmarking metrics.
Provides complete adaptive mesh refinement data including convergence status, error estimates, refinement statistics, and iteration-by-iteration benchmarking arrays. Only available for adaptive solver solutions.
- Returns:
Adaptive algorithm data dictionary with comprehensive benchmarking arrays:
Algorithm Status: - converged (bool): Algorithm convergence status - iterations (int): Total refinement iterations performed - target_tolerance (float): Target error tolerance - phase_converged (dict): Per-phase convergence status - final_errors (dict): Final error estimates per phase per interval - gamma_factors (dict): Normalization factors per phase
Complete Benchmarking Data: - iteration_history (dict): Raw per-iteration algorithm state - benchmark (dict): Processed mission-wide benchmark arrays - phase_benchmarks (dict): Per-phase benchmark arrays
Benchmark Array Structure (both mission-wide and per-phase): - mesh_iteration (list[int]): Iteration numbers [0, 1, 2, …] - estimated_error (list[float]): Error estimates [1e-2, 1e-3, 1e-5, …] - collocation_points (list[int]): Total collocation points [50, 75, 100, …] - mesh_intervals (list[int]): Total mesh intervals [10, 15, 20, …] - polynomial_degrees (list[list[int]]): Polynomial degrees per interval - refinement_strategy (list[dict]): Refinement actions per iteration
- Raises:
- RuntimeError: If no adaptive data available. This typically means
solve_fixed_mesh() was used instead of solve_adaptive().
- Examples:
Safe adaptive data access:
>>> try: ... adaptive = solution.adaptive ... converged = adaptive["converged"] ... iterations = adaptive["iterations"] ... except RuntimeError: ... print("Fixed mesh solution - no adaptive data available")
Complete benchmark arrays:
>>> adaptive = solution.adaptive # May raise RuntimeError >>> benchmark = adaptive["benchmark"] >>> iterations = benchmark["mesh_iteration"] # [0, 1, 2, 3] >>> errors = benchmark["estimated_error"] # [1e-2, 1e-3, 1e-5, 1e-7] >>> points = benchmark["collocation_points"] # [50, 75, 100, 150]
Phase-specific benchmark data:
>>> phase_benchmarks = solution.adaptive["phase_benchmarks"] >>> phase1_data = phase_benchmarks[1] >>> phase1_errors = phase1_data["estimated_error"]
Built-in analysis methods:
>>> try: ... solution.print_benchmark_summary() ... solution.plot_refinement_history(phase_id=1) ... except RuntimeError: ... print("No adaptive data for analysis")
- 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:
- 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.
- 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
- plot_refinement_history(phase_id, figsize=(12, 6), transform_domain=None)[source]#
Visualize adaptive mesh refinement history for research analysis.
Creates mesh point distribution evolution plot showing both mesh interval boundaries (red circles) and interior collocation points (black dots).
- Return type:
- Parameters:
- Args:
phase_id: Phase to visualize figsize: Figure dimensions (width, height) transform_domain: Transform from [-1,1] to physical domain (min, max)
- print_benchmark_summary()[source]#
Display professional adaptive mesh refinement benchmark analysis.
Provides comprehensive performance metrics, convergence analysis, refinement strategies, and research integrity verification suitable for academic comparison with established pseudospectral optimal control methods.
- Return type:
- Examples:
Complete benchmark analysis:
>>> solution = mtor.solve_adaptive(problem) >>> solution.print_benchmark_summary()
Access raw benchmark data:
>>> benchmark_data = solution.adaptive["benchmark"] >>> iterations = benchmark_data["mesh_iteration"] >>> errors = benchmark_data["estimated_error"]
Phase-specific analysis:
>>> phase_data = solution.adaptive["phase_benchmarks"][1] >>> solution.plot_refinement_history(phase_id=1)