CHOWDHARY Sandeep commited on
Commit ·
427f6a0
1
Parent(s): a65ef76
Fix trig mutation probability: enforce minimum 0.02, restructure logic to properly follow probability
Browse files- genetic_backend.py +55 -40
genetic_backend.py
CHANGED
|
@@ -43,13 +43,16 @@ class EquationGene:
|
|
| 43 |
has_atan = any(self.params.get(k) == 'atan' for k in keys)
|
| 44 |
const_delta = constant_mutation_delta * 0.5 if has_atan else constant_mutation_delta
|
| 45 |
|
| 46 |
-
#
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
| 53 |
# Trig function mutation
|
| 54 |
old = self.params.get(key)
|
| 55 |
if isinstance(old, str):
|
|
@@ -59,36 +62,46 @@ class EquationGene:
|
|
| 59 |
new = random.choice(choices)
|
| 60 |
self.params[key] = new
|
| 61 |
self.mutation_log.append(f"{key}:{old}->{new}")
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
class GeneticEquationEvolver:
|
| 94 |
def __init__(self, population_size: int = 16, elite_size: int = 4):
|
|
@@ -223,13 +236,15 @@ class GeneticEquationEvolver:
|
|
| 223 |
|
| 224 |
def get_trig_mutation_probability(self) -> float:
|
| 225 |
"""Calculate probability of trig mutations vs constant mutations.
|
| 226 |
-
Always decays from base value using square root, based on generations since base was set.
|
|
|
|
| 227 |
if self.generation == self.trig_mutation_prob_base_generation:
|
| 228 |
# Same generation as when base was set, return base value
|
| 229 |
-
return self.trig_mutation_prob_base
|
| 230 |
# Decay from base: base / sqrt(generations_since_base_set + 1)
|
| 231 |
generations_since_base = self.generation - self.trig_mutation_prob_base_generation
|
| 232 |
-
|
|
|
|
| 233 |
|
| 234 |
def get_constant_mutation_delta(self) -> float:
|
| 235 |
"""Calculate mutation delta for constants.
|
|
|
|
| 43 |
has_atan = any(self.params.get(k) == 'atan' for k in keys)
|
| 44 |
const_delta = constant_mutation_delta * 0.5 if has_atan else constant_mutation_delta
|
| 45 |
|
| 46 |
+
# Decide upfront: will this mutation cycle be trig-focused or constant-focused?
|
| 47 |
+
# This ensures the probability is properly respected
|
| 48 |
+
use_trig_mutation = random.random() < trig_mutation_prob
|
| 49 |
+
|
| 50 |
+
if use_trig_mutation:
|
| 51 |
+
# Trig-focused: mutate trig sites, with small chance for constant mutations
|
| 52 |
+
for key in keys:
|
| 53 |
+
# Use site-specific mutation rate if provided, otherwise use default
|
| 54 |
+
site_rate = site_mutation_rates.get(key, mutation_rate) if site_mutation_rates else mutation_rate
|
| 55 |
+
if random.random() < site_rate:
|
| 56 |
# Trig function mutation
|
| 57 |
old = self.params.get(key)
|
| 58 |
if isinstance(old, str):
|
|
|
|
| 62 |
new = random.choice(choices)
|
| 63 |
self.params[key] = new
|
| 64 |
self.mutation_log.append(f"{key}:{old}->{new}")
|
| 65 |
+
|
| 66 |
+
# Small chance for constant mutations even in trig-focused mode (10% of constant mutation rate)
|
| 67 |
+
if constant_keys and random.random() < 0.1 * mutation_rate:
|
| 68 |
+
const_key = random.choice(constant_keys)
|
| 69 |
+
old = self.params.get(const_key)
|
| 70 |
+
if isinstance(old, (int, float)):
|
| 71 |
+
direction = random.choice([-1, 1])
|
| 72 |
+
new = old + direction * const_delta
|
| 73 |
+
if 'div' in const_key.lower():
|
| 74 |
+
new = max(0.1, new)
|
| 75 |
+
elif 'mult' in const_key.lower():
|
| 76 |
+
new = max(0.01, new)
|
| 77 |
+
self.params[const_key] = new
|
| 78 |
+
self.mutation_log.append(f"{const_key}:{old:.3f}->{new:.3f}")
|
| 79 |
+
else:
|
| 80 |
+
# Constant-focused: primarily mutate constants, with very small chance for trig mutations
|
| 81 |
+
# Mutate constants
|
| 82 |
+
if constant_keys and random.random() < mutation_rate:
|
| 83 |
+
key = random.choice(constant_keys)
|
| 84 |
+
old = self.params.get(key)
|
| 85 |
+
if isinstance(old, (int, float)):
|
| 86 |
+
direction = random.choice([-1, 1])
|
| 87 |
+
new = old + direction * const_delta
|
| 88 |
+
if 'div' in key.lower():
|
| 89 |
+
new = max(0.1, new)
|
| 90 |
+
elif 'mult' in key.lower():
|
| 91 |
+
new = max(0.01, new)
|
| 92 |
+
self.params[key] = new
|
| 93 |
+
self.mutation_log.append(f"{key}:{old:.3f}->{new:.3f}")
|
| 94 |
+
|
| 95 |
+
# Very small chance for trig mutations in constant-focused mode (5% of trig mutation rate)
|
| 96 |
+
if keys and random.random() < 0.05 * trig_mutation_prob * mutation_rate:
|
| 97 |
+
key = random.choice(keys)
|
| 98 |
+
old = self.params.get(key)
|
| 99 |
+
if isinstance(old, str):
|
| 100 |
+
choices = [f for f in TRIG_FUNCS if f != old]
|
| 101 |
+
if choices:
|
| 102 |
+
new = random.choice(choices)
|
| 103 |
+
self.params[key] = new
|
| 104 |
+
self.mutation_log.append(f"{key}:{old}->{new}")
|
| 105 |
|
| 106 |
class GeneticEquationEvolver:
|
| 107 |
def __init__(self, population_size: int = 16, elite_size: int = 4):
|
|
|
|
| 236 |
|
| 237 |
def get_trig_mutation_probability(self) -> float:
|
| 238 |
"""Calculate probability of trig mutations vs constant mutations.
|
| 239 |
+
Always decays from base value using square root, based on generations since base was set.
|
| 240 |
+
Minimum probability is 0.02."""
|
| 241 |
if self.generation == self.trig_mutation_prob_base_generation:
|
| 242 |
# Same generation as when base was set, return base value
|
| 243 |
+
return max(0.02, self.trig_mutation_prob_base)
|
| 244 |
# Decay from base: base / sqrt(generations_since_base_set + 1)
|
| 245 |
generations_since_base = self.generation - self.trig_mutation_prob_base_generation
|
| 246 |
+
prob = self.trig_mutation_prob_base / math.sqrt(generations_since_base + 1)
|
| 247 |
+
return max(0.02, prob) # Ensure minimum of 0.02
|
| 248 |
|
| 249 |
def get_constant_mutation_delta(self) -> float:
|
| 250 |
"""Calculate mutation delta for constants.
|