fable-engine 1.3.1__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.
Files changed (104) hide show
  1. fable_compressor.py +356 -0
  2. fable_engine/__init__.py +1 -0
  3. fable_engine/actions/__init__.py +291 -0
  4. fable_engine/actions/cas.py +182 -0
  5. fable_engine/actions/deliberation.py +523 -0
  6. fable_engine/actions/fleet.py +807 -0
  7. fable_engine/actions/lifecycle.py +298 -0
  8. fable_engine/actions/scrapers.py +116 -0
  9. fable_engine/actions/system3.py +815 -0
  10. fable_engine/browser.py +824 -0
  11. fable_engine/cas.py +974 -0
  12. fable_engine/fable_session.json +510 -0
  13. fable_engine/guards.py +283 -0
  14. fable_engine/schema.py +714 -0
  15. fable_engine/scrapers/__init__.py +32 -0
  16. fable_engine/scrapers/arxiv.py +115 -0
  17. fable_engine/scrapers/base.py +386 -0
  18. fable_engine/scrapers/github.py +129 -0
  19. fable_engine/scrapers/reddit.py +154 -0
  20. fable_engine/scrapers/web.py +120 -0
  21. fable_engine/scrapers/x.py +125 -0
  22. fable_engine/scrapers/youtube.py +132 -0
  23. fable_engine/server.py +414 -0
  24. fable_engine/session.py +1819 -0
  25. fable_engine/test_server.py +1362 -0
  26. fable_engine/updater.py +541 -0
  27. fable_engine-1.3.1.dist-info/LICENSE +22 -0
  28. fable_engine-1.3.1.dist-info/METADATA +173 -0
  29. fable_engine-1.3.1.dist-info/RECORD +104 -0
  30. fable_engine-1.3.1.dist-info/WHEEL +5 -0
  31. fable_engine-1.3.1.dist-info/entry_points.txt +5 -0
  32. fable_engine-1.3.1.dist-info/top_level.txt +6 -0
  33. fable_mode/__init__.py +3 -0
  34. fable_mode/__main__.py +4 -0
  35. fable_mode/adapters.py +1014 -0
  36. fable_mode/installer.py +553 -0
  37. fable_mode/launcher.py +437 -0
  38. fable_mode/manifest.py +142 -0
  39. fable_mode/resources.json +114 -0
  40. fable_mode/safety.py +103 -0
  41. fable_mode_entry.py +10 -0
  42. fable_v2/__init__.py +146 -0
  43. fable_v2/adapters.py +151 -0
  44. fable_v2/coder_fleet/__init__.py +100 -0
  45. fable_v2/coder_fleet/ast_tools.py +158 -0
  46. fable_v2/coder_fleet/compute.py +199 -0
  47. fable_v2/coder_fleet/design_engine.py +1316 -0
  48. fable_v2/coder_fleet/diagnostics.py +293 -0
  49. fable_v2/coder_fleet/fleet_dispatcher.py +214 -0
  50. fable_v2/coder_fleet/mock_auditor.py +306 -0
  51. fable_v2/coder_fleet/mutation.py +216 -0
  52. fable_v2/coder_fleet/property_oracle.py +260 -0
  53. fable_v2/coder_fleet/receipt_attestor.py +122 -0
  54. fable_v2/coder_fleet/red_team_swarm.py +908 -0
  55. fable_v2/coder_fleet/test_harness.py +198 -0
  56. fable_v2/coder_fleet/vector_engine.py +1287 -0
  57. fable_v2/coder_fleet/visual.py +357 -0
  58. fable_v2/coder_fleet/workspace.py +153 -0
  59. fable_v2/cortical/__init__.py +20 -0
  60. fable_v2/cortical/plasticity_engine.py +992 -0
  61. fable_v2/execution_broker.py +811 -0
  62. fable_v2/proof_engine.py +1141 -0
  63. fable_v2/protocol.py +485 -0
  64. fable_v2/runtime.py +1010 -0
  65. fable_v2/system3/__init__.py +204 -0
  66. fable_v2/system3/causal.py +558 -0
  67. fable_v2/system3/dialectical.py +577 -0
  68. fable_v2/system3/evolution.py +503 -0
  69. fable_v2/system3/executive.py +338 -0
  70. fable_v2/system3/free_energy.py +479 -0
  71. fable_v2/system3/hyperbolic.py +555 -0
  72. fable_v2/system3/induction.py +336 -0
  73. fable_v2/system3/kripke.py +548 -0
  74. fable_v2/system3/oracle.py +745 -0
  75. fable_v2/verifiers.py +72 -0
  76. tests/__init__.py +1 -0
  77. tests/test_anti_loop_circuit_breaker.py +64 -0
  78. tests/test_auto_updater.py +407 -0
  79. tests/test_coder_fleet.py +535 -0
  80. tests/test_delegation_compiler.py +54 -0
  81. tests/test_descriptor_boundaries.py +126 -0
  82. tests/test_design_engine.py +603 -0
  83. tests/test_epistemic_evidence_validator.py +66 -0
  84. tests/test_execution_broker.py +233 -0
  85. tests/test_fable_v2.py +406 -0
  86. tests/test_fleet_transitions.py +116 -0
  87. tests/test_fsm_redteam_evolution.py +406 -0
  88. tests/test_goal_rubric_and_pipeline.py +367 -0
  89. tests/test_hebbian_plasticity.py +585 -0
  90. tests/test_packaging_runtime.py +194 -0
  91. tests/test_proof_engine.py +259 -0
  92. tests/test_red_team_swarm.py +645 -0
  93. tests/test_redteam_remediation.py +169 -0
  94. tests/test_registration_transaction.py +375 -0
  95. tests/test_requested_regressions.py +467 -0
  96. tests/test_scrapers.py +370 -0
  97. tests/test_server_actions.py +93 -0
  98. tests/test_server_frontier_actions.py +269 -0
  99. tests/test_server_protocol.py +88 -0
  100. tests/test_stealth_browser.py +970 -0
  101. tests/test_system3.py +381 -0
  102. tests/test_system3_deep_integration.py +385 -0
  103. tests/test_system3_frontier.py +436 -0
  104. tests/test_vector_engine.py +608 -0
@@ -0,0 +1,503 @@
1
+ """System 3 Evolutionary Paradigm Engine & 10D Pareto Frontier Optimizer.
2
+
3
+ Implements genetic algorithms, multi-objective NSGA-II non-dominated Pareto sorting,
4
+ crowding-distance diversity maintenance, and architectural genome mutation/crossover.
5
+ Zero external dependencies.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field, asdict
11
+ import copy
12
+ import hashlib
13
+ import json
14
+ import math
15
+ import random
16
+ from typing import Any, Callable, Dict, List, Optional, Set, Tuple
17
+
18
+
19
+ PARETO_DIMENSIONS = [
20
+ "latency", # Speed / execution responsiveness (higher is better)
21
+ "throughput", # Concurrency / operations per second
22
+ "memory_efficiency", # Low footprint / bounded cache overhead
23
+ "fault_tolerance", # Resilience under faults / self-healing
24
+ "modularity", # Decoupling / clear component boundaries
25
+ "simplicity", # Low cognitive complexity / maintainability
26
+ "testability", # Ease of automated verification / determinism
27
+ "security", # Trust boundaries / isolation / attack surface
28
+ "determinism", # Reproducibility / zero race conditions
29
+ "token_compaction", # Compaction efficiency (CAS storage / grammar)
30
+ ]
31
+
32
+ GENE_ALLELE_OPTIONS = {
33
+ "concurrency_model": [
34
+ "actor_model", "event_loop_async", "worker_thread_pool",
35
+ "lock_free_ring_buffer", "csp_channels", "speculative_dual_pass"
36
+ ],
37
+ "state_persistence": [
38
+ "cas_immutable_store", "event_sourcing_log", "write_ahead_log",
39
+ "in_memory_sharded_lru", "cow_snapshot_tree", "hybrid_disk_mmap"
40
+ ],
41
+ "synchronization": [
42
+ "optimistic_concurrency_cas", "single_writer_multi_reader",
43
+ "crdt_conflict_free", "pessimistic_rwlock", "message_passing_only"
44
+ ],
45
+ "cache_hierarchy": [
46
+ "two_level_sharded_lru", "direct_mapped_cas",
47
+ "arc_adaptive_replacement", "write_through_memory_pool"
48
+ ],
49
+ "error_recovery": [
50
+ "ooda_circuit_breaker", "erlang_supervisor_tree",
51
+ "exponential_backoff_jitter", "fail_fast_rollback", "shadow_consensus"
52
+ ],
53
+ "data_layout": [
54
+ "columnar_structure_of_arrays", "row_array_of_structures",
55
+ "flat_buffer_varint", "chunked_composite_frames"
56
+ ],
57
+ "execution_strategy": [
58
+ "subagent_fleet_pipeline", "hierarchical_director_worker",
59
+ "deterministic_single_pass", "mcts_branching_deliberation"
60
+ ],
61
+ "network_topology": [
62
+ "hub_and_spoke_orchestrator", "peer_to_peer_mesh",
63
+ "ring_token_bus", "hierarchical_tree"
64
+ ],
65
+ }
66
+
67
+
68
+ @dataclass
69
+ class CognitiveGenome:
70
+ """Genetic encoding of an architectural candidate paradigm."""
71
+ genome_id: str
72
+ paradigm_name: str
73
+ genes: Dict[str, Any] = field(default_factory=dict)
74
+ fitness_scores: Dict[str, float] = field(default_factory=dict)
75
+ generation: int = 0
76
+ pareto_rank: int = 1
77
+ crowding_distance: float = 0.0
78
+ lineage: List[str] = field(default_factory=list)
79
+ metadata: Dict[str, Any] = field(default_factory=dict)
80
+
81
+ def dominates(self, other: "CognitiveGenome") -> bool:
82
+ """
83
+ Pareto Dominance Check:
84
+ Returns True if self is >= other in all 10 dimensions AND strictly > in at least one.
85
+ """
86
+ better_in_at_least_one = False
87
+ for dim in PARETO_DIMENSIONS:
88
+ score_self = self.fitness_scores.get(dim, 0.0)
89
+ score_other = other.fitness_scores.get(dim, 0.0)
90
+ if score_self < score_other:
91
+ return False
92
+ if score_self > score_other:
93
+ better_in_at_least_one = True
94
+ return better_in_at_least_one
95
+
96
+ def compute_scalar_fitness(self, weights: Optional[Dict[str, float]] = None) -> float:
97
+ """Compute weighted scalar composite fitness score [0.0, 1.0]."""
98
+ w = weights or {dim: 1.0 / len(PARETO_DIMENSIONS) for dim in PARETO_DIMENSIONS}
99
+ total_w = sum(w.values()) or 1.0
100
+ score = sum(self.fitness_scores.get(dim, 0.0) * w.get(dim, 1.0) for dim in PARETO_DIMENSIONS)
101
+ return round(score / total_w, 4)
102
+
103
+ def to_dict(self) -> Dict[str, Any]:
104
+ return asdict(self)
105
+
106
+ @classmethod
107
+ def from_dict(cls, data: Dict[str, Any]) -> "CognitiveGenome":
108
+ return cls(**data)
109
+
110
+
111
+ def create_random_genome(
112
+ genome_id: str,
113
+ paradigm_name: str,
114
+ generation: int = 0,
115
+ seed: Optional[int] = None,
116
+ ) -> CognitiveGenome:
117
+ """Instantiate a valid randomized genome across the architectural dimensions."""
118
+ rng = random.Random(seed)
119
+ genes: Dict[str, Any] = {}
120
+ for gene_name, alleles in GENE_ALLELE_OPTIONS.items():
121
+ genes[gene_name] = rng.choice(alleles)
122
+
123
+ # Continuous parameters
124
+ genes["verification_depth"] = round(rng.uniform(0.5, 1.0), 3)
125
+ genes["compression_target"] = round(rng.uniform(0.001, 0.005), 5)
126
+ genes["concurrency_workers"] = rng.randint(2, 16)
127
+
128
+ # Default heuristic fitness initialization
129
+ fitness: Dict[str, float] = {}
130
+ for dim in PARETO_DIMENSIONS:
131
+ fitness[dim] = round(rng.uniform(0.5, 0.85), 3)
132
+
133
+ return CognitiveGenome(
134
+ genome_id=genome_id,
135
+ paradigm_name=paradigm_name,
136
+ genes=genes,
137
+ fitness_scores=fitness,
138
+ generation=generation,
139
+ )
140
+
141
+
142
+ class CognitiveGenePool:
143
+ """
144
+ Population manager executing NSGA-II Multi-Objective Evolutionary Optimization
145
+ across the 10D Pareto frontier with elite preservation and crowding distance diversity.
146
+ """
147
+
148
+ def __init__(
149
+ self,
150
+ population_size: int = 20,
151
+ mutation_rate: float = 0.15,
152
+ crossover_rate: float = 0.80,
153
+ random_seed: Optional[int] = 42,
154
+ ):
155
+ self.population_size = max(4, population_size)
156
+ self.mutation_rate = max(0.01, min(1.0, mutation_rate))
157
+ self.crossover_rate = max(0.1, min(1.0, crossover_rate))
158
+ self.rng = random.Random(random_seed)
159
+ self.population: List[CognitiveGenome] = []
160
+ self.generation_count: int = 0
161
+ self.history: List[Dict[str, Any]] = []
162
+
163
+ def initialize_population(
164
+ self,
165
+ seed_paradigms: Optional[List[Dict[str, Any]]] = None,
166
+ ) -> None:
167
+ """Seed initial population with provided archetypes and generated variants."""
168
+ self.population = []
169
+ self.generation_count = 0
170
+
171
+ # Ingest custom seeds if provided
172
+ if seed_paradigms:
173
+ for idx, p in enumerate(seed_paradigms):
174
+ gid = p.get("genome_id", f"seed_{idx+1:03d}")
175
+ name = p.get("paradigm_name", f"Seed Paradigm #{idx+1}")
176
+ genes = p.get("genes", {})
177
+ # Fill missing genes
178
+ for k, v in GENE_ALLELE_OPTIONS.items():
179
+ if k not in genes:
180
+ genes[k] = self.rng.choice(v)
181
+ if "verification_depth" not in genes:
182
+ genes["verification_depth"] = 0.85
183
+ if "compression_target" not in genes:
184
+ genes["compression_target"] = 0.003
185
+ if "concurrency_workers" not in genes:
186
+ genes["concurrency_workers"] = 4
187
+
188
+ fitness = p.get("fitness_scores", {})
189
+ for dim in PARETO_DIMENSIONS:
190
+ if dim not in fitness:
191
+ fitness[dim] = 0.70
192
+
193
+ genome = CognitiveGenome(
194
+ genome_id=gid,
195
+ paradigm_name=name,
196
+ genes=genes,
197
+ fitness_scores=fitness,
198
+ generation=0,
199
+ )
200
+ self.population.append(genome)
201
+
202
+ # Fill remainder with diversified random archetypes
203
+ archetype_names = [
204
+ "Ultra-Low Latency Ring Engine",
205
+ "Resilient CAS-Immutable Actor Grid",
206
+ "Adaptive Neuro-Symbolic Pipeline",
207
+ "High-Throughput Varint Streamer",
208
+ "Hierarchical Supervisor Worktree",
209
+ "Zero-Allocation Columnar Engine",
210
+ "Speculative Dual-Pass MCTS",
211
+ "Event-Sourced Self-Healing Bus",
212
+ ]
213
+
214
+ while len(self.population) < self.population_size:
215
+ idx = len(self.population) + 1
216
+ name = archetype_names[idx % len(archetype_names)] + f" (Gen 0 #{idx})"
217
+ genome = create_random_genome(f"g0_{idx:03d}", name, generation=0, seed=self.rng.randint(1, 1000000))
218
+ self.population.append(genome)
219
+
220
+ def evaluate_fitness(
221
+ self,
222
+ fitness_fn: Optional[Callable[[CognitiveGenome], Dict[str, float]]] = None,
223
+ ) -> None:
224
+ """
225
+ Evaluate 10D fitness scores for all genomes in the population.
226
+ If no custom evaluator provided, uses domain structural heuristics.
227
+ """
228
+ for genome in self.population:
229
+ if fitness_fn:
230
+ scores = fitness_fn(genome)
231
+ genome.fitness_scores.update(scores)
232
+ else:
233
+ # Built-in structural heuristic evaluator based on gene synergies
234
+ g = genome.genes
235
+ scores: Dict[str, float] = {}
236
+
237
+ # Latency & Throughput heuristics
238
+ is_lock_free = g.get("concurrency_model") == "lock_free_ring_buffer"
239
+ is_cas_imm = g.get("state_persistence") == "cas_immutable_store"
240
+ is_soa = g.get("data_layout") == "columnar_structure_of_arrays"
241
+
242
+ scores["latency"] = min(0.98, 0.65 + (0.20 if is_lock_free else 0.05) + (0.10 if is_soa else 0.0))
243
+ scores["throughput"] = min(0.98, 0.60 + (0.15 if is_lock_free else 0.05) + (0.15 if g.get("concurrency_workers", 4) >= 8 else 0.05))
244
+ scores["memory_efficiency"] = min(0.98, 0.70 + (0.15 if is_cas_imm else 0.0) + (0.10 if is_soa else 0.0))
245
+ scores["fault_tolerance"] = min(0.98, 0.65 + (0.25 if g.get("error_recovery") == "ooda_circuit_breaker" else 0.10))
246
+ scores["modularity"] = min(0.98, 0.75 + (0.15 if g.get("execution_strategy") == "subagent_fleet_pipeline" else 0.05))
247
+ scores["simplicity"] = min(0.95, 0.85 - (0.15 if is_lock_free else 0.0) - (0.10 if g.get("concurrency_workers", 4) > 8 else 0.0))
248
+ scores["testability"] = min(0.98, 0.70 + (0.20 if g.get("synchronization") == "single_writer_multi_reader" else 0.05))
249
+ scores["security"] = min(0.98, 0.75 + (0.15 if is_cas_imm else 0.05))
250
+ scores["determinism"] = min(0.98, 0.80 + (0.15 if is_cas_imm else 0.0))
251
+ scores["token_compaction"] = min(0.99, 0.75 + (0.20 if g.get("data_layout") == "chunked_composite_frames" else 0.05))
252
+
253
+ for dim in PARETO_DIMENSIONS:
254
+ scores[dim] = round(max(0.1, min(0.99, scores.get(dim, 0.7))), 3)
255
+
256
+ genome.fitness_scores = scores
257
+
258
+ def fast_non_dominated_sort(self) -> List[List[CognitiveGenome]]:
259
+ """
260
+ NSGA-II Fast Non-Dominated Sorting Algorithm.
261
+ Sorts the population into Pareto Fronts F_1, F_2, ... F_k.
262
+ Front 1 (rank 1) contains the strictly non-dominated solutions.
263
+ """
264
+ fronts: List[List[CognitiveGenome]] = [[]]
265
+ domination_count: Dict[str, int] = {}
266
+ dominated_solutions: Dict[str, List[CognitiveGenome]] = {}
267
+
268
+ for p in self.population:
269
+ domination_count[p.genome_id] = 0
270
+ dominated_solutions[p.genome_id] = []
271
+ for q in self.population:
272
+ if p.dominates(q):
273
+ dominated_solutions[p.genome_id].append(q)
274
+ elif q.dominates(p):
275
+ domination_count[p.genome_id] += 1
276
+
277
+ if domination_count[p.genome_id] == 0:
278
+ p.pareto_rank = 1
279
+ fronts[0].append(p)
280
+
281
+ i = 0
282
+ while len(fronts[i]) > 0:
283
+ next_front: List[CognitiveGenome] = []
284
+ for p in fronts[i]:
285
+ for q in dominated_solutions[p.genome_id]:
286
+ domination_count[q.genome_id] -= 1
287
+ if domination_count[q.genome_id] == 0:
288
+ q.pareto_rank = i + 2
289
+ next_front.append(q)
290
+ i += 1
291
+ fronts.append(next_front)
292
+
293
+ # Remove trailing empty front
294
+ if fronts and not fronts[-1]:
295
+ fronts.pop()
296
+
297
+ return fronts
298
+
299
+ def calculate_crowding_distance(self, front: List[CognitiveGenome]) -> None:
300
+ """
301
+ Compute crowding distance for genomes in a front to favor diversity along the frontier.
302
+ Boundary points receive infinite distance.
303
+ """
304
+ l = len(front)
305
+ if l == 0:
306
+ return
307
+ if l <= 2:
308
+ for g in front:
309
+ g.crowding_distance = float("inf")
310
+ return
311
+
312
+ for g in front:
313
+ g.crowding_distance = 0.0
314
+
315
+ for dim in PARETO_DIMENSIONS:
316
+ # Sort front by current objective
317
+ front.sort(key=lambda g: g.fitness_scores.get(dim, 0.0))
318
+ front[0].crowding_distance = float("inf")
319
+ front[-1].crowding_distance = float("inf")
320
+
321
+ dim_min = front[0].fitness_scores.get(dim, 0.0)
322
+ dim_max = front[-1].fitness_scores.get(dim, 0.0)
323
+ denom = max(1e-6, dim_max - dim_min)
324
+
325
+ for i in range(1, l - 1):
326
+ if not math.isinf(front[i].crowding_distance):
327
+ prev_score = front[i - 1].fitness_scores.get(dim, 0.0)
328
+ next_score = front[i + 1].fitness_scores.get(dim, 0.0)
329
+ front[i].crowding_distance += (next_score - prev_score) / denom
330
+
331
+ def crossover(self, parent1: CognitiveGenome, parent2: CognitiveGenome) -> Tuple[CognitiveGenome, CognitiveGenome]:
332
+ """Uniform & blended genetic crossover between two architectural genomes."""
333
+ if self.rng.random() > self.crossover_rate:
334
+ # Clone without crossover
335
+ return copy.deepcopy(parent1), copy.deepcopy(parent2)
336
+
337
+ genes1 = copy.deepcopy(parent1.genes)
338
+ genes2 = copy.deepcopy(parent2.genes)
339
+
340
+ for gene_key in list(genes1.keys()):
341
+ if self.rng.random() < 0.5:
342
+ # Swap discrete gene
343
+ genes1[gene_key], genes2[gene_key] = genes2.get(gene_key, genes1[gene_key]), genes1[gene_key]
344
+
345
+ # Blend continuous parameters
346
+ for cont_param in ["verification_depth", "compression_target"]:
347
+ v1 = genes1.get(cont_param, 0.8)
348
+ v2 = genes2.get(cont_param, 0.8)
349
+ alpha = self.rng.uniform(0.2, 0.8)
350
+ genes1[cont_param] = round(alpha * v1 + (1 - alpha) * v2, 4)
351
+ genes2[cont_param] = round((1 - alpha) * v1 + alpha * v2, 4)
352
+
353
+ gid1 = f"g{self.generation_count+1}_{self.rng.randint(100, 999)}"
354
+ gid2 = f"g{self.generation_count+1}_{self.rng.randint(100, 999)}"
355
+
356
+ child1 = CognitiveGenome(
357
+ genome_id=gid1,
358
+ paradigm_name=f"Hybrid({parent1.paradigm_name[:15]}+{parent2.paradigm_name[:15]})",
359
+ genes=genes1,
360
+ generation=self.generation_count + 1,
361
+ lineage=[parent1.genome_id, parent2.genome_id],
362
+ )
363
+ child2 = CognitiveGenome(
364
+ genome_id=gid2,
365
+ paradigm_name=f"Recombinant({parent2.paradigm_name[:15]}+{parent1.paradigm_name[:15]})",
366
+ genes=genes2,
367
+ generation=self.generation_count + 1,
368
+ lineage=[parent2.genome_id, parent1.genome_id],
369
+ )
370
+ return child1, child2
371
+
372
+ def mutate(self, genome: CognitiveGenome) -> CognitiveGenome:
373
+ """Mutate genome discrete alleles and continuous variables."""
374
+ mutated_genes = copy.deepcopy(genome.genes)
375
+
376
+ for gene_name, alleles in GENE_ALLELE_OPTIONS.items():
377
+ if self.rng.random() < self.mutation_rate:
378
+ # Gene flip
379
+ new_val = self.rng.choice(alleles)
380
+ mutated_genes[gene_name] = new_val
381
+
382
+ # Continuous jitter
383
+ if self.rng.random() < self.mutation_rate:
384
+ depth = mutated_genes.get("verification_depth", 0.85)
385
+ mutated_genes["verification_depth"] = round(max(0.1, min(1.0, depth + self.rng.gauss(0, 0.05))), 3)
386
+
387
+ if self.rng.random() < self.mutation_rate:
388
+ workers = mutated_genes.get("concurrency_workers", 4)
389
+ mutated_genes["concurrency_workers"] = max(1, min(32, workers + self.rng.choice([-1, 1, 2])))
390
+
391
+ genome.genes = mutated_genes
392
+ return genome
393
+
394
+ def tournament_selection(self, k: int = 3) -> CognitiveGenome:
395
+ """Crowded Tournament Selection based on Pareto Rank and Crowding Distance."""
396
+ candidates = self.rng.sample(self.population, min(k, len(self.population)))
397
+ # Winner has lower rank (better), or in tie has higher crowding distance
398
+ candidates.sort(key=lambda g: (g.pareto_rank, -g.crowding_distance))
399
+ return candidates[0]
400
+
401
+ def evolve_generation(
402
+ self,
403
+ fitness_fn: Optional[Callable[[CognitiveGenome], Dict[str, float]]] = None,
404
+ elite_ratio: float = 0.2,
405
+ ) -> List[CognitiveGenome]:
406
+ """
407
+ Execute one complete generation of NSGA-II Multi-Objective Evolution:
408
+ 1. Evaluate fitness scores.
409
+ 2. Fast non-dominated sorting into Pareto fronts.
410
+ 3. Compute crowding distance.
411
+ 4. Select elites from Front 1.
412
+ 5. Generate offspring via tournament selection, crossover, and mutation.
413
+ 6. Form new population and increment generation count.
414
+ """
415
+ if not self.population:
416
+ self.initialize_population()
417
+
418
+ self.evaluate_fitness(fitness_fn)
419
+ fronts = self.fast_non_dominated_sort()
420
+ for front in fronts:
421
+ self.calculate_crowding_distance(front)
422
+
423
+ # Record history snapshot
424
+ front1 = fronts[0] if fronts else []
425
+ self.history.append({
426
+ "generation": self.generation_count,
427
+ "population_size": len(self.population),
428
+ "front1_size": len(front1),
429
+ "front1_best_scalar": max([g.compute_scalar_fitness() for g in front1]) if front1 else 0.0,
430
+ "front1_genomes": [g.genome_id for g in front1],
431
+ })
432
+
433
+ # Elite selection from top fronts
434
+ num_elites = max(2, int(self.population_size * elite_ratio))
435
+ next_population: List[CognitiveGenome] = []
436
+
437
+ for front in fronts:
438
+ if len(next_population) + len(front) <= num_elites:
439
+ next_population.extend([copy.deepcopy(g) for g in front])
440
+ else:
441
+ # Partial front fill based on crowding distance
442
+ sorted_front = sorted(front, key=lambda g: g.crowding_distance, reverse=True)
443
+ needed = num_elites - len(next_population)
444
+ next_population.extend([copy.deepcopy(g) for g in sorted_front[:needed]])
445
+ break
446
+
447
+ # Breed remaining offspring
448
+ while len(next_population) < self.population_size:
449
+ p1 = self.tournament_selection()
450
+ p2 = self.tournament_selection()
451
+ c1, c2 = self.crossover(p1, p2)
452
+ c1 = self.mutate(c1)
453
+ next_population.append(c1)
454
+ if len(next_population) < self.population_size:
455
+ c2 = self.mutate(c2)
456
+ next_population.append(c2)
457
+
458
+ self.generation_count += 1
459
+ self.population = next_population
460
+ self.evaluate_fitness(fitness_fn)
461
+ new_fronts = self.fast_non_dominated_sort()
462
+ for front in new_fronts:
463
+ self.calculate_crowding_distance(front)
464
+
465
+ return self.get_pareto_frontier()
466
+
467
+ def get_pareto_frontier(self) -> List[CognitiveGenome]:
468
+ """Return the current Generation's Rank 1 non-dominated Pareto frontier."""
469
+ fronts = self.fast_non_dominated_sort()
470
+ return fronts[0] if fronts else []
471
+
472
+ def get_best_genome(self, weights: Optional[Dict[str, float]] = None) -> CognitiveGenome:
473
+ """Return the highest-scoring genome according to scalar objective weighting."""
474
+ if not self.population:
475
+ self.initialize_population()
476
+ self.evaluate_fitness()
477
+ return max(self.population, key=lambda g: g.compute_scalar_fitness(weights))
478
+
479
+ def to_dict(self) -> Dict[str, Any]:
480
+ """Serialize GenePool to JSON dictionary."""
481
+ return {
482
+ "population_size": self.population_size,
483
+ "generation_count": self.generation_count,
484
+ "mutation_rate": self.mutation_rate,
485
+ "crossover_rate": self.crossover_rate,
486
+ "population": [g.to_dict() for g in self.population],
487
+ "history": self.history,
488
+ }
489
+
490
+ @classmethod
491
+ def from_dict(cls, data: Dict[str, Any]) -> "CognitiveGenePool":
492
+ """Deserialize GenePool from dictionary."""
493
+ pool = cls(
494
+ population_size=data.get("population_size", 20),
495
+ mutation_rate=data.get("mutation_rate", 0.15),
496
+ crossover_rate=data.get("crossover_rate", 0.80),
497
+ )
498
+ pool.generation_count = data.get("generation_count", 0)
499
+ pool.history = data.get("history", [])
500
+ pool.population = [
501
+ CognitiveGenome.from_dict(g) for g in data.get("population", [])
502
+ ]
503
+ return pool