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
Files changed (1) hide show
  1. 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
- # For each trig site, decide mutation type based on probability
47
- for key in keys:
48
- # Use site-specific mutation rate if provided, otherwise use default
49
- site_rate = site_mutation_rates.get(key, mutation_rate) if site_mutation_rates else mutation_rate
50
- if random.random() < site_rate:
51
- # Decide if this should be a trig mutation or constant mutation
52
- if random.random() < trig_mutation_prob:
 
 
 
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
- else:
63
- # Constant mutation instead - find a constant to mutate
64
- if constant_keys:
65
- const_key = random.choice(constant_keys)
66
- old = self.params.get(const_key)
67
- if isinstance(old, (int, float)):
68
- # Randomly increase or decrease
69
- direction = random.choice([-1, 1])
70
- new = old + direction * const_delta
71
- # Keep reasonable bounds (avoid negative divisors, etc.)
72
- if 'div' in const_key.lower():
73
- new = max(0.1, new) # Prevent zero or negative divisors
74
- elif 'mult' in const_key.lower():
75
- new = max(0.01, new) # Prevent zero or negative multipliers
76
- self.params[const_key] = new
77
- self.mutation_log.append(f"{const_key}:{old:.3f}->{new:.3f}")
78
-
79
- # Also allow direct constant mutations (independent of trig site checks)
80
- if constant_keys and random.random() < (1.0 - trig_mutation_prob) * mutation_rate:
81
- key = random.choice(constant_keys)
82
- old = self.params.get(key)
83
- if isinstance(old, (int, float)):
84
- direction = random.choice([-1, 1])
85
- new = old + direction * const_delta
86
- if 'div' in key.lower():
87
- new = max(0.1, new)
88
- elif 'mult' in key.lower():
89
- new = max(0.01, new)
90
- self.params[key] = new
91
- self.mutation_log.append(f"{key}:{old:.3f}->{new:.3f}")
 
 
 
 
 
 
 
 
 
 
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
- return self.trig_mutation_prob_base / math.sqrt(generations_since_base + 1)
 
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.