Optiscope Plotting Examples¶
This notebook demonstrates the various plotting capabilities of the optiscope library, including:
- Pareto Front (2D/3D)
- Parallel Coordinates
- Scatter Matrix (SPLOM)
- Correlation Heatmap
- Time Series
- Optimization History
- Multi-Result Comparison
In [1]:
Copied!
import numpy as np
import pandas as pd
from optiscope import (
Constraint,
DesignVariable,
Objective,
OptimizationResult,
ProblemMetadata,
)
from optiscope.analysis import (
check_feasibility,
detect_knee_points,
identify_pareto_front,
)
from optiscope.core import OptimizationDirection
from optiscope.core.data_types import ConstraintType
from optiscope.plotting import (
plot_correlation_heatmap,
plot_parallel_coordinates,
plot_pareto_front_2d,
plot_pareto_front_3d,
plot_scatter_matrix,
)
from optiscope.plotting.time_series import (
plot_optimization_history,
plot_time_series,
)
import numpy as np
import pandas as pd
from optiscope import (
Constraint,
DesignVariable,
Objective,
OptimizationResult,
ProblemMetadata,
)
from optiscope.analysis import (
check_feasibility,
detect_knee_points,
identify_pareto_front,
)
from optiscope.core import OptimizationDirection
from optiscope.core.data_types import ConstraintType
from optiscope.plotting import (
plot_correlation_heatmap,
plot_parallel_coordinates,
plot_pareto_front_2d,
plot_pareto_front_3d,
plot_scatter_matrix,
)
from optiscope.plotting.time_series import (
plot_optimization_history,
plot_time_series,
)
In [2]:
Copied!
import plotly.io as pio
pio.renderers.default = "sphinx_gallery"
import plotly.io as pio
pio.renderers.default = "sphinx_gallery"
Data Generation¶
We generate a synthetic multi-objective optimization problem with 4 design variables and 3 objectives.
In [3]:
Copied!
def generate_test_problem(n_points: int = 200, seed: int = 42):
"""Generate synthetic multi-objective optimization problem."""
np.random.seed(seed)
def objective_function(x1, x2, x3, x4, n_points):
"""ZDT-like objective function with 3 objectives."""
f1 = x1
g = 1 + 9 * (x2 + x3 + x4) / 3
h1 = 1 - np.sqrt(f1 / g)
h2 = 1 - (f1 / g) ** 2
f2 = g * h1
f3 = g * h2
# Add some noise
f2 += np.random.normal(0, 0.1, n_points)
f3 += np.random.normal(0, 0.1, n_points)
return f1, f2, f3
def constraint_functions(x1, x2, x3, x4):
"""Define constraints."""
g1 = x1 + x2 - 12 # inequality constraint: g1 <= 0
g2 = x3 - x4 - 5 # inequality constraint: g2 <= 0
h1 = x3 - x4 # equality constraint: h1 = 0
return g1, g2, h1
def observable_functions(x1, x2, x3, x4, f1, f2):
"""Define observables."""
total_mass = x1 + x2 + x3 + x4
efficiency = f1 / (f2 + 1e-6)
cost = 100 * x1 + 50 * x2 + 75 * x3 + 60 * x4
return total_mass, efficiency, cost
# Design variables
x1 = np.random.uniform(0, 10, n_points)
x2 = np.random.uniform(0, 10, n_points)
x3 = np.random.uniform(0, 10, n_points)
x4 = np.random.uniform(0, 10, n_points)
# Solve problem by random sampling
f1, f2, f3 = objective_function(x1, x2, x3, x4, n_points)
g1, g2, h1 = constraint_functions(x1, x2, x3, x4)
total_mass, efficiency, cost = observable_functions(x1, x2, x3, x4, f1, f2)
iterations = np.arange(n_points)
# Create result
result = OptimizationResult(
problem_metadata=ProblemMetadata(
name="3-Objective Test Problem",
description="Synthetic multi-objective optimization test problem",
solver="Random Sampling",
n_design_variables=4,
n_objectives=2,
n_inequality_constraints=2,
n_equality_constraints=1,
n_evaluations=n_points,
solver_version="1.0",
computation_time=None,
),
design_variables=pd.DataFrame(
{
"x1_width": x1,
"x2_height": x2,
"x3_depth": x3,
"x4_thickness": x4,
"iteration": iterations,
}
),
objectives=pd.DataFrame({"f1_cost": f1, "f2_weight": f2, "f3_volume": f3}),
inequality_constraints=pd.DataFrame(
{"g1_size_constraint": g1, "g2_dimensional_constraint": g2}
),
equality_constraints=pd.DataFrame({"h1_balance": h1}),
observables=pd.DataFrame(
{"total_mass": total_mass, "efficiency": efficiency, "cost": cost}
),
)
# Add metadata
result.add_variable_metadata(
DesignVariable(name="x1_width", units="cm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="x2_height", units="cm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="x3_depth", units="cm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="x4_thickness", units="mm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="iteration", units=None, lower_bound=0, upper_bound=n_points)
)
result.add_variable_metadata(
Objective(name="f1_cost", units="$", direction=OptimizationDirection.MINIMIZE)
)
result.add_variable_metadata(
Objective(name="f2_weight", units="kg", direction=OptimizationDirection.MINIMIZE)
)
result.add_variable_metadata(
Objective(name="f3_volume", units="cm³", direction=OptimizationDirection.MINIMIZE)
)
result.add_variable_metadata(
Constraint(
name="g1_size_constraint", constraint_type=ConstraintType.INEQUALITY, reference_value=0
)
)
result.add_variable_metadata(
Constraint(
name="g2_dimensional_constraint",
constraint_type=ConstraintType.INEQUALITY,
reference_value=0,
)
)
result.add_variable_metadata(
Constraint(
name="h1_balance",
constraint_type=ConstraintType.EQUALITY,
reference_value=0,
tolerance=0.5,
)
)
return result
def setup_result_sets(result):
"""Create useful result sets for visualization."""
# 1. Find feasible solutions
feasible_mask = check_feasibility(result)
feasible_indices = np.where(feasible_mask)[0].tolist()
result.create_set(
name="feasible",
indices=feasible_indices,
created_by="constraint_filter",
description="Solutions satisfying all constraints",
color="#2ecc71",
)
# 2. Create Pareto set
pareto_mask = identify_pareto_front(
result.objectives,
directions=result.optimization_directions,
mask=feasible_mask,
)
pareto_indices = np.where(pareto_mask)[0].tolist()
pareto_set = result.create_set(
name="pareto_front",
indices=pareto_indices,
created_by="smart_filter",
description="Pareto optimal solutions",
color="#e74c3c",
)
# 3. Knee points
if pareto_set and len(pareto_set) >= 5:
pareto_objectives = result.objectives.iloc[pareto_indices][["f1_cost", "f2_weight"]]
knee_indices_relative = detect_knee_points(
pareto_objectives, method="angle", n_knees=3, normalize=True
)
knee_indices = [pareto_indices[i] for i in knee_indices_relative]
result.create_set(
name="knee_points",
indices=knee_indices,
created_by="knee_detection",
description="Knee points on Pareto front",
color="#9b59b6",
)
return result
def generate_test_problem(n_points: int = 200, seed: int = 42):
"""Generate synthetic multi-objective optimization problem."""
np.random.seed(seed)
def objective_function(x1, x2, x3, x4, n_points):
"""ZDT-like objective function with 3 objectives."""
f1 = x1
g = 1 + 9 * (x2 + x3 + x4) / 3
h1 = 1 - np.sqrt(f1 / g)
h2 = 1 - (f1 / g) ** 2
f2 = g * h1
f3 = g * h2
# Add some noise
f2 += np.random.normal(0, 0.1, n_points)
f3 += np.random.normal(0, 0.1, n_points)
return f1, f2, f3
def constraint_functions(x1, x2, x3, x4):
"""Define constraints."""
g1 = x1 + x2 - 12 # inequality constraint: g1 <= 0
g2 = x3 - x4 - 5 # inequality constraint: g2 <= 0
h1 = x3 - x4 # equality constraint: h1 = 0
return g1, g2, h1
def observable_functions(x1, x2, x3, x4, f1, f2):
"""Define observables."""
total_mass = x1 + x2 + x3 + x4
efficiency = f1 / (f2 + 1e-6)
cost = 100 * x1 + 50 * x2 + 75 * x3 + 60 * x4
return total_mass, efficiency, cost
# Design variables
x1 = np.random.uniform(0, 10, n_points)
x2 = np.random.uniform(0, 10, n_points)
x3 = np.random.uniform(0, 10, n_points)
x4 = np.random.uniform(0, 10, n_points)
# Solve problem by random sampling
f1, f2, f3 = objective_function(x1, x2, x3, x4, n_points)
g1, g2, h1 = constraint_functions(x1, x2, x3, x4)
total_mass, efficiency, cost = observable_functions(x1, x2, x3, x4, f1, f2)
iterations = np.arange(n_points)
# Create result
result = OptimizationResult(
problem_metadata=ProblemMetadata(
name="3-Objective Test Problem",
description="Synthetic multi-objective optimization test problem",
solver="Random Sampling",
n_design_variables=4,
n_objectives=2,
n_inequality_constraints=2,
n_equality_constraints=1,
n_evaluations=n_points,
solver_version="1.0",
computation_time=None,
),
design_variables=pd.DataFrame(
{
"x1_width": x1,
"x2_height": x2,
"x3_depth": x3,
"x4_thickness": x4,
"iteration": iterations,
}
),
objectives=pd.DataFrame({"f1_cost": f1, "f2_weight": f2, "f3_volume": f3}),
inequality_constraints=pd.DataFrame(
{"g1_size_constraint": g1, "g2_dimensional_constraint": g2}
),
equality_constraints=pd.DataFrame({"h1_balance": h1}),
observables=pd.DataFrame(
{"total_mass": total_mass, "efficiency": efficiency, "cost": cost}
),
)
# Add metadata
result.add_variable_metadata(
DesignVariable(name="x1_width", units="cm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="x2_height", units="cm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="x3_depth", units="cm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="x4_thickness", units="mm", lower_bound=0, upper_bound=10)
)
result.add_variable_metadata(
DesignVariable(name="iteration", units=None, lower_bound=0, upper_bound=n_points)
)
result.add_variable_metadata(
Objective(name="f1_cost", units="$", direction=OptimizationDirection.MINIMIZE)
)
result.add_variable_metadata(
Objective(name="f2_weight", units="kg", direction=OptimizationDirection.MINIMIZE)
)
result.add_variable_metadata(
Objective(name="f3_volume", units="cm³", direction=OptimizationDirection.MINIMIZE)
)
result.add_variable_metadata(
Constraint(
name="g1_size_constraint", constraint_type=ConstraintType.INEQUALITY, reference_value=0
)
)
result.add_variable_metadata(
Constraint(
name="g2_dimensional_constraint",
constraint_type=ConstraintType.INEQUALITY,
reference_value=0,
)
)
result.add_variable_metadata(
Constraint(
name="h1_balance",
constraint_type=ConstraintType.EQUALITY,
reference_value=0,
tolerance=0.5,
)
)
return result
def setup_result_sets(result):
"""Create useful result sets for visualization."""
# 1. Find feasible solutions
feasible_mask = check_feasibility(result)
feasible_indices = np.where(feasible_mask)[0].tolist()
result.create_set(
name="feasible",
indices=feasible_indices,
created_by="constraint_filter",
description="Solutions satisfying all constraints",
color="#2ecc71",
)
# 2. Create Pareto set
pareto_mask = identify_pareto_front(
result.objectives,
directions=result.optimization_directions,
mask=feasible_mask,
)
pareto_indices = np.where(pareto_mask)[0].tolist()
pareto_set = result.create_set(
name="pareto_front",
indices=pareto_indices,
created_by="smart_filter",
description="Pareto optimal solutions",
color="#e74c3c",
)
# 3. Knee points
if pareto_set and len(pareto_set) >= 5:
pareto_objectives = result.objectives.iloc[pareto_indices][["f1_cost", "f2_weight"]]
knee_indices_relative = detect_knee_points(
pareto_objectives, method="angle", n_knees=3, normalize=True
)
knee_indices = [pareto_indices[i] for i in knee_indices_relative]
result.create_set(
name="knee_points",
indices=knee_indices,
created_by="knee_detection",
description="Knee points on Pareto front",
color="#9b59b6",
)
return result
In [4]:
Copied!
print("Generating test data...")
result = generate_test_problem(n_points=200)
print("Setting up result sets...")
result = setup_result_sets(result)
print("Generating test data...")
result = generate_test_problem(n_points=200)
print("Setting up result sets...")
result = setup_result_sets(result)
Generating test data... Setting up result sets...
1. 2D Pareto Front¶
Visualize the trade-off between two objectives.
In [5]:
Copied!
fig_2d = plot_pareto_front_2d(
result,
obj_x="f1_cost",
obj_y="f2_weight",
pareto_set="pareto_front",
additional_sets=["knee_points", "feasible"],
show_dominated=False,
show_ideal_nadir=False,
title="2D Pareto Front (Cost vs. Weight)",
)
fig_2d.show()
fig_2d = plot_pareto_front_2d(
result,
obj_x="f1_cost",
obj_y="f2_weight",
pareto_set="pareto_front",
additional_sets=["knee_points", "feasible"],
show_dominated=False,
show_ideal_nadir=False,
title="2D Pareto Front (Cost vs. Weight)",
)
fig_2d.show()
2. 3D Pareto Front¶
Visualize the trade-off between three objectives.
In [6]:
Copied!
fig_3d = plot_pareto_front_3d(
result,
obj_x="f1_cost",
obj_y="f2_weight",
obj_z="f3_volume",
pareto_set="pareto_front",
show_dominated=True,
show_surface=False,
title="3D Pareto Front",
)
fig_3d.show()
fig_3d = plot_pareto_front_3d(
result,
obj_x="f1_cost",
obj_y="f2_weight",
obj_z="f3_volume",
pareto_set="pareto_front",
show_dominated=True,
show_surface=False,
title="3D Pareto Front",
)
fig_3d.show()
3. Parallel Coordinates¶
Visualize high-dimensional data.
In [7]:
Copied!
fig_pc = plot_parallel_coordinates(
result,
include_design_vars=True,
include_objectives=True,
include_constraints=False,
result_sets=["pareto_front", "feasible"],
title="Parallel Coordinates Plot",
)
fig_pc.show()
fig_pc = plot_parallel_coordinates(
result,
include_design_vars=True,
include_objectives=True,
include_constraints=False,
result_sets=["pareto_front", "feasible"],
title="Parallel Coordinates Plot",
)
fig_pc.show()
4. Scatter Matrix (SPLOM)¶
Visualize pairwise relationships between variables.
In [8]:
Copied!
fig_splom = plot_scatter_matrix(
result,
include_objectives=True,
include_design_vars=True,
max_vars=6,
highlight_feasible=True,
show_distributions=True,
show_correlations=True,
marker_size=4,
title="Scatter Plot Matrix",
)
fig_splom.show()
fig_splom = plot_scatter_matrix(
result,
include_objectives=True,
include_design_vars=True,
max_vars=6,
highlight_feasible=True,
show_distributions=True,
show_correlations=True,
marker_size=4,
title="Scatter Plot Matrix",
)
fig_splom.show()
5. Correlation Heatmap¶
Visualize correlations between variables.
In [9]:
Copied!
fig_corr = plot_correlation_heatmap(
result,
include_objectives=True,
include_design_vars=True,
method="pearson",
title="Pearson Correlation Heatmap",
)
fig_corr.show()
fig_corr = plot_correlation_heatmap(
result,
include_objectives=True,
include_design_vars=True,
method="pearson",
title="Pearson Correlation Heatmap",
)
fig_corr.show()
6. Time Series¶
Visualize the evolution of variables over iterations.
In [10]:
Copied!
fig_ts = plot_time_series(
result,
columns=["x1_width", "x2_height", "x3_depth", "x4_thickness"],
data_attr="design_variables",
title="Design Variables Evolution",
show_markers=True,
line_width=2,
marker_size=4,
)
fig_ts.show()
fig_ts = plot_time_series(
result,
columns=["x1_width", "x2_height", "x3_depth", "x4_thickness"],
data_attr="design_variables",
title="Design Variables Evolution",
show_markers=True,
line_width=2,
marker_size=4,
)
fig_ts.show()
7. Optimization History¶
Visualize the full history of the optimization process.
In [11]:
Copied!
figs_hist = plot_optimization_history(
result,
variables=["x1_width", "x2_height"],
objectives=["f1_cost", "f2_weight", "f3_volume"],
constraints=["g1_size_constraint", "h1_balance"],
observables=["efficiency", "total_mass"],
show_markers=True,
line_width=2,
marker_size=4,
)
# Display one of the figures
figs_hist["objectives"].show()
figs_hist = plot_optimization_history(
result,
variables=["x1_width", "x2_height"],
objectives=["f1_cost", "f2_weight", "f3_volume"],
constraints=["g1_size_constraint", "h1_balance"],
observables=["efficiency", "total_mass"],
show_markers=True,
line_width=2,
marker_size=4,
)
# Display one of the figures
figs_hist["objectives"].show()