morphml 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of morphml might be problematic. Click here for more details.
- morphml/__init__.py +14 -0
- morphml/api/__init__.py +26 -0
- morphml/api/app.py +326 -0
- morphml/api/auth.py +193 -0
- morphml/api/client.py +338 -0
- morphml/api/models.py +132 -0
- morphml/api/rate_limit.py +192 -0
- morphml/benchmarking/__init__.py +36 -0
- morphml/benchmarking/comparison.py +430 -0
- morphml/benchmarks/__init__.py +56 -0
- morphml/benchmarks/comparator.py +409 -0
- morphml/benchmarks/datasets.py +280 -0
- morphml/benchmarks/metrics.py +199 -0
- morphml/benchmarks/openml_suite.py +201 -0
- morphml/benchmarks/problems.py +289 -0
- morphml/benchmarks/suite.py +318 -0
- morphml/cli/__init__.py +5 -0
- morphml/cli/commands/experiment.py +329 -0
- morphml/cli/main.py +457 -0
- morphml/cli/quickstart.py +312 -0
- morphml/config.py +278 -0
- morphml/constraints/__init__.py +19 -0
- morphml/constraints/handler.py +205 -0
- morphml/constraints/predicates.py +285 -0
- morphml/core/__init__.py +3 -0
- morphml/core/crossover.py +449 -0
- morphml/core/dsl/README.md +359 -0
- morphml/core/dsl/__init__.py +72 -0
- morphml/core/dsl/ast_nodes.py +364 -0
- morphml/core/dsl/compiler.py +318 -0
- morphml/core/dsl/layers.py +368 -0
- morphml/core/dsl/lexer.py +336 -0
- morphml/core/dsl/parser.py +455 -0
- morphml/core/dsl/search_space.py +386 -0
- morphml/core/dsl/syntax.py +199 -0
- morphml/core/dsl/type_system.py +361 -0
- morphml/core/dsl/validator.py +386 -0
- morphml/core/graph/__init__.py +40 -0
- morphml/core/graph/edge.py +124 -0
- morphml/core/graph/graph.py +507 -0
- morphml/core/graph/mutations.py +409 -0
- morphml/core/graph/node.py +196 -0
- morphml/core/graph/serialization.py +361 -0
- morphml/core/graph/visualization.py +431 -0
- morphml/core/objectives/__init__.py +20 -0
- morphml/core/search/__init__.py +33 -0
- morphml/core/search/individual.py +252 -0
- morphml/core/search/parameters.py +453 -0
- morphml/core/search/population.py +375 -0
- morphml/core/search/search_engine.py +340 -0
- morphml/distributed/__init__.py +76 -0
- morphml/distributed/fault_tolerance.py +497 -0
- morphml/distributed/health_monitor.py +348 -0
- morphml/distributed/master.py +709 -0
- morphml/distributed/proto/README.md +224 -0
- morphml/distributed/proto/__init__.py +74 -0
- morphml/distributed/proto/worker.proto +170 -0
- morphml/distributed/proto/worker_pb2.py +79 -0
- morphml/distributed/proto/worker_pb2_grpc.py +423 -0
- morphml/distributed/resource_manager.py +416 -0
- morphml/distributed/scheduler.py +567 -0
- morphml/distributed/storage/__init__.py +33 -0
- morphml/distributed/storage/artifacts.py +381 -0
- morphml/distributed/storage/cache.py +366 -0
- morphml/distributed/storage/checkpointing.py +329 -0
- morphml/distributed/storage/database.py +459 -0
- morphml/distributed/worker.py +549 -0
- morphml/evaluation/__init__.py +5 -0
- morphml/evaluation/heuristic.py +237 -0
- morphml/exceptions.py +55 -0
- morphml/execution/__init__.py +5 -0
- morphml/execution/local_executor.py +350 -0
- morphml/integrations/__init__.py +28 -0
- morphml/integrations/jax_adapter.py +206 -0
- morphml/integrations/pytorch_adapter.py +530 -0
- morphml/integrations/sklearn_adapter.py +206 -0
- morphml/integrations/tensorflow_adapter.py +230 -0
- morphml/logging_config.py +93 -0
- morphml/meta_learning/__init__.py +66 -0
- morphml/meta_learning/architecture_similarity.py +277 -0
- morphml/meta_learning/experiment_database.py +240 -0
- morphml/meta_learning/knowledge_base/__init__.py +19 -0
- morphml/meta_learning/knowledge_base/embedder.py +179 -0
- morphml/meta_learning/knowledge_base/knowledge_base.py +313 -0
- morphml/meta_learning/knowledge_base/meta_features.py +265 -0
- morphml/meta_learning/knowledge_base/vector_store.py +271 -0
- morphml/meta_learning/predictors/__init__.py +27 -0
- morphml/meta_learning/predictors/ensemble.py +221 -0
- morphml/meta_learning/predictors/gnn_predictor.py +552 -0
- morphml/meta_learning/predictors/learning_curve.py +231 -0
- morphml/meta_learning/predictors/proxy_metrics.py +261 -0
- morphml/meta_learning/strategy_evolution/__init__.py +27 -0
- morphml/meta_learning/strategy_evolution/adaptive_optimizer.py +226 -0
- morphml/meta_learning/strategy_evolution/bandit.py +276 -0
- morphml/meta_learning/strategy_evolution/portfolio.py +230 -0
- morphml/meta_learning/transfer.py +581 -0
- morphml/meta_learning/warm_start.py +286 -0
- morphml/optimizers/__init__.py +74 -0
- morphml/optimizers/adaptive_operators.py +399 -0
- morphml/optimizers/bayesian/__init__.py +52 -0
- morphml/optimizers/bayesian/acquisition.py +387 -0
- morphml/optimizers/bayesian/base.py +319 -0
- morphml/optimizers/bayesian/gaussian_process.py +635 -0
- morphml/optimizers/bayesian/smac.py +534 -0
- morphml/optimizers/bayesian/tpe.py +411 -0
- morphml/optimizers/differential_evolution.py +220 -0
- morphml/optimizers/evolutionary/__init__.py +61 -0
- morphml/optimizers/evolutionary/cma_es.py +416 -0
- morphml/optimizers/evolutionary/differential_evolution.py +556 -0
- morphml/optimizers/evolutionary/encoding.py +426 -0
- morphml/optimizers/evolutionary/particle_swarm.py +449 -0
- morphml/optimizers/genetic_algorithm.py +486 -0
- morphml/optimizers/gradient_based/__init__.py +22 -0
- morphml/optimizers/gradient_based/darts.py +550 -0
- morphml/optimizers/gradient_based/enas.py +585 -0
- morphml/optimizers/gradient_based/operations.py +474 -0
- morphml/optimizers/gradient_based/utils.py +601 -0
- morphml/optimizers/hill_climbing.py +169 -0
- morphml/optimizers/multi_objective/__init__.py +56 -0
- morphml/optimizers/multi_objective/indicators.py +504 -0
- morphml/optimizers/multi_objective/nsga2.py +647 -0
- morphml/optimizers/multi_objective/visualization.py +427 -0
- morphml/optimizers/nsga2.py +308 -0
- morphml/optimizers/random_search.py +172 -0
- morphml/optimizers/simulated_annealing.py +181 -0
- morphml/plugins/__init__.py +35 -0
- morphml/plugins/custom_evaluator_example.py +81 -0
- morphml/plugins/custom_optimizer_example.py +63 -0
- morphml/plugins/plugin_system.py +454 -0
- morphml/reports/__init__.py +30 -0
- morphml/reports/generator.py +362 -0
- morphml/tracking/__init__.py +7 -0
- morphml/tracking/experiment.py +309 -0
- morphml/tracking/logger.py +301 -0
- morphml/tracking/reporter.py +357 -0
- morphml/utils/__init__.py +6 -0
- morphml/utils/checkpoint.py +189 -0
- morphml/utils/comparison.py +390 -0
- morphml/utils/export.py +407 -0
- morphml/utils/progress.py +392 -0
- morphml/utils/validation.py +392 -0
- morphml/version.py +7 -0
- morphml/visualization/__init__.py +50 -0
- morphml/visualization/analytics.py +423 -0
- morphml/visualization/architecture_diagrams.py +353 -0
- morphml/visualization/architecture_plot.py +223 -0
- morphml/visualization/convergence_plot.py +174 -0
- morphml/visualization/crossover_viz.py +386 -0
- morphml/visualization/graph_viz.py +338 -0
- morphml/visualization/pareto_plot.py +149 -0
- morphml/visualization/plotly_dashboards.py +422 -0
- morphml/visualization/population.py +309 -0
- morphml/visualization/progress.py +260 -0
- morphml-1.0.0.dist-info/METADATA +434 -0
- morphml-1.0.0.dist-info/RECORD +158 -0
- morphml-1.0.0.dist-info/WHEEL +4 -0
- morphml-1.0.0.dist-info/entry_points.txt +3 -0
- morphml-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"""Interactive Plotly dashboards for MorphML.
|
|
2
|
+
|
|
3
|
+
Create publication-quality interactive visualizations for NAS experiments.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
>>> from morphml.visualization.plotly_dashboards import InteractiveDashboard
|
|
7
|
+
>>> dashboard = InteractiveDashboard()
|
|
8
|
+
>>> fig = dashboard.create_experiment_dashboard(history)
|
|
9
|
+
>>> fig.show()
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any, Dict, List
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import plotly.express as px
|
|
18
|
+
import plotly.graph_objects as go
|
|
19
|
+
from plotly.subplots import make_subplots
|
|
20
|
+
|
|
21
|
+
PLOTLY_AVAILABLE = True
|
|
22
|
+
except ImportError:
|
|
23
|
+
PLOTLY_AVAILABLE = False
|
|
24
|
+
go = None
|
|
25
|
+
make_subplots = None
|
|
26
|
+
px = None
|
|
27
|
+
|
|
28
|
+
from morphml.logging_config import get_logger
|
|
29
|
+
|
|
30
|
+
logger = get_logger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class InteractiveDashboard:
|
|
34
|
+
"""
|
|
35
|
+
Create interactive Plotly dashboards for NAS experiments.
|
|
36
|
+
|
|
37
|
+
Provides comprehensive visualizations including:
|
|
38
|
+
- Convergence curves
|
|
39
|
+
- Pareto fronts (multi-objective)
|
|
40
|
+
- Architecture distributions
|
|
41
|
+
- Performance heatmaps
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> dashboard = InteractiveDashboard()
|
|
45
|
+
>>> fig = dashboard.create_experiment_dashboard(history)
|
|
46
|
+
>>> fig.write_html("dashboard.html")
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self):
|
|
50
|
+
"""Initialize dashboard generator."""
|
|
51
|
+
if not PLOTLY_AVAILABLE:
|
|
52
|
+
raise ImportError(
|
|
53
|
+
"Plotly is required for interactive dashboards. " "Install with: pip install plotly"
|
|
54
|
+
)
|
|
55
|
+
logger.info("Initialized InteractiveDashboard")
|
|
56
|
+
|
|
57
|
+
def create_experiment_dashboard(
|
|
58
|
+
self, history: Dict[str, Any], title: str = "Experiment Dashboard"
|
|
59
|
+
) -> "go.Figure":
|
|
60
|
+
"""
|
|
61
|
+
Create comprehensive experiment dashboard.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
history: Optimization history with keys:
|
|
65
|
+
- best_fitness: List of best fitness per generation
|
|
66
|
+
- mean_fitness: List of mean fitness per generation
|
|
67
|
+
- diversity: List of diversity scores
|
|
68
|
+
- architectures: List of architecture dicts
|
|
69
|
+
title: Dashboard title
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Plotly Figure with subplots
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
>>> fig = dashboard.create_experiment_dashboard(optimizer.history)
|
|
76
|
+
>>> fig.show()
|
|
77
|
+
"""
|
|
78
|
+
# Create subplots
|
|
79
|
+
fig = make_subplots(
|
|
80
|
+
rows=2,
|
|
81
|
+
cols=2,
|
|
82
|
+
subplot_titles=(
|
|
83
|
+
"Convergence Curve",
|
|
84
|
+
"Fitness Distribution",
|
|
85
|
+
"Diversity Over Time",
|
|
86
|
+
"Performance vs Complexity",
|
|
87
|
+
),
|
|
88
|
+
specs=[
|
|
89
|
+
[{"type": "scatter"}, {"type": "histogram"}],
|
|
90
|
+
[{"type": "scatter"}, {"type": "scatter"}],
|
|
91
|
+
],
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# 1. Convergence curve
|
|
95
|
+
generations = list(range(len(history.get("best_fitness", []))))
|
|
96
|
+
best_fitness = history.get("best_fitness", [])
|
|
97
|
+
mean_fitness = history.get("mean_fitness", [])
|
|
98
|
+
|
|
99
|
+
fig.add_trace(
|
|
100
|
+
go.Scatter(
|
|
101
|
+
x=generations,
|
|
102
|
+
y=best_fitness,
|
|
103
|
+
mode="lines+markers",
|
|
104
|
+
name="Best Fitness",
|
|
105
|
+
line={"color": "#FF6B6B", "width": 2},
|
|
106
|
+
marker={"size": 6},
|
|
107
|
+
),
|
|
108
|
+
row=1,
|
|
109
|
+
col=1,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
fig.add_trace(
|
|
113
|
+
go.Scatter(
|
|
114
|
+
x=generations,
|
|
115
|
+
y=mean_fitness,
|
|
116
|
+
mode="lines",
|
|
117
|
+
name="Mean Fitness",
|
|
118
|
+
line={"color": "#4ECDC4", "width": 2, "dash": "dash"},
|
|
119
|
+
),
|
|
120
|
+
row=1,
|
|
121
|
+
col=1,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# 2. Fitness distribution
|
|
125
|
+
all_fitness = []
|
|
126
|
+
for arch_list in history.get("population_fitness", []):
|
|
127
|
+
all_fitness.extend(arch_list)
|
|
128
|
+
|
|
129
|
+
if all_fitness:
|
|
130
|
+
fig.add_trace(
|
|
131
|
+
go.Histogram(
|
|
132
|
+
x=all_fitness,
|
|
133
|
+
nbinsx=30,
|
|
134
|
+
name="Fitness Distribution",
|
|
135
|
+
marker={"color": "#45B7D1"},
|
|
136
|
+
),
|
|
137
|
+
row=1,
|
|
138
|
+
col=2,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# 3. Diversity over time
|
|
142
|
+
diversity = history.get("diversity", [])
|
|
143
|
+
if diversity:
|
|
144
|
+
fig.add_trace(
|
|
145
|
+
go.Scatter(
|
|
146
|
+
x=generations,
|
|
147
|
+
y=diversity,
|
|
148
|
+
mode="lines+markers",
|
|
149
|
+
name="Diversity",
|
|
150
|
+
line={"color": "#96CEB4", "width": 2},
|
|
151
|
+
marker={"size": 6},
|
|
152
|
+
),
|
|
153
|
+
row=2,
|
|
154
|
+
col=1,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# 4. Performance vs Complexity
|
|
158
|
+
architectures = history.get("architectures", [])
|
|
159
|
+
if architectures:
|
|
160
|
+
params = [a.get("parameters", 0) for a in architectures]
|
|
161
|
+
fitness = [a.get("fitness", 0) for a in architectures]
|
|
162
|
+
|
|
163
|
+
fig.add_trace(
|
|
164
|
+
go.Scatter(
|
|
165
|
+
x=params,
|
|
166
|
+
y=fitness,
|
|
167
|
+
mode="markers",
|
|
168
|
+
name="Architectures",
|
|
169
|
+
marker={
|
|
170
|
+
"size": 8,
|
|
171
|
+
"color": fitness,
|
|
172
|
+
"colorscale": "Viridis",
|
|
173
|
+
"showscale": True,
|
|
174
|
+
"colorbar": {"title": "Fitness"},
|
|
175
|
+
},
|
|
176
|
+
text=[f"Params: {p:,}" for p in params],
|
|
177
|
+
hovertemplate="<b>Fitness:</b> %{y:.4f}<br><b>%{text}</b>",
|
|
178
|
+
),
|
|
179
|
+
row=2,
|
|
180
|
+
col=2,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Update layout
|
|
184
|
+
fig.update_xaxes(title_text="Generation", row=1, col=1)
|
|
185
|
+
fig.update_yaxes(title_text="Fitness", row=1, col=1)
|
|
186
|
+
|
|
187
|
+
fig.update_xaxes(title_text="Fitness", row=1, col=2)
|
|
188
|
+
fig.update_yaxes(title_text="Count", row=1, col=2)
|
|
189
|
+
|
|
190
|
+
fig.update_xaxes(title_text="Generation", row=2, col=1)
|
|
191
|
+
fig.update_yaxes(title_text="Diversity", row=2, col=1)
|
|
192
|
+
|
|
193
|
+
fig.update_xaxes(title_text="Parameters", row=2, col=2)
|
|
194
|
+
fig.update_yaxes(title_text="Fitness", row=2, col=2)
|
|
195
|
+
|
|
196
|
+
fig.update_layout(title=title, height=800, showlegend=True, template="plotly_white")
|
|
197
|
+
|
|
198
|
+
return fig
|
|
199
|
+
|
|
200
|
+
def create_pareto_front_3d(
|
|
201
|
+
self,
|
|
202
|
+
architectures: List[Dict[str, Any]],
|
|
203
|
+
objectives: List[str] = ["accuracy", "latency", "parameters"],
|
|
204
|
+
) -> "go.Figure":
|
|
205
|
+
"""
|
|
206
|
+
Create 3D Pareto front visualization.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
architectures: List of architecture dicts with objective values
|
|
210
|
+
objectives: List of 3 objective names
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
3D scatter plot
|
|
214
|
+
|
|
215
|
+
Example:
|
|
216
|
+
>>> fig = dashboard.create_pareto_front_3d(architectures)
|
|
217
|
+
>>> fig.show()
|
|
218
|
+
"""
|
|
219
|
+
if len(objectives) != 3:
|
|
220
|
+
raise ValueError("Exactly 3 objectives required for 3D plot")
|
|
221
|
+
|
|
222
|
+
# Extract objective values
|
|
223
|
+
x = [a.get(objectives[0], 0) for a in architectures]
|
|
224
|
+
y = [a.get(objectives[1], 0) for a in architectures]
|
|
225
|
+
z = [a.get(objectives[2], 0) for a in architectures]
|
|
226
|
+
|
|
227
|
+
# Create 3D scatter
|
|
228
|
+
fig = go.Figure(
|
|
229
|
+
data=[
|
|
230
|
+
go.Scatter3d(
|
|
231
|
+
x=x,
|
|
232
|
+
y=y,
|
|
233
|
+
z=z,
|
|
234
|
+
mode="markers",
|
|
235
|
+
marker={
|
|
236
|
+
"size": 6,
|
|
237
|
+
"color": x, # Color by first objective
|
|
238
|
+
"colorscale": "Viridis",
|
|
239
|
+
"showscale": True,
|
|
240
|
+
"colorbar": {"title": objectives[0]},
|
|
241
|
+
},
|
|
242
|
+
text=[f"ID: {a.get('id', 'N/A')}" for a in architectures],
|
|
243
|
+
hovertemplate=(
|
|
244
|
+
f"<b>{objectives[0]}:</b> %{{x:.4f}}<br>"
|
|
245
|
+
f"<b>{objectives[1]}:</b> %{{y:.4f}}<br>"
|
|
246
|
+
f"<b>{objectives[2]}:</b> %{{z:.4f}}<br>"
|
|
247
|
+
"<b>%{text}</b>"
|
|
248
|
+
),
|
|
249
|
+
)
|
|
250
|
+
]
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
fig.update_layout(
|
|
254
|
+
title="3D Pareto Front",
|
|
255
|
+
scene={
|
|
256
|
+
"xaxis_title": objectives[0],
|
|
257
|
+
"yaxis_title": objectives[1],
|
|
258
|
+
"zaxis_title": objectives[2],
|
|
259
|
+
},
|
|
260
|
+
height=700,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return fig
|
|
264
|
+
|
|
265
|
+
def create_operation_distribution(self, architectures: List[Dict[str, Any]]) -> "go.Figure":
|
|
266
|
+
"""
|
|
267
|
+
Create operation distribution pie chart.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
architectures: List of architecture dicts
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Pie chart figure
|
|
274
|
+
"""
|
|
275
|
+
# Count operations
|
|
276
|
+
operation_counts = {}
|
|
277
|
+
for arch in architectures:
|
|
278
|
+
for node in arch.get("nodes", []):
|
|
279
|
+
op = node.get("operation", "unknown")
|
|
280
|
+
operation_counts[op] = operation_counts.get(op, 0) + 1
|
|
281
|
+
|
|
282
|
+
# Create pie chart
|
|
283
|
+
fig = go.Figure(
|
|
284
|
+
data=[
|
|
285
|
+
go.Pie(
|
|
286
|
+
labels=list(operation_counts.keys()),
|
|
287
|
+
values=list(operation_counts.values()),
|
|
288
|
+
hole=0.3,
|
|
289
|
+
marker={"colors": px.colors.qualitative.Set3},
|
|
290
|
+
)
|
|
291
|
+
]
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
fig.update_layout(title="Operation Distribution", height=500)
|
|
295
|
+
|
|
296
|
+
return fig
|
|
297
|
+
|
|
298
|
+
def create_convergence_comparison(self, experiments: Dict[str, Dict[str, Any]]) -> "go.Figure":
|
|
299
|
+
"""
|
|
300
|
+
Compare convergence across multiple experiments.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
experiments: Dict mapping experiment names to history dicts
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Line plot comparing convergence
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
>>> experiments = {
|
|
310
|
+
... 'GA': ga_history,
|
|
311
|
+
... 'Random': random_history,
|
|
312
|
+
... 'Bayesian': bayesian_history
|
|
313
|
+
... }
|
|
314
|
+
>>> fig = dashboard.create_convergence_comparison(experiments)
|
|
315
|
+
"""
|
|
316
|
+
fig = go.Figure()
|
|
317
|
+
|
|
318
|
+
colors = px.colors.qualitative.Set1
|
|
319
|
+
|
|
320
|
+
for i, (name, history) in enumerate(experiments.items()):
|
|
321
|
+
generations = list(range(len(history.get("best_fitness", []))))
|
|
322
|
+
best_fitness = history.get("best_fitness", [])
|
|
323
|
+
|
|
324
|
+
fig.add_trace(
|
|
325
|
+
go.Scatter(
|
|
326
|
+
x=generations,
|
|
327
|
+
y=best_fitness,
|
|
328
|
+
mode="lines+markers",
|
|
329
|
+
name=name,
|
|
330
|
+
line={"color": colors[i % len(colors)], "width": 2},
|
|
331
|
+
marker={"size": 6},
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
fig.update_layout(
|
|
336
|
+
title="Convergence Comparison",
|
|
337
|
+
xaxis_title="Generation",
|
|
338
|
+
yaxis_title="Best Fitness",
|
|
339
|
+
height=500,
|
|
340
|
+
template="plotly_white",
|
|
341
|
+
hovermode="x unified",
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
return fig
|
|
345
|
+
|
|
346
|
+
def create_performance_heatmap(
|
|
347
|
+
self,
|
|
348
|
+
architectures: List[Dict[str, Any]],
|
|
349
|
+
x_metric: str = "depth",
|
|
350
|
+
y_metric: str = "width",
|
|
351
|
+
z_metric: str = "fitness",
|
|
352
|
+
) -> "go.Figure":
|
|
353
|
+
"""
|
|
354
|
+
Create performance heatmap.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
architectures: List of architecture dicts
|
|
358
|
+
x_metric: Metric for x-axis
|
|
359
|
+
y_metric: Metric for y-axis
|
|
360
|
+
z_metric: Metric for color (performance)
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Heatmap figure
|
|
364
|
+
"""
|
|
365
|
+
# Extract metrics
|
|
366
|
+
x_values = [a.get(x_metric, 0) for a in architectures]
|
|
367
|
+
y_values = [a.get(y_metric, 0) for a in architectures]
|
|
368
|
+
z_values = [a.get(z_metric, 0) for a in architectures]
|
|
369
|
+
|
|
370
|
+
# Create bins
|
|
371
|
+
x_bins = np.linspace(min(x_values), max(x_values), 20)
|
|
372
|
+
y_bins = np.linspace(min(y_values), max(y_values), 20)
|
|
373
|
+
|
|
374
|
+
# Compute heatmap
|
|
375
|
+
heatmap, xedges, yedges = np.histogram2d(
|
|
376
|
+
x_values, y_values, bins=[x_bins, y_bins], weights=z_values
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
counts, _, _ = np.histogram2d(x_values, y_values, bins=[x_bins, y_bins])
|
|
380
|
+
heatmap = np.divide(heatmap, counts, where=counts != 0)
|
|
381
|
+
|
|
382
|
+
# Create heatmap
|
|
383
|
+
fig = go.Figure(
|
|
384
|
+
data=go.Heatmap(
|
|
385
|
+
z=heatmap.T,
|
|
386
|
+
x=xedges[:-1],
|
|
387
|
+
y=yedges[:-1],
|
|
388
|
+
colorscale="Viridis",
|
|
389
|
+
colorbar={"title": z_metric},
|
|
390
|
+
)
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
fig.update_layout(
|
|
394
|
+
title=f"{z_metric} Heatmap", xaxis_title=x_metric, yaxis_title=y_metric, height=500
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
return fig
|
|
398
|
+
|
|
399
|
+
def export_dashboard(self, fig: "go.Figure", output_path: str, format: str = "html"):
|
|
400
|
+
"""
|
|
401
|
+
Export dashboard to file.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
fig: Plotly figure
|
|
405
|
+
output_path: Output file path
|
|
406
|
+
format: Export format ('html', 'png', 'svg', 'pdf')
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
>>> dashboard.export_dashboard(fig, "dashboard.html")
|
|
410
|
+
"""
|
|
411
|
+
if format == "html":
|
|
412
|
+
fig.write_html(output_path)
|
|
413
|
+
elif format == "png":
|
|
414
|
+
fig.write_image(output_path, format="png")
|
|
415
|
+
elif format == "svg":
|
|
416
|
+
fig.write_image(output_path, format="svg")
|
|
417
|
+
elif format == "pdf":
|
|
418
|
+
fig.write_image(output_path, format="pdf")
|
|
419
|
+
else:
|
|
420
|
+
raise ValueError(f"Unsupported format: {format}")
|
|
421
|
+
|
|
422
|
+
logger.info(f"Exported dashboard to {output_path}")
|