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,399 @@
|
|
|
1
|
+
"""Adaptive genetic operators that adjust based on population diversity.
|
|
2
|
+
|
|
3
|
+
This module provides adaptive versions of genetic operators that automatically
|
|
4
|
+
adjust their rates based on population diversity and search progress.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from morphml.optimizers.adaptive_operators import AdaptiveCrossoverManager
|
|
8
|
+
>>>
|
|
9
|
+
>>> manager = AdaptiveCrossoverManager(
|
|
10
|
+
... initial_rate=0.8,
|
|
11
|
+
... min_rate=0.5,
|
|
12
|
+
... max_rate=0.95
|
|
13
|
+
... )
|
|
14
|
+
>>>
|
|
15
|
+
>>> # During optimization
|
|
16
|
+
>>> crossover_rate = manager.get_rate(population, generation)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from typing import List
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
from morphml.core.search.population import Population
|
|
24
|
+
from morphml.logging_config import get_logger
|
|
25
|
+
|
|
26
|
+
logger = get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AdaptiveCrossoverManager:
|
|
30
|
+
"""
|
|
31
|
+
Manages adaptive crossover rate based on population diversity.
|
|
32
|
+
|
|
33
|
+
Increases crossover rate when diversity is low (exploitation)
|
|
34
|
+
Decreases crossover rate when diversity is high (exploration)
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
initial_rate: Starting crossover rate
|
|
38
|
+
min_rate: Minimum allowed rate
|
|
39
|
+
max_rate: Maximum allowed rate
|
|
40
|
+
adaptation_speed: How quickly to adapt (0.0-1.0)
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> manager = AdaptiveCrossoverManager(initial_rate=0.8)
|
|
44
|
+
>>> rate = manager.get_rate(population, generation=10)
|
|
45
|
+
>>> print(f"Adaptive rate: {rate:.3f}")
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
initial_rate: float = 0.8,
|
|
51
|
+
min_rate: float = 0.5,
|
|
52
|
+
max_rate: float = 0.95,
|
|
53
|
+
adaptation_speed: float = 0.1,
|
|
54
|
+
diversity_window: int = 10,
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Initialize adaptive crossover manager.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
initial_rate: Starting crossover rate (0.0-1.0)
|
|
61
|
+
min_rate: Minimum crossover rate
|
|
62
|
+
max_rate: Maximum crossover rate
|
|
63
|
+
adaptation_speed: Speed of adaptation (0.0-1.0)
|
|
64
|
+
diversity_window: Number of generations to track diversity
|
|
65
|
+
"""
|
|
66
|
+
self.initial_rate = initial_rate
|
|
67
|
+
self.min_rate = min_rate
|
|
68
|
+
self.max_rate = max_rate
|
|
69
|
+
self.adaptation_speed = adaptation_speed
|
|
70
|
+
self.diversity_window = diversity_window
|
|
71
|
+
|
|
72
|
+
self.current_rate = initial_rate
|
|
73
|
+
self.diversity_history: List[float] = []
|
|
74
|
+
|
|
75
|
+
logger.info(
|
|
76
|
+
f"Initialized AdaptiveCrossoverManager: "
|
|
77
|
+
f"rate={initial_rate:.2f}, range=[{min_rate:.2f}, {max_rate:.2f}]"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def get_rate(
|
|
81
|
+
self,
|
|
82
|
+
population: Population,
|
|
83
|
+
generation: int,
|
|
84
|
+
force_update: bool = True,
|
|
85
|
+
) -> float:
|
|
86
|
+
"""
|
|
87
|
+
Get adaptive crossover rate for current generation.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
population: Current population
|
|
91
|
+
generation: Current generation number
|
|
92
|
+
force_update: Whether to force rate update
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Adaptive crossover rate
|
|
96
|
+
"""
|
|
97
|
+
if force_update:
|
|
98
|
+
diversity = self._calculate_diversity(population)
|
|
99
|
+
self._update_rate(diversity, generation)
|
|
100
|
+
|
|
101
|
+
return self.current_rate
|
|
102
|
+
|
|
103
|
+
def _calculate_diversity(self, population: Population) -> float:
|
|
104
|
+
"""
|
|
105
|
+
Calculate population diversity.
|
|
106
|
+
|
|
107
|
+
Uses multiple metrics:
|
|
108
|
+
- Fitness variance
|
|
109
|
+
- Graph structure diversity
|
|
110
|
+
- Parameter diversity
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
population: Population to analyze
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Diversity score (0.0-1.0)
|
|
117
|
+
"""
|
|
118
|
+
if len(population) < 2:
|
|
119
|
+
return 0.0
|
|
120
|
+
|
|
121
|
+
individuals = list(population.individuals.values())
|
|
122
|
+
|
|
123
|
+
# Fitness diversity (normalized variance)
|
|
124
|
+
fitnesses = [ind.fitness for ind in individuals if ind.fitness is not None]
|
|
125
|
+
if len(fitnesses) < 2:
|
|
126
|
+
fitness_diversity = 0.0
|
|
127
|
+
else:
|
|
128
|
+
fitness_std = np.std(fitnesses)
|
|
129
|
+
fitness_mean = np.mean(fitnesses)
|
|
130
|
+
fitness_diversity = fitness_std / (abs(fitness_mean) + 1e-8)
|
|
131
|
+
|
|
132
|
+
# Structure diversity (unique node counts)
|
|
133
|
+
node_counts = [len(ind.graph.nodes) for ind in individuals]
|
|
134
|
+
unique_counts = len(set(node_counts))
|
|
135
|
+
structure_diversity = unique_counts / len(individuals)
|
|
136
|
+
|
|
137
|
+
# Edge diversity
|
|
138
|
+
edge_counts = [len(ind.graph.edges) for ind in individuals]
|
|
139
|
+
unique_edges = len(set(edge_counts))
|
|
140
|
+
edge_diversity = unique_edges / len(individuals)
|
|
141
|
+
|
|
142
|
+
# Combined diversity (weighted average)
|
|
143
|
+
diversity = 0.5 * fitness_diversity + 0.3 * structure_diversity + 0.2 * edge_diversity
|
|
144
|
+
|
|
145
|
+
# Normalize to [0, 1]
|
|
146
|
+
diversity = np.clip(diversity, 0.0, 1.0)
|
|
147
|
+
|
|
148
|
+
# Track history
|
|
149
|
+
self.diversity_history.append(diversity)
|
|
150
|
+
if len(self.diversity_history) > self.diversity_window:
|
|
151
|
+
self.diversity_history.pop(0)
|
|
152
|
+
|
|
153
|
+
return diversity
|
|
154
|
+
|
|
155
|
+
def _update_rate(self, diversity: float, generation: int) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Update crossover rate based on diversity.
|
|
158
|
+
|
|
159
|
+
Low diversity -> Increase crossover (more exploration)
|
|
160
|
+
High diversity -> Decrease crossover (more exploitation)
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
diversity: Current diversity score
|
|
164
|
+
generation: Current generation
|
|
165
|
+
"""
|
|
166
|
+
# Calculate target rate based on diversity
|
|
167
|
+
# Inverse relationship: low diversity -> high crossover
|
|
168
|
+
target_rate = self.max_rate - (diversity * (self.max_rate - self.min_rate))
|
|
169
|
+
|
|
170
|
+
# Smooth adaptation
|
|
171
|
+
delta = target_rate - self.current_rate
|
|
172
|
+
self.current_rate += self.adaptation_speed * delta
|
|
173
|
+
|
|
174
|
+
# Clamp to bounds
|
|
175
|
+
self.current_rate = np.clip(self.current_rate, self.min_rate, self.max_rate)
|
|
176
|
+
|
|
177
|
+
logger.debug(
|
|
178
|
+
f"Gen {generation}: diversity={diversity:.3f}, "
|
|
179
|
+
f"crossover_rate={self.current_rate:.3f}"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def get_diversity_trend(self) -> str:
|
|
183
|
+
"""
|
|
184
|
+
Get diversity trend description.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Trend description ("increasing", "decreasing", "stable")
|
|
188
|
+
"""
|
|
189
|
+
if len(self.diversity_history) < 3:
|
|
190
|
+
return "insufficient_data"
|
|
191
|
+
|
|
192
|
+
recent = self.diversity_history[-3:]
|
|
193
|
+
if recent[-1] > recent[0] + 0.1:
|
|
194
|
+
return "increasing"
|
|
195
|
+
elif recent[-1] < recent[0] - 0.1:
|
|
196
|
+
return "decreasing"
|
|
197
|
+
else:
|
|
198
|
+
return "stable"
|
|
199
|
+
|
|
200
|
+
def reset(self) -> None:
|
|
201
|
+
"""Reset to initial state."""
|
|
202
|
+
self.current_rate = self.initial_rate
|
|
203
|
+
self.diversity_history.clear()
|
|
204
|
+
logger.info("Reset adaptive crossover manager")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class AdaptiveMutationManager:
|
|
208
|
+
"""
|
|
209
|
+
Manages adaptive mutation rate based on search progress.
|
|
210
|
+
|
|
211
|
+
Increases mutation when stuck in local optima
|
|
212
|
+
Decreases mutation when making good progress
|
|
213
|
+
|
|
214
|
+
Example:
|
|
215
|
+
>>> manager = AdaptiveMutationManager(initial_rate=0.2)
|
|
216
|
+
>>> rate = manager.get_rate(best_fitness_history, generation)
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
def __init__(
|
|
220
|
+
self,
|
|
221
|
+
initial_rate: float = 0.2,
|
|
222
|
+
min_rate: float = 0.05,
|
|
223
|
+
max_rate: float = 0.5,
|
|
224
|
+
stagnation_threshold: int = 5,
|
|
225
|
+
):
|
|
226
|
+
"""
|
|
227
|
+
Initialize adaptive mutation manager.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
initial_rate: Starting mutation rate
|
|
231
|
+
min_rate: Minimum mutation rate
|
|
232
|
+
max_rate: Maximum mutation rate
|
|
233
|
+
stagnation_threshold: Generations without improvement to trigger increase
|
|
234
|
+
"""
|
|
235
|
+
self.initial_rate = initial_rate
|
|
236
|
+
self.min_rate = min_rate
|
|
237
|
+
self.max_rate = max_rate
|
|
238
|
+
self.stagnation_threshold = stagnation_threshold
|
|
239
|
+
|
|
240
|
+
self.current_rate = initial_rate
|
|
241
|
+
self.best_fitness_history: List[float] = []
|
|
242
|
+
self.stagnation_counter = 0
|
|
243
|
+
|
|
244
|
+
logger.info(
|
|
245
|
+
f"Initialized AdaptiveMutationManager: "
|
|
246
|
+
f"rate={initial_rate:.2f}, range=[{min_rate:.2f}, {max_rate:.2f}]"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def get_rate(
|
|
250
|
+
self,
|
|
251
|
+
current_best_fitness: float,
|
|
252
|
+
generation: int,
|
|
253
|
+
) -> float:
|
|
254
|
+
"""
|
|
255
|
+
Get adaptive mutation rate.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
current_best_fitness: Best fitness in current generation
|
|
259
|
+
generation: Current generation number
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Adaptive mutation rate
|
|
263
|
+
"""
|
|
264
|
+
self._update_rate(current_best_fitness, generation)
|
|
265
|
+
return self.current_rate
|
|
266
|
+
|
|
267
|
+
def _update_rate(self, best_fitness: float, generation: int) -> None:
|
|
268
|
+
"""
|
|
269
|
+
Update mutation rate based on progress.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
best_fitness: Current best fitness
|
|
273
|
+
generation: Current generation
|
|
274
|
+
"""
|
|
275
|
+
# Track best fitness
|
|
276
|
+
if not self.best_fitness_history:
|
|
277
|
+
self.best_fitness_history.append(best_fitness)
|
|
278
|
+
return
|
|
279
|
+
|
|
280
|
+
# Check for improvement
|
|
281
|
+
previous_best = max(self.best_fitness_history)
|
|
282
|
+
improvement = best_fitness - previous_best
|
|
283
|
+
|
|
284
|
+
if improvement > 1e-6: # Improvement threshold
|
|
285
|
+
# Making progress - decrease mutation
|
|
286
|
+
self.stagnation_counter = 0
|
|
287
|
+
self.current_rate *= 0.95 # Gradual decrease
|
|
288
|
+
else:
|
|
289
|
+
# Stagnation - increase mutation
|
|
290
|
+
self.stagnation_counter += 1
|
|
291
|
+
if self.stagnation_counter >= self.stagnation_threshold:
|
|
292
|
+
self.current_rate *= 1.1 # Increase to escape local optimum
|
|
293
|
+
logger.info(
|
|
294
|
+
f"Gen {generation}: Stagnation detected, "
|
|
295
|
+
f"increasing mutation to {self.current_rate:.3f}"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Clamp to bounds
|
|
299
|
+
self.current_rate = np.clip(self.current_rate, self.min_rate, self.max_rate)
|
|
300
|
+
|
|
301
|
+
# Update history
|
|
302
|
+
self.best_fitness_history.append(best_fitness)
|
|
303
|
+
|
|
304
|
+
logger.debug(
|
|
305
|
+
f"Gen {generation}: best={best_fitness:.4f}, " f"mutation_rate={self.current_rate:.3f}"
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
def reset(self) -> None:
|
|
309
|
+
"""Reset to initial state."""
|
|
310
|
+
self.current_rate = self.initial_rate
|
|
311
|
+
self.best_fitness_history.clear()
|
|
312
|
+
self.stagnation_counter = 0
|
|
313
|
+
logger.info("Reset adaptive mutation manager")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class AdaptiveOperatorScheduler:
|
|
317
|
+
"""
|
|
318
|
+
Coordinates multiple adaptive operators.
|
|
319
|
+
|
|
320
|
+
Manages crossover and mutation rates together, ensuring they
|
|
321
|
+
complement each other for effective search.
|
|
322
|
+
|
|
323
|
+
Example:
|
|
324
|
+
>>> scheduler = AdaptiveOperatorScheduler()
|
|
325
|
+
>>> rates = scheduler.get_rates(population, best_fitness, generation)
|
|
326
|
+
>>> crossover_rate, mutation_rate = rates
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
def __init__(
|
|
330
|
+
self,
|
|
331
|
+
initial_crossover: float = 0.8,
|
|
332
|
+
initial_mutation: float = 0.2,
|
|
333
|
+
balance_operators: bool = True,
|
|
334
|
+
):
|
|
335
|
+
"""
|
|
336
|
+
Initialize adaptive operator scheduler.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
initial_crossover: Initial crossover rate
|
|
340
|
+
initial_mutation: Initial mutation rate
|
|
341
|
+
balance_operators: Whether to balance crossover and mutation
|
|
342
|
+
"""
|
|
343
|
+
self.crossover_manager = AdaptiveCrossoverManager(initial_rate=initial_crossover)
|
|
344
|
+
self.mutation_manager = AdaptiveMutationManager(initial_rate=initial_mutation)
|
|
345
|
+
self.balance_operators = balance_operators
|
|
346
|
+
|
|
347
|
+
logger.info("Initialized AdaptiveOperatorScheduler")
|
|
348
|
+
|
|
349
|
+
def get_rates(
|
|
350
|
+
self,
|
|
351
|
+
population: Population,
|
|
352
|
+
current_best_fitness: float,
|
|
353
|
+
generation: int,
|
|
354
|
+
) -> tuple:
|
|
355
|
+
"""
|
|
356
|
+
Get adaptive rates for both operators.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
population: Current population
|
|
360
|
+
current_best_fitness: Best fitness in current generation
|
|
361
|
+
generation: Current generation number
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Tuple of (crossover_rate, mutation_rate)
|
|
365
|
+
"""
|
|
366
|
+
crossover_rate = self.crossover_manager.get_rate(population, generation)
|
|
367
|
+
mutation_rate = self.mutation_manager.get_rate(current_best_fitness, generation)
|
|
368
|
+
|
|
369
|
+
# Balance operators if enabled
|
|
370
|
+
if self.balance_operators:
|
|
371
|
+
# Ensure they sum to reasonable value
|
|
372
|
+
total = crossover_rate + mutation_rate
|
|
373
|
+
if total > 1.0:
|
|
374
|
+
scale = 1.0 / total
|
|
375
|
+
crossover_rate *= scale
|
|
376
|
+
mutation_rate *= scale
|
|
377
|
+
|
|
378
|
+
return crossover_rate, mutation_rate
|
|
379
|
+
|
|
380
|
+
def get_statistics(self) -> dict:
|
|
381
|
+
"""
|
|
382
|
+
Get statistics about adaptive operators.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
Dictionary with operator statistics
|
|
386
|
+
"""
|
|
387
|
+
return {
|
|
388
|
+
"crossover_rate": self.crossover_manager.current_rate,
|
|
389
|
+
"mutation_rate": self.mutation_manager.current_rate,
|
|
390
|
+
"diversity_trend": self.crossover_manager.get_diversity_trend(),
|
|
391
|
+
"stagnation_counter": self.mutation_manager.stagnation_counter,
|
|
392
|
+
"diversity_history": self.crossover_manager.diversity_history.copy(),
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
def reset(self) -> None:
|
|
396
|
+
"""Reset all managers."""
|
|
397
|
+
self.crossover_manager.reset()
|
|
398
|
+
self.mutation_manager.reset()
|
|
399
|
+
logger.info("Reset adaptive operator scheduler")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Bayesian Optimization algorithms for neural architecture search.
|
|
2
|
+
|
|
3
|
+
This module implements sample-efficient Bayesian optimization methods including:
|
|
4
|
+
- Gaussian Process (GP) optimization with various acquisition functions
|
|
5
|
+
- Tree-structured Parzen Estimator (TPE)
|
|
6
|
+
- Sequential Model-based Algorithm Configuration (SMAC)
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from morphml.optimizers.bayesian import GaussianProcessOptimizer
|
|
10
|
+
>>> optimizer = GaussianProcessOptimizer(
|
|
11
|
+
... search_space=space,
|
|
12
|
+
... acquisition='ei',
|
|
13
|
+
... n_initial_points=10
|
|
14
|
+
... )
|
|
15
|
+
>>> best = optimizer.optimize(evaluator)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from morphml.optimizers.bayesian.acquisition import (
|
|
19
|
+
AcquisitionOptimizer,
|
|
20
|
+
expected_improvement,
|
|
21
|
+
get_acquisition_function,
|
|
22
|
+
probability_of_improvement,
|
|
23
|
+
thompson_sampling,
|
|
24
|
+
upper_confidence_bound,
|
|
25
|
+
)
|
|
26
|
+
from morphml.optimizers.bayesian.base import BaseBayesianOptimizer
|
|
27
|
+
from morphml.optimizers.bayesian.gaussian_process import (
|
|
28
|
+
GaussianProcessOptimizer,
|
|
29
|
+
optimize_with_gp,
|
|
30
|
+
)
|
|
31
|
+
from morphml.optimizers.bayesian.smac import SMACOptimizer, optimize_with_smac
|
|
32
|
+
from morphml.optimizers.bayesian.tpe import TPEOptimizer, optimize_with_tpe
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
# Base class
|
|
36
|
+
"BaseBayesianOptimizer",
|
|
37
|
+
# Optimizers
|
|
38
|
+
"GaussianProcessOptimizer",
|
|
39
|
+
"TPEOptimizer",
|
|
40
|
+
"SMACOptimizer",
|
|
41
|
+
# Convenience functions
|
|
42
|
+
"optimize_with_gp",
|
|
43
|
+
"optimize_with_tpe",
|
|
44
|
+
"optimize_with_smac",
|
|
45
|
+
# Acquisition functions
|
|
46
|
+
"expected_improvement",
|
|
47
|
+
"upper_confidence_bound",
|
|
48
|
+
"probability_of_improvement",
|
|
49
|
+
"thompson_sampling",
|
|
50
|
+
"get_acquisition_function",
|
|
51
|
+
"AcquisitionOptimizer",
|
|
52
|
+
]
|