Analysis Tools¶
OptiScope provides a comprehensive suite of analysis tools designed to help you make sense of your optimization results. These tools go beyond visualization to provide quantitative insights and decision support.
Overview¶
Multi-objective optimization often produces hundreds or thousands of solutions. Analysis tools help you:
- Identify the best solutions based on your preferences.
- Understand trade-offs between competing objectives.
- Reduce complexity by filtering to representative solutions.
- Make informed decisions with quantitative metrics.
Pareto Front Identification¶
The Pareto front represents the set of non-dominated solutions - solutions where you cannot improve one objective without worsening another. This concept is based on Pareto efficiency [1], named after Italian economist Vilfredo Pareto.
What is Pareto Dominance?¶
Solution A dominates solution B if: - A is at least as good as B in all objectives, AND - A is strictly better than B in at least one objective.
The Pareto front consists of all non-dominated solutions.
Usage¶
from optiscope.analysis import identify_pareto_front
# Identify Pareto-optimal solutions
pareto_mask = identify_pareto_front(
objectives=result.objectives,
directions=result.metadata.optimization_directions
)
# Create a result set from Pareto front
from optiscope.core.result_set import ResultSet
pareto_set = ResultSet(
name="pareto_front",
indices=np.where(pareto_mask)[0].tolist(),
created_by="pareto_analysis",
description="Non-dominated solutions"
)
Algorithm Selection¶
OptiScope includes multiple Pareto identification algorithms:
is_pareto_efficient_dumb: Simple, easy to understand, but slow.is_pareto_efficient_simple: Faster for many points, fewer objectives.is_pareto_efficient: Optimized algorithm with reordering.is_pareto_efficient_auto: Automatically selects the best algorithm based on data shape.
The identify_pareto_front function uses the auto-selection by default.
Smart Pareto Filter¶
When your Pareto front contains hundreds of solutions, it becomes difficult to visualize and analyze. The Smart Pareto Filter [2] reduces the Pareto front to a minimal representative subset while preserving the overall shape and diversity.
How It Works¶
The algorithm iteratively selects points that are maximally separated in the objective space, ensuring: - Coverage: The filtered set represents the entire Pareto front. - Diversity: Selected points are well-distributed. - Compactness: Minimal number of points for a given coverage level.
Usage¶
from optiscope.analysis import smart_pareto_filter, adaptive_smart_pareto_filter
# Manual epsilon specification
selected_indices = smart_pareto_filter(
objectives=result.objectives,
epsilon=0.05, # Minimum separation distance
normalize=True
)
# Adaptive - automatically determines epsilon
selected_indices = adaptive_smart_pareto_filter(
objectives=result.objectives,
target_reduction=0.5, # Reduce to 50% of original
min_points=10, # Keep at least 10 points
normalize=True
)
print(f"Reduced from {len(result)} to {len(selected_indices)} points")
When to Use¶
- Large Pareto Fronts: When you have hundreds of Pareto-optimal solutions.
- Visualization: To create cleaner, more interpretable plots.
- Decision Making: To focus on a manageable set of diverse alternatives.
TOPSIS (Multi-Criteria Decision Analysis)¶
TOPSIS (Technique for Order Preference by Similarity to Ideal Solution) is a decision-making method that ranks solutions based on their similarity to an ideal solution [3].
How It Works¶
- Define Weights: Assign importance weights to each objective.
- Identify Ideal Solution: Best possible value for each objective.
- Identify Anti-Ideal Solution: Worst possible value for each objective.
- Calculate Distances: Measure distance to ideal and anti-ideal.
- Compute Score: Score = distance to anti-ideal / (distance to ideal + distance to anti-ideal).
Higher scores indicate better solutions.
Usage¶
from optiscope.analysis import topsis, topsis_from_result
# Basic usage with custom weights
scores = topsis(
objectives=result.objectives,
weights=[0.4, 0.3, 0.3], # Weights for 3 objectives
directions=["minimize", "minimize", "maximize"],
normalize_method="vector"
)
# Get detailed results
details = topsis(
objectives=result.objectives,
weights=[0.4, 0.3, 0.3],
return_details=True
)
print(f"Best solution index: {details['ranking'][0]}")
print(f"TOPSIS score: {details['scores'][details['ranking'][0]]:.3f}")
# Use with OptimizationResult (auto-detects directions)
scores = topsis_from_result(
result,
weights={"objective_0": 0.4, "objective_1": 0.3, "objective_2": 0.3}
)
Weight Sensitivity Analysis¶
TOPSIS results depend on the weights you choose. Sensitivity analysis helps you understand how robust your rankings are:
from optiscope.analysis import sensitivity_analysis_topsis
sensitivity = sensitivity_analysis_topsis(
objectives=result.objectives,
base_weights=np.array([0.4, 0.3, 0.3]),
perturbation=0.1, # ±10% variation
n_samples=100
)
print(f"Top solution appears in top 5: {sensitivity['stability_top_5'][0]:.1%} of scenarios")
Comparing Weight Scenarios¶
Compare how different stakeholder preferences affect rankings:
from optiscope.analysis import compare_weight_scenarios
scenarios = {
"Cost-focused": np.array([0.6, 0.2, 0.2]),
"Performance-focused": np.array([0.2, 0.6, 0.2]),
"Balanced": np.array([0.33, 0.33, 0.34])
}
comparison = compare_weight_scenarios(
objectives=result.objectives,
weight_scenarios=scenarios
)
print(comparison) # DataFrame showing top solutions for each scenario
Knee Point Detection¶
A knee point is a solution on the Pareto front that represents the best compromise between objectives [4]. It's the point where a small improvement in one objective requires a large sacrifice in another, and the concept is similar to the "elbow method" used in cluster analysis.
Why Knee Points Matter¶
- Decision Support: Often the most preferred solution by decision-makers.
- Trade-off Visualization: Highlights the critical trade-off region.
- Automatic Selection: Provides a data-driven way to select a single solution.
Detection Methods¶
OptiScope implements multiple knee detection methods:
- Angle-based: Detects sharp turns in the Pareto front.
- Distance-based: Finds points farthest from the line connecting extremes.
- Trade-off-based: Identifies maximum changes in trade-off rates.
- Curvature-based: Detects high-curvature regions.
Usage¶
from optiscope.analysis import detect_knee_points, find_knee_region
# Detect single knee point
knee_indices = detect_knee_points(
objectives=result.objectives,
method="angle", # or "distance", "tradeoff", "curvature"
n_knees=1,
normalize=True
)
print(f"Knee point at index: {knee_indices[0]}")
# Find knee region (multiple nearby solutions)
region_indices, start, end = find_knee_region(
objectives=result.objectives,
width=0.1, # 10% of normalized space
method="angle"
)
print(f"Knee region contains {len(region_indices)} solutions")
Knee Quality Metrics¶
Evaluate how good your detected knee points are:
from optiscope.analysis import calculate_knee_quality_metrics
metrics = calculate_knee_quality_metrics(
objectives=result.objectives,
knee_indices=knee_indices,
normalize=True
)
print(f"Angle sharpness: {metrics['angle_sharpness']:.2f}")
print(f"Distance from line: {metrics['distance_from_line']:.3f}")
Feasibility Analysis¶
Not all optimization solutions satisfy constraints. OptiScope provides tools to identify and analyze feasible solutions.
Usage¶
from optiscope.analysis import is_feasible, get_feasible_mask
# Check if solutions are feasible
feasible_mask = get_feasible_mask(
result,
tolerance=1e-6 # Constraint violation tolerance
)
print(f"Feasible solutions: {feasible_mask.sum()} / {len(result)}")
# Create a result set of feasible solutions
feasible_set = ResultSet(
name="feasible",
indices=np.where(feasible_mask)[0].tolist(),
created_by="feasibility_analysis",
description="Solutions satisfying all constraints"
)
Combining Analysis Tools¶
The real power comes from combining multiple analysis tools:
# 1. Filter to feasible solutions
feasible_mask = get_feasible_mask(result)
# 2. Identify Pareto front among feasible solutions
pareto_mask = identify_pareto_front(
objectives=result.objectives,
directions=result.metadata.optimization_directions,
mask=feasible_mask
)
# 3. Apply Smart Pareto Filter to reduce complexity
pareto_indices = np.where(pareto_mask)[0]
filtered_indices = smart_pareto_filter(
objectives=result.objectives[pareto_indices],
epsilon=0.05
)
# 4. Detect knee point in filtered set
knee_in_filtered = detect_knee_points(
objectives=result.objectives[pareto_indices[filtered_indices]],
method="angle",
n_knees=1
)
final_knee_index = pareto_indices[filtered_indices[knee_in_filtered[0]]]
print(f"Recommended solution: {final_knee_index}")
Using Analysis Tools in the Dash App¶
All analysis tools are integrated into the Dash App:
- Pareto Filter Page: Apply Smart Pareto Filter with interactive epsilon adjustment.
- TOPSIS Page: Adjust weights with sliders and see rankings update in real-time.
- Knee Detection Page: Compare different detection methods visually.
- Result Set Creation: Automatically create result sets from analysis results.
The Dash App makes these tools accessible without writing code, while still providing full control over parameters.
References¶
[1] Wikipedia contributors. "Pareto efficiency." Wikipedia, The Free Encyclopedia. Available at: https://en.wikipedia.org/wiki/Pareto_efficiency
[2] Mattson, C. A., Mullur, A. A., & Messac, A. (2004). "Smart Pareto filter: obtaining a minimal representation of multiobjective design space." Engineering Optimization, 36(6), 721-740. DOI: 10.1080/03052150410001689875
[3] Hwang, C. L., & Yoon, K. (1981). Multiple Attribute Decision Making: Methods and Applications. Springer-Verlag. See also: Wikipedia contributors. "TOPSIS." Wikipedia, The Free Encyclopedia. Available at: https://en.wikipedia.org/wiki/TOPSIS
[4] Das, I. (1999). "On characterizing the 'knee' of the Pareto curve based on Normal-Boundary Intersection." Structural Optimization, 18(2-3), 107-115. DOI: 10.1007/s001580050096