theapemachine commited on
Commit
d9cf00b
Β·
verified Β·
1 Parent(s): e6f258c

feat: self-tuning unified_field.py

Browse files
Files changed (1) hide show
  1. tensegrity/engine/unified_field.py +64 -10
tensegrity/engine/unified_field.py CHANGED
@@ -192,10 +192,22 @@ class UnifiedField:
192
  # FHRR encoder
193
  self.encoder = FHRREncoder(dim=fhrr_dim)
194
 
195
- # Random projection: FHRR (complex, fhrr_dim) β†’ real (obs_dim)
196
- # Fixed, not learned β€” this is the sensory transduction
197
- rng = np.random.RandomState(42)
198
- self._proj = rng.randn(obs_dim, fhrr_dim).astype(np.float64) / np.sqrt(fhrr_dim)
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
  # NGC circuit: hierarchical predictive coding
201
  layer_sizes = [obs_dim] + hidden_dims
@@ -215,9 +227,22 @@ class UnifiedField:
215
  self.energy_history: Deque[EnergyDecomposition] = deque(maxlen=max(1, int(energy_history_maxlen)))
216
 
217
  def _fhrr_to_obs(self, fhrr_vec: np.ndarray) -> np.ndarray:
218
- """Project FHRR complex vector to real observation space."""
 
 
 
 
 
 
219
  real_part = np.real(fhrr_vec).astype(np.float64)
220
- return self._proj @ real_part
 
 
 
 
 
 
 
221
 
222
  def observe(self, raw_input: Any, input_type: str = "numeric") -> Dict[str, Any]:
223
  """
@@ -258,13 +283,16 @@ class UnifiedField:
258
  settle_result = self.ngc.settle(obs_vec)
259
  perception_energy = settle_result["final_energy"]
260
 
261
- prediction_error_post_settle = self.ngc.prediction_error(obs_vec)
262
-
263
- # === 4. REMEMBER: query Hopfield with abstract state ===
 
 
 
264
  abstract_state = self.ngc.get_abstract_state(level=-1)
265
  retrieved, memory_energy = self.memory.retrieve(abstract_state)
266
 
267
- # Compute memory consistency: how similar is this observation to stored patterns?
268
  abstract_norm = np.linalg.norm(abstract_state)
269
  retrieved_norm = np.linalg.norm(retrieved)
270
  if abstract_norm > 1e-8 and retrieved_norm > 1e-8:
@@ -273,6 +301,32 @@ class UnifiedField:
273
  else:
274
  memory_similarity = 0.0
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  # === 5. LEARN: Precision-modulated Hebbian update ===
277
  # Learning modulation: high when observation is consistent with memory,
278
  # low when it contradicts stored patterns.
 
192
  # FHRR encoder
193
  self.encoder = FHRREncoder(dim=fhrr_dim)
194
 
195
+ # Structure-preserving projection: FHRR (complex, fhrr_dim) β†’ real (obs_dim)
196
+ # Instead of a random matrix that destroys semantic structure, we use
197
+ # a fixed projection derived from the FHRR basis itself. The real part
198
+ # of the FHRR vector is sliced/averaged into obs_dim buckets. This
199
+ # preserves the phasor structure: similar FHRR vectors β†’ similar obs.
200
+ #
201
+ # For obs_dim < fhrr_dim: average adjacent blocks of size fhrr_dim/obs_dim.
202
+ # For obs_dim >= fhrr_dim: pad with zeros (rare in practice).
203
+ self._proj_mode = "structured"
204
+ if obs_dim <= fhrr_dim:
205
+ # Structured averaging: each obs dimension = mean of a block of FHRR dims
206
+ self._proj_block_size = fhrr_dim // obs_dim
207
+ self._proj_remainder = fhrr_dim % obs_dim
208
+ else:
209
+ self._proj_block_size = 1
210
+ self._proj_remainder = 0
211
 
212
  # NGC circuit: hierarchical predictive coding
213
  layer_sizes = [obs_dim] + hidden_dims
 
227
  self.energy_history: Deque[EnergyDecomposition] = deque(maxlen=max(1, int(energy_history_maxlen)))
228
 
229
  def _fhrr_to_obs(self, fhrr_vec: np.ndarray) -> np.ndarray:
230
+ """Project FHRR complex vector to real observation space.
231
+
232
+ Uses structure-preserving block averaging instead of random projection.
233
+ Each obs dimension = mean of a contiguous block of FHRR real components.
234
+ This preserves semantic similarity: if two FHRR vectors have similar
235
+ phasor angles, their block averages will also be similar.
236
+ """
237
  real_part = np.real(fhrr_vec).astype(np.float64)
238
+ bs = self._proj_block_size
239
+ obs = np.zeros(self.obs_dim, dtype=np.float64)
240
+ for i in range(self.obs_dim):
241
+ start = i * bs
242
+ end = min(start + bs, len(real_part))
243
+ if start < len(real_part):
244
+ obs[i] = np.mean(real_part[start:end])
245
+ return obs
246
 
247
  def observe(self, raw_input: Any, input_type: str = "numeric") -> Dict[str, Any]:
248
  """
 
283
  settle_result = self.ngc.settle(obs_vec)
284
  perception_energy = settle_result["final_energy"]
285
 
286
+ # === 4. JOINT SETTLING: Hopfield retrieval feeds back into NGC ===
287
+ # This closes the loop that was previously sequential:
288
+ # settle NGC β†’ query Hopfield β†’ DONE (old: pipeline)
289
+ # Now: settle NGC β†’ query Hopfield β†’ inject memory β†’ re-settle NGC
290
+ # The second settle integrates memory evidence, making the energy
291
+ # decomposition genuinely joint rather than a sequential pipeline.
292
  abstract_state = self.ngc.get_abstract_state(level=-1)
293
  retrieved, memory_energy = self.memory.retrieve(abstract_state)
294
 
295
+ # Compute memory consistency
296
  abstract_norm = np.linalg.norm(abstract_state)
297
  retrieved_norm = np.linalg.norm(retrieved)
298
  if abstract_norm > 1e-8 and retrieved_norm > 1e-8:
 
301
  else:
302
  memory_similarity = 0.0
303
 
304
+ # Memory-guided re-settle: blend retrieved memory into top NGC layer
305
+ # and re-settle to integrate memory evidence into the full hierarchy.
306
+ # The blend weight is derived from memory_similarity itself:
307
+ # high similarity β†’ strong blend (memory confirms), low β†’ weak blend.
308
+ if self.memory.n_patterns > 2 and retrieved_norm > 1e-8:
309
+ # Blend weight = sigmoid(memory_similarity * 3) clamped to [0, 0.5]
310
+ # This means memory can provide up to 50% of the top-layer state,
311
+ # but only when it strongly matches the current abstract state.
312
+ blend = float(1.0 / (1.0 + np.exp(-3.0 * memory_similarity)))
313
+ blend = min(blend, 0.5)
314
+
315
+ # Inject retrieved memory into the top NGC layer
316
+ top_layer = self.ngc.layers[-1]
317
+ top_layer.z = (1.0 - blend) * top_layer.z + blend * retrieved
318
+
319
+ # Re-settle with memory evidence integrated
320
+ # Use fewer steps since we're refining, not starting from scratch
321
+ re_settle = self.ngc.settle(obs_vec, steps=max(3, self.ngc.settle_steps // 3))
322
+ perception_energy = re_settle["final_energy"]
323
+
324
+ # Re-query Hopfield with the refined abstract state
325
+ abstract_state = self.ngc.get_abstract_state(level=-1)
326
+ retrieved, memory_energy = self.memory.retrieve(abstract_state)
327
+
328
+ prediction_error_post_settle = self.ngc.prediction_error(obs_vec)
329
+
330
  # === 5. LEARN: Precision-modulated Hebbian update ===
331
  # Learning modulation: high when observation is consistent with memory,
332
  # low when it contradicts stored patterns.