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,392 @@
|
|
|
1
|
+
"""Progress tracking utilities for optimization.
|
|
2
|
+
|
|
3
|
+
Provides rich progress bars and status displays for long-running NAS operations.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
>>> from morphml.utils.progress import OptimizationProgress
|
|
7
|
+
>>>
|
|
8
|
+
>>> progress = OptimizationProgress(total_generations=100)
|
|
9
|
+
>>> for gen in range(100):
|
|
10
|
+
... progress.update(gen, best_fitness=0.95, diversity=0.7)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from datetime import datetime, timedelta
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.layout import Layout
|
|
19
|
+
from rich.live import Live
|
|
20
|
+
from rich.panel import Panel
|
|
21
|
+
from rich.progress import (
|
|
22
|
+
BarColumn,
|
|
23
|
+
Progress,
|
|
24
|
+
SpinnerColumn,
|
|
25
|
+
TaskProgressColumn,
|
|
26
|
+
TextColumn,
|
|
27
|
+
TimeRemainingColumn,
|
|
28
|
+
)
|
|
29
|
+
from rich.table import Table
|
|
30
|
+
|
|
31
|
+
RICH_AVAILABLE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
RICH_AVAILABLE = False
|
|
34
|
+
|
|
35
|
+
from morphml.logging_config import get_logger
|
|
36
|
+
|
|
37
|
+
logger = get_logger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OptimizationProgress:
|
|
41
|
+
"""
|
|
42
|
+
Track and display optimization progress with rich formatting.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
total_generations: Total number of generations
|
|
46
|
+
show_stats: Whether to show detailed statistics
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> progress = OptimizationProgress(total_generations=50)
|
|
50
|
+
>>> progress.start()
|
|
51
|
+
>>> for gen in range(50):
|
|
52
|
+
... progress.update(gen, best_fitness=0.9, diversity=0.5)
|
|
53
|
+
>>> progress.finish()
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
total_generations: int,
|
|
59
|
+
show_stats: bool = True,
|
|
60
|
+
refresh_rate: float = 0.5,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Initialize progress tracker.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
total_generations: Total number of generations
|
|
67
|
+
show_stats: Whether to show detailed statistics
|
|
68
|
+
refresh_rate: Update frequency in seconds
|
|
69
|
+
"""
|
|
70
|
+
self.total_generations = total_generations
|
|
71
|
+
self.show_stats = show_stats
|
|
72
|
+
self.refresh_rate = refresh_rate
|
|
73
|
+
|
|
74
|
+
self.start_time = None
|
|
75
|
+
self.current_gen = 0
|
|
76
|
+
self.best_fitness = 0.0
|
|
77
|
+
self.history = []
|
|
78
|
+
|
|
79
|
+
if RICH_AVAILABLE:
|
|
80
|
+
self.console = Console()
|
|
81
|
+
self.progress = None
|
|
82
|
+
self.task_id = None
|
|
83
|
+
else:
|
|
84
|
+
logger.warning("Rich not available. Install with: pip install rich")
|
|
85
|
+
self.console = None
|
|
86
|
+
|
|
87
|
+
def start(self):
|
|
88
|
+
"""Start progress tracking."""
|
|
89
|
+
self.start_time = datetime.now()
|
|
90
|
+
|
|
91
|
+
if RICH_AVAILABLE and self.console:
|
|
92
|
+
self.progress = Progress(
|
|
93
|
+
SpinnerColumn(),
|
|
94
|
+
TextColumn("[progress.description]{task.description}"),
|
|
95
|
+
BarColumn(),
|
|
96
|
+
TaskProgressColumn(),
|
|
97
|
+
TimeRemainingColumn(),
|
|
98
|
+
console=self.console,
|
|
99
|
+
)
|
|
100
|
+
self.progress.start()
|
|
101
|
+
self.task_id = self.progress.add_task(
|
|
102
|
+
"[cyan]Optimizing...", total=self.total_generations
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
print(f"Starting optimization: {self.total_generations} generations")
|
|
106
|
+
|
|
107
|
+
def update(
|
|
108
|
+
self,
|
|
109
|
+
generation: int,
|
|
110
|
+
best_fitness: Optional[float] = None,
|
|
111
|
+
diversity: Optional[float] = None,
|
|
112
|
+
**kwargs,
|
|
113
|
+
):
|
|
114
|
+
"""
|
|
115
|
+
Update progress.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
generation: Current generation number
|
|
119
|
+
best_fitness: Best fitness so far
|
|
120
|
+
diversity: Population diversity
|
|
121
|
+
**kwargs: Additional statistics
|
|
122
|
+
"""
|
|
123
|
+
self.current_gen = generation
|
|
124
|
+
|
|
125
|
+
if best_fitness is not None:
|
|
126
|
+
self.best_fitness = best_fitness
|
|
127
|
+
|
|
128
|
+
# Track history
|
|
129
|
+
self.history.append(
|
|
130
|
+
{
|
|
131
|
+
"generation": generation,
|
|
132
|
+
"best_fitness": best_fitness,
|
|
133
|
+
"diversity": diversity,
|
|
134
|
+
**kwargs,
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if RICH_AVAILABLE and self.progress:
|
|
139
|
+
# Update progress bar
|
|
140
|
+
self.progress.update(
|
|
141
|
+
self.task_id,
|
|
142
|
+
completed=generation + 1,
|
|
143
|
+
description=f"[cyan]Gen {generation+1}/{self.total_generations} | Best: {self.best_fitness:.4f}",
|
|
144
|
+
)
|
|
145
|
+
else:
|
|
146
|
+
# Simple text output
|
|
147
|
+
if generation % 10 == 0 or generation == self.total_generations - 1:
|
|
148
|
+
elapsed = (datetime.now() - self.start_time).total_seconds()
|
|
149
|
+
print(
|
|
150
|
+
f"Gen {generation+1}/{self.total_generations} | "
|
|
151
|
+
f"Best: {self.best_fitness:.4f} | "
|
|
152
|
+
f"Time: {elapsed:.1f}s"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def finish(self):
|
|
156
|
+
"""Finish progress tracking and show summary."""
|
|
157
|
+
if RICH_AVAILABLE and self.progress:
|
|
158
|
+
self.progress.stop()
|
|
159
|
+
self._show_summary()
|
|
160
|
+
else:
|
|
161
|
+
elapsed = (datetime.now() - self.start_time).total_seconds()
|
|
162
|
+
print("\nOptimization complete!")
|
|
163
|
+
print(f"Total time: {elapsed:.2f}s")
|
|
164
|
+
print(f"Best fitness: {self.best_fitness:.4f}")
|
|
165
|
+
|
|
166
|
+
def _show_summary(self):
|
|
167
|
+
"""Show optimization summary."""
|
|
168
|
+
if not RICH_AVAILABLE:
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
elapsed = datetime.now() - self.start_time
|
|
172
|
+
|
|
173
|
+
# Create summary table
|
|
174
|
+
table = Table(title="Optimization Summary", show_header=True)
|
|
175
|
+
table.add_column("Metric", style="cyan")
|
|
176
|
+
table.add_column("Value", style="green")
|
|
177
|
+
|
|
178
|
+
table.add_row("Total Generations", str(self.total_generations))
|
|
179
|
+
table.add_row("Best Fitness", f"{self.best_fitness:.6f}")
|
|
180
|
+
table.add_row("Total Time", str(elapsed).split(".")[0])
|
|
181
|
+
table.add_row(
|
|
182
|
+
"Time per Generation", f"{elapsed.total_seconds() / self.total_generations:.2f}s"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if self.history:
|
|
186
|
+
improvements = sum(
|
|
187
|
+
1
|
|
188
|
+
for i in range(1, len(self.history))
|
|
189
|
+
if self.history[i].get("best_fitness", 0)
|
|
190
|
+
> self.history[i - 1].get("best_fitness", 0)
|
|
191
|
+
)
|
|
192
|
+
table.add_row("Improvements", str(improvements))
|
|
193
|
+
|
|
194
|
+
self.console.print(table)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class SimpleProgressBar:
|
|
198
|
+
"""
|
|
199
|
+
Simple progress bar without rich dependency.
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
>>> bar = SimpleProgressBar(total=100, desc="Processing")
|
|
203
|
+
>>> for i in range(100):
|
|
204
|
+
... bar.update(i)
|
|
205
|
+
>>> bar.finish()
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
def __init__(self, total: int, desc: str = "Progress", width: int = 50):
|
|
209
|
+
"""
|
|
210
|
+
Initialize simple progress bar.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
total: Total number of steps
|
|
214
|
+
desc: Description
|
|
215
|
+
width: Bar width in characters
|
|
216
|
+
"""
|
|
217
|
+
self.total = total
|
|
218
|
+
self.desc = desc
|
|
219
|
+
self.width = width
|
|
220
|
+
self.current = 0
|
|
221
|
+
self.start_time = datetime.now()
|
|
222
|
+
|
|
223
|
+
def update(self, current: int):
|
|
224
|
+
"""Update progress bar."""
|
|
225
|
+
self.current = current
|
|
226
|
+
|
|
227
|
+
# Calculate progress
|
|
228
|
+
progress = current / self.total
|
|
229
|
+
filled = int(self.width * progress)
|
|
230
|
+
bar = "█" * filled + "░" * (self.width - filled)
|
|
231
|
+
|
|
232
|
+
# Calculate time
|
|
233
|
+
elapsed = (datetime.now() - self.start_time).total_seconds()
|
|
234
|
+
if current > 0:
|
|
235
|
+
eta = elapsed * (self.total - current) / current
|
|
236
|
+
eta_str = str(timedelta(seconds=int(eta)))
|
|
237
|
+
else:
|
|
238
|
+
eta_str = "??:??:??"
|
|
239
|
+
|
|
240
|
+
# Print bar
|
|
241
|
+
print(
|
|
242
|
+
f"\r{self.desc}: |{bar}| {current}/{self.total} [{progress*100:.1f}%] ETA: {eta_str}",
|
|
243
|
+
end="",
|
|
244
|
+
flush=True,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def finish(self):
|
|
248
|
+
"""Finish progress bar."""
|
|
249
|
+
elapsed = (datetime.now() - self.start_time).total_seconds()
|
|
250
|
+
print(f"\n{self.desc} complete! Time: {elapsed:.2f}s")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def create_progress_tracker(total_generations: int, use_rich: bool = True, **kwargs) -> Any:
|
|
254
|
+
"""
|
|
255
|
+
Create appropriate progress tracker.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
total_generations: Total number of generations
|
|
259
|
+
use_rich: Whether to use rich progress (if available)
|
|
260
|
+
**kwargs: Additional arguments
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Progress tracker instance
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
>>> tracker = create_progress_tracker(100)
|
|
267
|
+
>>> tracker.start()
|
|
268
|
+
>>> for i in range(100):
|
|
269
|
+
... tracker.update(i, best_fitness=0.9)
|
|
270
|
+
>>> tracker.finish()
|
|
271
|
+
"""
|
|
272
|
+
if use_rich and RICH_AVAILABLE:
|
|
273
|
+
return OptimizationProgress(total_generations, **kwargs)
|
|
274
|
+
else:
|
|
275
|
+
return SimpleProgressBar(total_generations, desc="Optimization", **kwargs)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class LiveDashboard:
|
|
279
|
+
"""
|
|
280
|
+
Live dashboard for real-time optimization monitoring.
|
|
281
|
+
|
|
282
|
+
Shows multiple metrics in a live-updating display.
|
|
283
|
+
|
|
284
|
+
Example:
|
|
285
|
+
>>> dashboard = LiveDashboard()
|
|
286
|
+
>>> dashboard.start()
|
|
287
|
+
>>> for gen in range(100):
|
|
288
|
+
... dashboard.update({
|
|
289
|
+
... 'generation': gen,
|
|
290
|
+
... 'best_fitness': 0.95,
|
|
291
|
+
... 'diversity': 0.7,
|
|
292
|
+
... 'crossover_rate': 0.8
|
|
293
|
+
... })
|
|
294
|
+
>>> dashboard.stop()
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
def __init__(self):
|
|
298
|
+
"""Initialize live dashboard."""
|
|
299
|
+
if not RICH_AVAILABLE:
|
|
300
|
+
logger.warning("Live dashboard requires rich. Install with: pip install rich")
|
|
301
|
+
self.enabled = False
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
self.enabled = True
|
|
305
|
+
self.console = Console()
|
|
306
|
+
self.live = None
|
|
307
|
+
self.layout = Layout()
|
|
308
|
+
self.stats = {}
|
|
309
|
+
|
|
310
|
+
def start(self):
|
|
311
|
+
"""Start live dashboard."""
|
|
312
|
+
if not self.enabled:
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
self.layout.split_column(
|
|
316
|
+
Layout(name="header", size=3),
|
|
317
|
+
Layout(name="body"),
|
|
318
|
+
Layout(name="footer", size=3),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
self.live = Live(self.layout, console=self.console, refresh_per_second=4)
|
|
322
|
+
self.live.start()
|
|
323
|
+
|
|
324
|
+
def update(self, stats: Dict[str, Any]):
|
|
325
|
+
"""
|
|
326
|
+
Update dashboard with new statistics.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
stats: Dictionary of statistics to display
|
|
330
|
+
"""
|
|
331
|
+
if not self.enabled or not self.live:
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
self.stats.update(stats)
|
|
335
|
+
|
|
336
|
+
# Update header
|
|
337
|
+
self.layout["header"].update(
|
|
338
|
+
Panel("[bold cyan]MorphML Optimization Dashboard[/bold cyan]", style="cyan")
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Update body with stats table
|
|
342
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
343
|
+
table.add_column("Metric", style="cyan")
|
|
344
|
+
table.add_column("Value", style="green")
|
|
345
|
+
|
|
346
|
+
for key, value in self.stats.items():
|
|
347
|
+
if isinstance(value, float):
|
|
348
|
+
table.add_row(key.replace("_", " ").title(), f"{value:.4f}")
|
|
349
|
+
else:
|
|
350
|
+
table.add_row(key.replace("_", " ").title(), str(value))
|
|
351
|
+
|
|
352
|
+
self.layout["body"].update(table)
|
|
353
|
+
|
|
354
|
+
# Update footer
|
|
355
|
+
self.layout["footer"].update(Panel("[dim]Press Ctrl+C to stop[/dim]", style="dim"))
|
|
356
|
+
|
|
357
|
+
def stop(self):
|
|
358
|
+
"""Stop live dashboard."""
|
|
359
|
+
if self.enabled and self.live:
|
|
360
|
+
self.live.stop()
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# Convenience function
|
|
364
|
+
def with_progress(func):
|
|
365
|
+
"""
|
|
366
|
+
Decorator to add progress tracking to optimization functions.
|
|
367
|
+
|
|
368
|
+
Example:
|
|
369
|
+
>>> @with_progress
|
|
370
|
+
... def my_optimization(num_generations=100):
|
|
371
|
+
... for gen in range(num_generations):
|
|
372
|
+
... # optimization logic
|
|
373
|
+
... yield gen, {"best_fitness": 0.9}
|
|
374
|
+
"""
|
|
375
|
+
|
|
376
|
+
def wrapper(*args, **kwargs):
|
|
377
|
+
total = kwargs.get("num_generations", 100)
|
|
378
|
+
progress = create_progress_tracker(total)
|
|
379
|
+
progress.start()
|
|
380
|
+
|
|
381
|
+
try:
|
|
382
|
+
result = func(*args, **kwargs)
|
|
383
|
+
if hasattr(result, "__iter__"):
|
|
384
|
+
for gen, stats in result:
|
|
385
|
+
progress.update(gen, **stats)
|
|
386
|
+
return result
|
|
387
|
+
else:
|
|
388
|
+
return result
|
|
389
|
+
finally:
|
|
390
|
+
progress.finish()
|
|
391
|
+
|
|
392
|
+
return wrapper
|