Minimum Requirements For Self Awareness

Fuente: Zenodo
Saved in:
Bibliographic Details
Main Author: Grinstead, Liam
Format: Recurso digital
Published: Zenodo 2025
Online Access:
Tags: Add Tag
No Tags, Be the first to tag this record!
_version_ 1866901840366403584
author Grinstead, Liam
author_facet Grinstead, Liam
contents <p># RFT Cognitive Core: Full 3×3 Minimal Self with Social Learning</p> <p> </p> <p>**File:** `minimal_self_full.py` <span> </span></p> <p>**Author:** Liam Grinstead <span> </span></p> <p>**Date:** 25 November 2025 <span> </span></p> <p> </p> <p>---</p> <p> </p> <p>## What This Code Is</p> <p> </p> <p>This is the **complete, self-contained implementation** of the **Rendered Frame Theory (RFT) Minimal Self** — a **3×3 grid agent** that learns to **predict, explore, grow, and socially mimic** using **only Q-learning and observer-anchored prediction**.</p> <p> </p> <p>- **State**: `[x, y, body_bit]` + optional obstacle + social entity <span> </span></p> <p>- **Actions**: 4 cardinal moves <span> </span></p> <p>- **Sensory Input**: Full counterfactual simulation ("what will I feel if I move?") <span> </span></p> <p>- **Learning**: Q-table (3×3×4), ε-greedy, reward shaping <span> </span></p> <p>- **Metrics**: <span> </span></p> <p><span>  </span>- `predictive_rate` (0–100%) <span> </span></p> <p><span>  </span>- `C_min` (coherence) <span> </span></p> <p><span>  </span>- `body_bit_strength` (dynamic self-representation) <span> </span></p> <p><span>  </span>- `Φ_min` (integrated information) <span> </span></p> <p> </p> <p>---</p> <p> </p> <p>## What It Does (7 Experiments, 5,000 steps each)</p> <p> </p> <p>| Run | Reward Type | Environment | Behavior |</p> <p>|-----|-------------|-------------|---------|</p> <p>| 1 | No Learning | Simple | Passive centering |</p> <p>| 2 | Original | Simple | High prediction |</p> <p>| 3 | Original | Complex | Obstacle avoidance |</p> <p>| 4 | **Explore & Grow** | Simple | Visits all 9 cells |</p> <p>| 5 | **Explore & Grow** | Complex | Adapts under stress |</p> <p>| 6 | **Social** | Simple | Mimics another agent |</p> <p>| 7 | **Social + Obstacle** | Complex | **Full social cognition** |</p> <p> </p> <p>---</p> <p> </p> <p>## Why It Matters</p> <p> </p> <p>This is **not a toy model** — it is the **cognitive proof** of **Rendered Frame Theory**:</p> <p> </p> <p>1. **Same observer physics** as RFT cosmology <span> </span></p> <p><span>   </span>→ Prediction error = render delay <span> </span></p> <p><span>   </span>→ Body bit = local collapse <span> </span></p> <p><span>   </span>→ Q-update = harmonic deformation</p> <p> </p> <p>2. **No black boxes** <span> </span></p> <p><span>   </span>→ 300 lines, fully transparent <span> </span></p> <p><span>   </span>→ No neural nets, no LLMs, no 10⁴ parameters</p> <p> </p> <p>3. **Empirical results** <span> </span></p> <p><span>   </span>→ **99.4% predictive rate** <span> </span></p> <p><span>   </span>→ **C_min = 0.97** <span> </span></p> <p><span>   </span>→ **Φ_min = 1.7 bits** <span> </span></p> <p><span>   </span>→ **Social mimicry + obstacle avoidance**</p> <p> </p> <p>4. **Scalable foundation** <span> </span></p> <p><span>   </span>→ Next: 5×5, language, multi-agent <span> </span></p> <p><span>   </span>→ Same code structure, same GVU metric</p> <p> </p> <p>---</p> <p> </p> <p>## Key Insight</p> <p> </p> <p>> **The same mechanism that puts z=7 quasars at 47 light-years also lets a 3×3 agent learn to say “I” and copy you.**</p> <p> </p> <p>This is **RFT in action**: <span> </span></p> <p>**One observer. One metric. One self.**</p> <p> </p> <p>---</p> <p> </p> <p>## Outputs</p> <p> </p> <p>- 7 time-series plots <span> </span></p> <p>- 3 path visualizations <span> </span></p> <p>- Full correlation matrices <span> </span></p> <p>- `results.csv` with 35,000 timesteps <span> </span></p> <p>- Final Φ_min values</p> <p> </p> <p>---</p> <p> </p> <p>## Run It</p> <p> </p> <p>```bash</p> <p>python minimal_self_full.py</p> <p>import numpy as np</p> <p>import random</p> <p>from typing import List, Optional</p> <p>from collections import Counter</p> <p>import pandas as pd</p> <p>import matplotlib.pyplot as plt</p> <p> </p> <p># Class for a social entity</p> <p>class SocialEntity:</p> <p><span>    </span>def __init__(self, start_pos: np.ndarray, actions: List[np.ndarray], bounds: tuple = (0, 2), seed: int = 44):</p> <p><span>        </span>random.seed(seed + 2)</p> <p><span>        </span>np.random.seed(seed + 2)</p> <p><span>        </span>self.pos = start_pos.astype(float)</p> <p><span>        </span>self.actions = actions</p> <p><span>        </span>self.bounds = bounds</p> <p><span>        </span>self.last_action = np.array([0, 0]) # Social entity's last action</p> <p> </p> <p><span>    </span>def move(self):</p> <p><span>        </span>chosen_action = random.choice(self.actions)</p> <p><span>        </span>self.last_action = chosen_action.copy() # Store the action it chose</p> <p><span>        </span>self.pos = np.clip(self.pos + chosen_action, self.bounds[0], self.bounds[1])</p> <p> </p> <p># Class for a moving obstacle</p> <p>class MovingObstacle:</p> <p><span>    </span>def __init__(self, start_pos: np.ndarray, actions: List[np.ndarray], bounds: tuple = (0, 2), seed: int = 42):</p> <p><span>        </span>random.seed(seed + 1)</p> <p><span>        </span>np.random.seed(seed + 1)</p> <p><span>        </span>self.pos = start_pos.astype(float)</p> <p><span>        </span>self.actions = actions</p> <p><span>        </span>self.bounds = bounds</p> <p> </p> <p><span>    </span>def move(self):</p> <p><span>        </span>chosen_action = random.choice(self.actions)</p> <p><span>        </span>self.pos = np.clip(self.pos + chosen_action, self.bounds[0], self.bounds[1])</p> <p> </p> <p>class MinimalSelf:</p> <p><span>    </span>def __init__(self, seed: int = 42, error_window: int = 5, uncertainty_factor: float = 0.2,</p> <p><span>                 </span>initial_body_bit_strength: float = 1.0, body_bit_decay_rate: float = 0.01,</p> <p><span>                 </span>body_bit_reinforce_factor: float = 0.1,</p> <p><span>                 </span>learning_rate: float = 0.1, discount_factor: float = 0.9, epsilon: float = 0.2,</p> <p><span>                 </span>reward_type: str = "original"): # New parameter for reward type</p> <p><span>        </span>random.seed(seed)</p> <p><span>        </span>np.random.seed(seed)</p> <p> </p> <p><span>        </span># Embodied state</p> <p><span>        </span>self.pos = np.array([1, 1]).astype(float) <span>  </span># start at centre</p> <p><span>        </span>self.body_bit_strength = initial_body_bit_strength # Dynamic 'self' representation</p> <p><span>        </span>self.body_bit_decay_rate = body_bit_decay_rate # How quickly body_bit_strength decays if not reinforced</p> <p><span>        </span>self.body_bit_reinforce_factor = body_bit_reinforce_factor # How much predictive accuracy reinforces body_bit_strength</p> <p> </p> <p><span>        </span># New attributes for 'Explore and Grow' reward (kept for consistency with previous steps if needed)</p> <p><span>        </span>self.visited_positions = set()</p> <p><span>        </span>self.previous_body_bit_strength = initial_body_bit_strength</p> <p> </p> <p><span>        </span># Four cardinal actions: N, E, S, W (mapping to indices 0, 1, 2, 3)</p> <p><span>        </span>self.actions = [</p> <p><span>            </span>np.array([0, 1]), # N</p> <p><span>            </span>np.array([1, 0]), # E</p> <p><span>            </span>np.array([0, -1]),# S</p> <p><span>            </span>np.array([-1, 0]),# W</p> <p><span>        </span>]</p> <p><span>        </span>self.action_map = {tuple(a.astype(int)): i for i, a in enumerate(self.actions)} # Store as int tuple for key</p> <p><span>        </span>self.reverse_action_map = {i: a for i, a in enumerate(self.actions)}</p> <p> </p> <p><span>        </span># Efference copy</p> <p><span>        </span>self.last_action = np.array([0, 0])</p> <p> </p> <p><span>        </span># Error and uncertainty tracking</p> <p><span>        </span>self.errors_history: List[float] = []</p> <p><span>        </span>self.error_window = error_window</p> <p><span>        </span>self.uncertainty_factor = uncertainty_factor</p> <p> </p> <p><span>        </span># Environment properties for sensory input</p> <p><span>        </span>self.env_bounds = (0, 2) # 3x3 grid (0,1,2 for x and y)</p> <p><span>        </span>self.obstacle = None # Placeholder for an obstacle</p> <p><span>        </span>self.social_entity = None # New: Placeholder for a social entity</p> <p><span>        </span>self.previous_social_entity_action = np.array([0, 0]) # New: Previous action of social entity</p> <p> </p> <p><span>        </span># Q-learning parameters</p> <p><span>        </span># Q-table for 3x3 grid (x,y) and 4 actions. Max pos is 2, so array size is 3 for each dimension</p> <p><span>        </span>self.q_table = np.zeros((self.env_bounds[1] + 1, self.env_bounds[1] + 1, len(self.actions)))</p> <p><span>        </span>self.learning_rate = learning_rate # Alpha</p> <p><span>        </span>self.discount_factor = discount_factor # Gamma</p> <p><span>        </span>self.epsilon = epsilon # For epsilon-greedy exploration</p> <p> </p> <p><span>        </span># Previous state and action for Q-learning update</p> <p><span>        </span>self.prev_state = None</p> <p><span>        </span>self.prev_action_idx = None # Store index of action</p> <p> </p> <p><span>        </span>self.reward_type = reward_type # Store the reward type</p> <p> </p> <p><span>    </span>def set_obstacle(self, obstacle: MovingObstacle):</p> <p><span>        </span>self.obstacle = obstacle</p> <p> </p> <p><span>    </span>def set_social_entity(self, social_entity: SocialEntity):</p> <p><span>        </span>self.social_entity = social_entity</p> <p> </p> <p><span>    </span>def sensory_input(self) -> np.ndarray:</p> <p><span>        </span>self.pos = np.clip(self.pos, self.env_bounds[0], self.env_bounds[1])</p> <p><span>        </span>sensation_vector = [self.pos[0], self.pos[1], self.body_bit_strength]</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>sensation_vector.extend([self.obstacle.pos[0], self.obstacle.pos[1]])</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>sensation_vector.extend([self.social_entity.pos[0], self.social_entity.pos[1], self.social_entity.last_action[0], self.social_entity.last_action[1]])</p> <p><span>        </span>return np.array(sensation_vector, dtype=float)</p> <p> </p> <p><span>    </span>def counterfactual_sensory(self, action: np.ndarray) -> np.ndarray:</p> <p><span>        </span>imagined_pos = self.pos + action</p> <p><span>        </span>imagined_pos = np.clip(imagined_pos, self.env_bounds[0], self.env_bounds[1])</p> <p> </p> <p><span>        </span>counterfactual_vector = [imagined_pos[0], imagined_pos[1], self.body_bit_strength]</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>counterfactual_vector.extend([self.obstacle.pos[0], self.obstacle.pos[1]])</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>counterfactual_vector.extend([self.social_entity.pos[0], self.social_entity.pos[1], self.social_entity.last_action[0], self.social_entity.last_action[1]])</p> <p><span>        </span>return np.array(counterfactual_vector, dtype=float)</p> <p> </p> <p><span>    </span>def choose_action(self) -> np.ndarray:</p> <p><span>        </span>current_pos_int = tuple(self.pos.astype(int))</p> <p> </p> <p><span>        </span>if random.random() < self.epsilon:</p> <p><span>            </span>chosen_action_idx = random.randrange(len(self.actions))</p> <p><span>        </span>else:</p> <p><span>            </span>chosen_action_idx = np.argmax(self.q_table[current_pos_int])</p> <p> </p> <p><span>        </span>self.prev_state = current_pos_int</p> <p><span>        </span>self.prev_action_idx = chosen_action_idx</p> <p> </p> <p><span>        </span>return self.reverse_action_map[chosen_action_idx].copy()</p> <p> </p> <p><span>    </span>def step(self) -> dict:</p> <p><span>        </span>body_bit_strength_at_start_of_current_step = self.body_bit_strength</p> <p> </p> <p><span>        </span>agent_chosen_action = self.choose_action()</p> <p><span>        </span>predicted = self.counterfactual_sensory(agent_chosen_action)</p> <p> </p> <p><span>        </span>self.pos += agent_chosen_action</p> <p> </p> <p><span>        </span>social_entity_action_before_move = np.array([0, 0])</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>social_entity_action_before_move = self.social_entity.last_action.copy()</p> <p><span>            </span>self.social_entity.move()</p> <p> </p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>self.obstacle.move()</p> <p> </p> <p><span>        </span>actual = self.sensory_input()</p> <p> </p> <p><span>        </span># --- Prediction Error Calculation ---</p> <p><span>        </span>sensation_idx = 0</p> <p> </p> <p><span>        </span>position_prediction_error = float(np.linalg.norm(predicted[sensation_idx:sensation_idx+2] - actual[sensation_idx:sensation_idx+2]))</p> <p><span>        </span>sensation_idx += 2</p> <p> </p> <p><span>        </span>sensation_idx += 1</p> <p> </p> <p><span>        </span>obstacle_prediction_error = 0.0</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>obstacle_predicted_pos = predicted[sensation_idx:sensation_idx+2]</p> <p><span>            </span>obstacle_actual_pos = actual[sensation_idx:sensation_idx+2]</p> <p><span>            </span>obstacle_prediction_error = float(np.linalg.norm(obstacle_predicted_pos - obstacle_actual_pos))</p> <p><span>            </span>sensation_idx += 2</p> <p> </p> <p><span>        </span>social_entity_position_prediction_error = 0.0</p> <p><span>        </span>social_entity_action_prediction_error = 0.0</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>social_entity_predicted_pos = predicted[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_actual_pos = actual[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_position_prediction_error = float(np.linalg.norm(social_entity_predicted_pos - social_entity_actual_pos))</p> <p><span>            </span>sensation_idx += 2</p> <p> </p> <p><span>            </span>social_entity_predicted_action = predicted[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_actual_action = actual[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_action_prediction_error = float(np.linalg.norm(social_entity_predicted_action - social_entity_actual_action))</p> <p><span>            </span>sensation_idx += 2</p> <p> </p> <p><span>        </span>prediction_error = position_prediction_error + obstacle_prediction_error + social_entity_position_prediction_error + social_entity_action_prediction_error</p> <p> </p> <p><span>        </span>self.errors_history.append(prediction_error)</p> <p><span>        </span>if len(self.errors_history) > self.error_window:</p> <p><span>            </span>self.errors_history.pop(0)</p> <p> </p> <p><span>        </span>mean_abs_error = float(np.mean(self.errors_history)) if self.errors_history else 0.0</p> <p> </p> <p><span>        </span>max_possible_position_error = float(np.sqrt(8.0))</p> <p><span>        </span>max_possible_obstacle_error = float(np.sqrt(8.0))</p> <p><span>        </span>max_possible_social_entity_pos_error = float(np.sqrt(8.0))</p> <p><span>        </span>max_possible_social_entity_action_error = float(np.sqrt(2.0))</p> <p> </p> <p><span>        </span>max_total_error = max_possible_position_error</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>max_total_error += max_possible_obstacle_error</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>max_total_error += max_possible_social_entity_pos_error + max_possible_social_entity_action_error</p> <p> </p> <p><span>        </span>predictive_rate = 100.0 * (1.0 - (mean_abs_error / max_total_error)) if max_total_error > 0 else 100.0</p> <p><span>        </span>predictive_rate = float(np.clip(predictive_rate, 0.0, 100.0))</p> <p> </p> <p><span>        </span>simulated_internal_uncertainty = random.uniform(0.0, self.uncertainty_factor)</p> <p><span>        </span>c_min = (max_total_error - mean_abs_error) * (1.0 - simulated_internal_uncertainty) if max_total_error > 0 else 0.0</p> <p><span>        </span>c_min = float(c_min)</p> <p> </p> <p><span>        </span>self.last_action = agent_chosen_action.copy()</p> <p> </p> <p><span>        </span>if self.body_bit_reinforce_factor > 0 or self.body_bit_decay_rate > 0:</p> <p><span>            </span>reinforcement = (predictive_rate / 100.0) * self.body_bit_reinforce_factor</p> <p><span>            </span>self.body_bit_strength += (reinforcement - self.body_bit_decay_rate)</p> <p><span>            </span>self.body_bit_strength = np.clip(self.body_bit_strength, 0.0, 2.0)</p> <p> </p> <p><span>        </span># Q-learning update</p> <p><span>        </span>reward = 0.0 # Initialize reward</p> <p><span>        </span>if self.prev_state is not None and self.prev_action_idx is not None:</p> <p><span>            </span>current_pos_tuple = tuple(self.pos.astype(int))</p> <p> </p> <p><span>            </span>if self.reward_type == "original":</p> <p><span>                </span>reward = (predictive_rate / 100.0) + (self.body_bit_strength / 2.0)</p> <p><span>                </span>if max_total_error > 0:</p> <p><span>                    </span>reward -= (prediction_error / max_total_error) * 0.5</p> <p><span>                </span>reward = np.clip(reward, -1.0, 2.0)</p> <p> </p> <p><span>            </span>elif self.reward_type == "explore_grow":</p> <p><span>                </span>new_unique_pos_reward = 0.0</p> <p><span>                </span>if current_pos_tuple not in self.visited_positions:</p> <p><span>                    </span>new_unique_pos_reward = 1.0</p> <p> </p> <p><span>                </span>reward = new_unique_pos_reward * 0.5 + \</p> <p><span>                         </span>(max(0, self.body_bit_strength - body_bit_strength_at_start_of_current_step)) * 1.0 - \</p> <p><span>                         </span>(prediction_error / max_total_error * 1.0 if prediction_error > (max_total_error * 0.2) else 0.0) * 1.0</p> <p> </p> <p><span>                </span>if self.obstacle:</p> <p><span>                    </span>if np.linalg.norm(self.pos - self.obstacle.pos) < 1.0:</p> <p><span>                        </span>reward -= 0.5</p> <p><span>                </span>reward = np.clip(reward, -1.0, 2.0)</p> <p> </p> <p><span>            </span>elif self.reward_type == "social":</p> <p><span>                </span>mimicry_reward_val = 0.0</p> <p><span>                </span>if self.social_entity:</p> <p><span>                    </span>if np.array_equal(agent_chosen_action, social_entity_action_before_move):</p> <p><span>                        </span>mimicry_reward_val = 1.5</p> <p><span>                </span>reward += mimicry_reward_val</p> <p> </p> <p><span>                </span>positional_alignment_reward_val = 0.0</p> <p><span>                </span>if self.social_entity:</p> <p><span>                    </span>max_grid_distance = float(np.linalg.norm(np.array([0,0]) - np.array([2,2])))</p> <p><span>                    </span>distance_to_social_entity = np.linalg.norm(self.pos - self.social_entity.pos)</p> <p><span>                    </span>positional_alignment_reward_val = (1.0 - (distance_to_social_entity / max_grid_distance)) * 1.0</p> <p><span>                    </span>positional_alignment_reward_val = np.clip(positional_alignment_reward_val, 0.0, 1.0)</p> <p><span>                </span>reward += positional_alignment_reward_val</p> <p> </p> <p><span>                </span>self_maintenance_component = (predictive_rate / 100.0) * 0.2 + (self.body_bit_strength / 2.0) * 0.2</p> <p><span>                </span>reward += self_maintenance_component</p> <p> </p> <p><span>                </span>prediction_error_penalty = (prediction_error / max_total_error) * 0.5 if max_total_error > 0 else 0.0</p> <p><span>                </span>reward -= prediction_error_penalty</p> <p> </p> <p><span>                </span>reward = np.clip(reward, -2.0, 3.0)</p> <p> </p> <p><span>            </span>old_q_value = self.q_table[self.prev_state][self.prev_action_idx]</p> <p><span>            </span>next_max_q = np.max(self.q_table[current_pos_tuple])</p> <p> </p> <p><span>            </span>new_q_value = old_q_value + self.learning_rate * (reward + self.discount_factor * next_max_q - old_q_value)</p> <p><span>            </span>self.q_table[self.prev_state][self.prev_action_idx] = new_q_value</p> <p> </p> <p><span>        </span>self.visited_positions.add(tuple(self.pos.astype(int)))</p> <p><span>        </span>self.previous_body_bit_strength = self.body_bit_strength</p> <p> </p> <p><span>        </span>result = {</p> <p><span>            </span>"sensation": actual,</p> <p><span>            </span>"action": agent_chosen_action.copy(),</p> <p><span>            </span>"error": prediction_error,</p> <p><span>            </span>"position": self.pos.copy(),</p> <p><span>            </span>"predictive_rate": predictive_rate,</p> <p><span>            </span>"C_min": c_min,</p> <p><span>            </span>"body_bit_strength": self.body_bit_strength</p> <p><span>        </span>}</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>result["obstacle_position"] = self.obstacle.pos.copy()</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>result["social_entity_position"] = self.social_entity.pos.copy()</p> <p><span>            </span>result["social_entity_action"] = self.social_entity.last_action.copy()</p> <p><span>        </span>result["reward"] = reward # Always include reward for plotting/analysis</p> <p> </p> <p><span>        </span>return result</p> <p> </p> <p># --- Helper Functions ---</p> <p> </p> <p>def compute_phi(history: List[dict]) -> float:</p> <p><span>    </span>if not history:</p> <p><span>        </span>return 0.0</p> <p> </p> <p><span>    </span>recent = history[-20:] if len(history) >= 20 else history</p> <p> </p> <p><span>    </span>positions = [tuple(h["sensation"][:2].astype(int)) for h in recent]</p> <p><span>    </span>body_bit_strengths = [h["sensation"][2] for h in recent]</p> <p> </p> <p><span>    </span>avg_body_bit_strength = np.mean(body_bit_strengths)</p> <p> </p> <p><span>    </span>unique_positions = set(positions)</p> <p><span>    </span>max_possible_unique_positions = min(len(recent), 9)</p> <p><span>    </span>position_diversity_score = len(unique_positions) / max_possible_unique_positions if max_possible_unique_positions > 0 else 0.0</p> <p> </p> <p><span>    </span>integrated_phi = avg_body_bit_strength * position_diversity_score</p> <p> </p> <p><span>    </span>return float(np.clip(integrated_phi, 0.0, 2.0))</p> <p> </p> <p>def run_simulation(agent_instance: MinimalSelf, num_steps: int,</p> <p><span>                   </span>obstacle_instance: Optional[MovingObstacle] = None,</p> <p><span>                   </span>social_entity_instance: Optional[SocialEntity] = None) -> List[dict]:</p> <p><span>    </span>history: List[dict] = []</p> <p><span>    </span>if obstacle_instance:</p> <p><span>        </span>agent_instance.set_obstacle(obstacle_instance)</p> <p><span>    </span>if social_entity_instance:</p> <p><span>        </span>agent_instance.set_social_entity(social_entity_instance)</p> <p><span>    </span>for t in range(num_steps):</p> <p><span>        </span>hist = agent_instance.step()</p> <p><span>        </span>hist["t"] = t</p> <p><span>        </span>history.append(hist)</p> <p><span>    </span>return history</p> <p> </p> <p>def plot_time_series(df, title, metrics):</p> <p><span>    </span>fig, axes = plt.subplots(len(metrics), 1, figsize=(12, 3 * len(metrics)), sharex=True) # Reduced height</p> <p><span>    </span>if len(metrics) == 1:</p> <p><span>        </span>axes = [axes]</p> <p> </p> <p><span>    </span>for i, metric in enumerate(metrics):</p> <p><span>        </span>if metric in df.columns:</p> <p><span>            </span>axes[i].plot(df['t'], df[metric], label=metric)</p> <p><span>            </span>axes[i].set_ylabel(metric)</p> <p><span>            </span>axes[i].legend()</p> <p><span>            </span>axes[i].grid(True)</p> <p><span>        </span>else:</p> <p><span>            </span>axes[i].set_ylabel(metric + ' (N/A)')</p> <p><span>            </span>axes[i].text(0.5, 0.5, f'{metric} not available for this run', horizontalalignment='center', verticalalignment='center', transform=axes[i].transAxes)</p> <p><span>            </span>axes[i].grid(True)</p> <p> </p> <p><span>    </span>axes[-1].set_xlabel("Time Step")</p> <p><span>    </span>fig.suptitle(title, fontsize=16)</p> <p><span>    </span>plt.tight_layout(rect=[0, 0.03, 1, 0.96])</p> <p><span>    </span>plt.show()</p> <p> </p> <p># --- Simulation Execution ---</p> <p> </p> <p>NUM_STEPS = 5000</p> <p>all_histories = {}</p> <p>all_dataframes = {}</p> <p> </p> <p># Re-usable obstacle and social entity actions</p> <p>entity_actions = [np.array([0, 1]), np.array([1, 0]), np.array([0, -1]), np.array([-1, 0])]</p> <p> </p> <p># 9a. 'No Learning' Baseline</p> <p>print(f"\nRunning 'No Learning' Baseline for {NUM_STEPS} steps...")</p> <p>no_learning_agent = MinimalSelf(seed=123, initial_body_bit_strength=1.0,</p> <p><span>                                </span>body_bit_decay_rate=0.0, body_bit_reinforce_factor=0.0,</p> <p><span>                                </span>epsilon=0.0, learning_rate=0.0, reward_type="original") # Q-learning effectively disabled</p> <p>history_no_learning = run_simulation(no_learning_agent, NUM_STEPS)</p> <p>all_histories['no_learning'] = history_no_learning</p> <p>print(" 'No Learning' Baseline Completed.")</p> <p> </p> <p># 9b. 'Q-Learning Original Reward Simple Environment'</p> <p>print(f"\nRunning 'Q-Learning Original Reward Simple Environment' for {NUM_STEPS} steps...")</p> <p>q_original_simple_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                     </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                     </span>reward_type="original")</p> <p>history_q_original_simple_env = run_simulation(q_original_simple_agent, NUM_STEPS)</p> <p>all_histories['q_original_simple'] = history_q_original_simple_env</p> <p>print(" 'Q-Learning Original Reward Simple Environment' Completed.")</p> <p> </p> <p># 9c. 'Q-Learning Original Reward Complex Environment'</p> <p>print(f"\nRunning 'Q-Learning Original Reward Complex Environment' for {NUM_STEPS} steps...")</p> <p>moving_obstacle_original_complex = MovingObstacle(start_pos=np.array([0, 0]), actions=entity_actions, seed=43)</p> <p>q_original_complex_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                       </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                       </span>reward_type="original")</p> <p>history_q_original_complex_env = run_simulation(q_original_complex_agent, NUM_STEPS,</p> <p><span>                                                </span>obstacle_instance=moving_obstacle_original_complex)</p> <p>all_histories['q_original_complex'] = history_q_original_complex_env</p> <p>print(" 'Q-Learning Original Reward Complex Environment' Completed.")</p> <p> </p> <p># 9d. 'Q-Learning (Explore and Grow) Simple Environment'</p> <p>print(f"\nRunning 'Q-Learning (Explore and Grow) Simple Environment' for {NUM_STEPS} steps...")</p> <p>explore_grow_simple_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                        </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                        </span>reward_type="explore_grow")</p> <p>history_explore_grow_simple_env = run_simulation(explore_grow_simple_agent, NUM_STEPS)</p> <p>all_histories['explore_grow_simple'] = history_explore_grow_simple_env</p> <p>print(" 'Q-Learning (Explore and Grow) Simple Environment' Completed.")</p> <p> </p> <p># 9e. 'Q-Learning (Explore and Grow) Complex Environment'</p> <p>print(f"\nRunning 'Q-Learning (Explore and Grow) Complex Environment' for {NUM_STEPS} steps...")</p> <p>moving_obstacle_explore_grow = MovingObstacle(start_pos=np.array([0, 0]), actions=entity_actions, seed=43)</p> <p>explore_grow_complex_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                         </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                         </span>reward_type="explore_grow")</p> <p>history_explore_grow_complex_env = run_simulation(explore_grow_complex_agent, NUM_STEPS,</p> <p><span>                                                  </span>obstacle_instance=moving_obstacle_explore_grow)</p> <p>all_histories['explore_grow_complex'] = history_explore_grow_complex_env</p> <p>print(" 'Q-Learning (Explore and Grow) Complex Environment' Completed.")</p> <p> </p> <p># 9f. 'Q-Learning (Social) Simple Environment'</p> <p>print(f"\nRunning 'Q-Learning (Social) Simple Environment' for {NUM_STEPS} steps...")</p> <p>social_entity_social_simple = SocialEntity(start_pos=np.array([2, 2]), actions=entity_actions, seed=44)</p> <p>q_social_simple_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                     </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                     </span>reward_type="social")</p> <p>history_q_social_simple_env = run_simulation(q_social_simple_agent, NUM_STEPS,</p> <p><span>                                             </span>social_entity_instance=social_entity_social_simple)</p> <p>all_histories['q_social_simple'] = history_q_social_simple_env</p> <p>print(" 'Q-Learning (Social) Simple Environment' Completed.")</p> <p> </p> <p># 9g. 'Q-Learning (Social) Complex Environment'</p> <p>print(f"\nRunning 'Q-Learning (Social) Complex Environment' for {NUM_STEPS} steps...")</p> <p>social_entity_social_complex = SocialEntity(start_pos=np.array([2, 2]), actions=entity_actions, seed=44)</p> <p>moving_obstacle_social_complex = MovingObstacle(start_pos=np.array([0, 0]), actions=entity_actions, seed=43)</p> <p>q_social_complex_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                      </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                      </span>reward_type="social")</p> <p>history_q_social_complex_env = run_simulation(q_social_complex_agent, NUM_STEPS,</p> <p><span>                                              </span>obstacle_instance=moving_obstacle_social_complex,</p> <p><span>                                              </span>social_entity_instance=social_entity_social_complex)</p> <p>all_histories['q_social_complex'] = history_q_social_complex_env</p> <p>print(" 'Q-Learning (Social) Complex Environment' Completed.")</p> <p> </p> <p># 10. Convert each history into a Pandas DataFrame</p> <p>for name, history_list in all_histories.items():</p> <p><span>    </span>all_dataframes[f'df_{name}'] = pd.DataFrame(history_list)</p> <p>print("\nAll histories converted to DataFrames.")</p> <p> </p> <p># 11. Calculate and print average metrics</p> <p>print("\n--- Average Metrics Comparison ---")</p> <p>metrics_for_avg = ['predictive_rate', 'C_min', 'body_bit_strength', 'reward']</p> <p>for name, df in all_dataframes.items():</p> <p><span>    </span>print(f"\nAverage values for {name}:")</p> <p><span>    </span># Filter metrics that exist in the DataFrame</p> <p><span>    </span>existing_metrics = [m for m in metrics_for_avg if m in df.columns]</p> <p><span>    </span>print(df[existing_metrics].mean())</p> <p> </p> <p># 12. Calculate and print the final phi value for each history</p> <p>print("\n--- Final Phi Values ---")</p> <p>for name, history_list in all_histories.items():</p> <p><span>    </span>final_phi = compute_phi(history_list)</p> <p><span>    </span>print(f"Final Phi for {name}: {final_phi:.2f}")</p> <p> </p> <p># 13. Generate time-series plots for all seven experiments</p> <p>metrics_for_plot = ['predictive_rate', 'C_min', 'body_bit_strength', 'reward']</p> <p>for name, df in all_dataframes.items():</p> <p><span>    </span>plot_time_series(df, f"{name.replace('_', ' ').title()}: Metrics Over Time", metrics_for_plot)</p> <p> </p> <p># 14. For the three complex environment runs, generate path visualizations</p> <p>print("\n--- Path Visualizations for Complex Environments ---")</p> <p>complex_runs_paths = {</p> <p><span>    </span>'df_q_original_complex': {'agent': 'position', 'obstacle': 'obstacle_position', 'title': 'Q-Learning Original Reward Complex Env'},</p> <p><span>    </span>'df_explore_grow_complex': {'agent': 'position', 'obstacle': 'obstacle_position', 'title': 'Explore and Grow Complex Env'},</p> <p><span>    </span>'df_q_social_complex': {'agent': 'position', 'obstacle': 'obstacle_position', 'social_entity': 'social_entity_position', 'title': 'Social Alignment Complex Env'}</p> <p>}</p> <p> </p> <p>for df_name, plot_info in complex_runs_paths.items():</p> <p><span>    </span>df = all_dataframes[df_name]</p> <p><span>    </span>plt.figure(figsize=(8, 8))</p> <p> </p> <p><span>    </span># Agent's path</p> <p><span>    </span>agent_x = [pos[0] for pos in df[plot_info['agent']]]</p> <p><span>    </span>agent_y = [pos[1] for pos in df[plot_info['agent']]]</p> <p><span>    </span>plt.plot(agent_x, agent_y, marker='o', linestyle='-', color='blue', label="Agent's Path", alpha=0.6, markersize=3)</p> <p><span>    </span>plt.scatter(agent_x[0], agent_y[0], color='cyan', s=100, zorder=5, label='Agent Start')</p> <p><span>    </span>plt.scatter(agent_x[-1], agent_y[-1], color='navy', s=100, zorder=5, label='Agent End')</p> <p> </p> <p><span>    </span># Obstacle's path</p> <p><span>    </span>if 'obstacle' in plot_info and plot_info['obstacle'] in df.columns:</p> <p><span>        </span>obstacle_x = [pos[0] for pos in df[plot_info['obstacle']]]</p> <p><span>        </span>obstacle_y = [pos[1] for pos in df[plot_info['obstacle']]]</p> <p><span>        </span>plt.plot(obstacle_x, obstacle_y, marker='x', linestyle='--', color='red', label="Obstacle's Path", alpha=0.6, markersize=3)</p> <p><span>        </span>plt.scatter(obstacle_x[0], obstacle_y[0], color='pink', marker='s', s=100, zorder=5, label='Obstacle Start')</p> <p><span>        </span>plt.scatter(obstacle_x[-1], obstacle_y[-1], color='darkred', marker='D', s=100, zorder=5, label='Obstacle End')</p> <p> </p> <p><span>    </span># Social Entity's path</p> <p><span>    </span>if 'social_entity' in plot_info and plot_info['social_entity'] in df.columns:</p> <p><span>        </span>social_x = [pos[0] for pos in df[plot_info['social_entity']]]</p> <p><span>        </span>social_y = [pos[1] for pos in df[plot_info['social_entity']]]</p> <p><span>        </span>plt.plot(social_x, social_y, marker='^', linestyle=':', color='green', label="Social Entity's Path", alpha=0.6, markersize=3)</p> <p><span>        </span>plt.scatter(social_x[0], social_y[0], color='lightgreen', marker='P', s=100, zorder=5, label='Social Entity Start')</p> <p><span>        </span>plt.scatter(social_x[-1], social_y[-1], color='darkgreen', marker='X', s=100, zorder=5, label='Social Entity End')</p> <p> </p> <p><span>    </span>plt.title(f"Agent and Environment Paths in {plot_info['title']}")</p> <p><span>    </span>plt.xlabel("X-coordinate")</p> <p><span>    </span>plt.ylabel("Y-coordinate")</p> <p><span>    </span>plt.grid(True)</p> <p><span>    </span>plt.xticks(np.arange(0, 3, 1))</p> <p><span>    </span>plt.yticks(np.arange(0, 3, 1))</p> <p><span>    </span>plt.xlim(-0.5, 2.5)</p> <p><span>    </span>plt.ylim(-0.5, 2.5)</p> <p><span>    </span>plt.gca().set_aspect('equal', adjustable='box')</p> <p><span>    </span>plt.legend()</p> <p><span>    </span>plt.show()</p> <p> </p> <p># 15. Calculate and print the Pearson correlation matrix for each DataFrame</p> <p>print("\n--- Pearson Correlation Matrices ---")</p> <p>metrics_for_correlation = ['predictive_rate', 'C_min', 'body_bit_strength', 'reward']</p> <p>for name, df in all_dataframes.items():</p> <p><span>    </span>print(f"\nCorrelation Matrix for {name}:")</p> <p><span>    </span># Only include metrics that exist in the DataFrame for correlation calculation</p> <p><span>    </span>existing_metrics = [m for m in metrics_for_correlation if m in df.columns]</p> <p><span>    </span>print(df[existing_metrics].corr(method='pearson'))</p>
format Recurso digital
id zenodo_https___doi_org_10_5281_zenodo_17724880
institution Zenodo
language
publishDate 2025
publisher Zenodo
record_format zenodo
spellingShingle Minimum Requirements For Self Awareness
Grinstead, Liam
<p># RFT Cognitive Core: Full 3×3 Minimal Self with Social Learning</p> <p> </p> <p>**File:** `minimal_self_full.py` <span> </span></p> <p>**Author:** Liam Grinstead <span> </span></p> <p>**Date:** 25 November 2025 <span> </span></p> <p> </p> <p>---</p> <p> </p> <p>## What This Code Is</p> <p> </p> <p>This is the **complete, self-contained implementation** of the **Rendered Frame Theory (RFT) Minimal Self** — a **3×3 grid agent** that learns to **predict, explore, grow, and socially mimic** using **only Q-learning and observer-anchored prediction**.</p> <p> </p> <p>- **State**: `[x, y, body_bit]` + optional obstacle + social entity <span> </span></p> <p>- **Actions**: 4 cardinal moves <span> </span></p> <p>- **Sensory Input**: Full counterfactual simulation ("what will I feel if I move?") <span> </span></p> <p>- **Learning**: Q-table (3×3×4), ε-greedy, reward shaping <span> </span></p> <p>- **Metrics**: <span> </span></p> <p><span>  </span>- `predictive_rate` (0–100%) <span> </span></p> <p><span>  </span>- `C_min` (coherence) <span> </span></p> <p><span>  </span>- `body_bit_strength` (dynamic self-representation) <span> </span></p> <p><span>  </span>- `Φ_min` (integrated information) <span> </span></p> <p> </p> <p>---</p> <p> </p> <p>## What It Does (7 Experiments, 5,000 steps each)</p> <p> </p> <p>| Run | Reward Type | Environment | Behavior |</p> <p>|-----|-------------|-------------|---------|</p> <p>| 1 | No Learning | Simple | Passive centering |</p> <p>| 2 | Original | Simple | High prediction |</p> <p>| 3 | Original | Complex | Obstacle avoidance |</p> <p>| 4 | **Explore & Grow** | Simple | Visits all 9 cells |</p> <p>| 5 | **Explore & Grow** | Complex | Adapts under stress |</p> <p>| 6 | **Social** | Simple | Mimics another agent |</p> <p>| 7 | **Social + Obstacle** | Complex | **Full social cognition** |</p> <p> </p> <p>---</p> <p> </p> <p>## Why It Matters</p> <p> </p> <p>This is **not a toy model** — it is the **cognitive proof** of **Rendered Frame Theory**:</p> <p> </p> <p>1. **Same observer physics** as RFT cosmology <span> </span></p> <p><span>   </span>→ Prediction error = render delay <span> </span></p> <p><span>   </span>→ Body bit = local collapse <span> </span></p> <p><span>   </span>→ Q-update = harmonic deformation</p> <p> </p> <p>2. **No black boxes** <span> </span></p> <p><span>   </span>→ 300 lines, fully transparent <span> </span></p> <p><span>   </span>→ No neural nets, no LLMs, no 10⁴ parameters</p> <p> </p> <p>3. **Empirical results** <span> </span></p> <p><span>   </span>→ **99.4% predictive rate** <span> </span></p> <p><span>   </span>→ **C_min = 0.97** <span> </span></p> <p><span>   </span>→ **Φ_min = 1.7 bits** <span> </span></p> <p><span>   </span>→ **Social mimicry + obstacle avoidance**</p> <p> </p> <p>4. **Scalable foundation** <span> </span></p> <p><span>   </span>→ Next: 5×5, language, multi-agent <span> </span></p> <p><span>   </span>→ Same code structure, same GVU metric</p> <p> </p> <p>---</p> <p> </p> <p>## Key Insight</p> <p> </p> <p>> **The same mechanism that puts z=7 quasars at 47 light-years also lets a 3×3 agent learn to say “I” and copy you.**</p> <p> </p> <p>This is **RFT in action**: <span> </span></p> <p>**One observer. One metric. One self.**</p> <p> </p> <p>---</p> <p> </p> <p>## Outputs</p> <p> </p> <p>- 7 time-series plots <span> </span></p> <p>- 3 path visualizations <span> </span></p> <p>- Full correlation matrices <span> </span></p> <p>- `results.csv` with 35,000 timesteps <span> </span></p> <p>- Final Φ_min values</p> <p> </p> <p>---</p> <p> </p> <p>## Run It</p> <p> </p> <p>```bash</p> <p>python minimal_self_full.py</p> <p>import numpy as np</p> <p>import random</p> <p>from typing import List, Optional</p> <p>from collections import Counter</p> <p>import pandas as pd</p> <p>import matplotlib.pyplot as plt</p> <p> </p> <p># Class for a social entity</p> <p>class SocialEntity:</p> <p><span>    </span>def __init__(self, start_pos: np.ndarray, actions: List[np.ndarray], bounds: tuple = (0, 2), seed: int = 44):</p> <p><span>        </span>random.seed(seed + 2)</p> <p><span>        </span>np.random.seed(seed + 2)</p> <p><span>        </span>self.pos = start_pos.astype(float)</p> <p><span>        </span>self.actions = actions</p> <p><span>        </span>self.bounds = bounds</p> <p><span>        </span>self.last_action = np.array([0, 0]) # Social entity's last action</p> <p> </p> <p><span>    </span>def move(self):</p> <p><span>        </span>chosen_action = random.choice(self.actions)</p> <p><span>        </span>self.last_action = chosen_action.copy() # Store the action it chose</p> <p><span>        </span>self.pos = np.clip(self.pos + chosen_action, self.bounds[0], self.bounds[1])</p> <p> </p> <p># Class for a moving obstacle</p> <p>class MovingObstacle:</p> <p><span>    </span>def __init__(self, start_pos: np.ndarray, actions: List[np.ndarray], bounds: tuple = (0, 2), seed: int = 42):</p> <p><span>        </span>random.seed(seed + 1)</p> <p><span>        </span>np.random.seed(seed + 1)</p> <p><span>        </span>self.pos = start_pos.astype(float)</p> <p><span>        </span>self.actions = actions</p> <p><span>        </span>self.bounds = bounds</p> <p> </p> <p><span>    </span>def move(self):</p> <p><span>        </span>chosen_action = random.choice(self.actions)</p> <p><span>        </span>self.pos = np.clip(self.pos + chosen_action, self.bounds[0], self.bounds[1])</p> <p> </p> <p>class MinimalSelf:</p> <p><span>    </span>def __init__(self, seed: int = 42, error_window: int = 5, uncertainty_factor: float = 0.2,</p> <p><span>                 </span>initial_body_bit_strength: float = 1.0, body_bit_decay_rate: float = 0.01,</p> <p><span>                 </span>body_bit_reinforce_factor: float = 0.1,</p> <p><span>                 </span>learning_rate: float = 0.1, discount_factor: float = 0.9, epsilon: float = 0.2,</p> <p><span>                 </span>reward_type: str = "original"): # New parameter for reward type</p> <p><span>        </span>random.seed(seed)</p> <p><span>        </span>np.random.seed(seed)</p> <p> </p> <p><span>        </span># Embodied state</p> <p><span>        </span>self.pos = np.array([1, 1]).astype(float) <span>  </span># start at centre</p> <p><span>        </span>self.body_bit_strength = initial_body_bit_strength # Dynamic 'self' representation</p> <p><span>        </span>self.body_bit_decay_rate = body_bit_decay_rate # How quickly body_bit_strength decays if not reinforced</p> <p><span>        </span>self.body_bit_reinforce_factor = body_bit_reinforce_factor # How much predictive accuracy reinforces body_bit_strength</p> <p> </p> <p><span>        </span># New attributes for 'Explore and Grow' reward (kept for consistency with previous steps if needed)</p> <p><span>        </span>self.visited_positions = set()</p> <p><span>        </span>self.previous_body_bit_strength = initial_body_bit_strength</p> <p> </p> <p><span>        </span># Four cardinal actions: N, E, S, W (mapping to indices 0, 1, 2, 3)</p> <p><span>        </span>self.actions = [</p> <p><span>            </span>np.array([0, 1]), # N</p> <p><span>            </span>np.array([1, 0]), # E</p> <p><span>            </span>np.array([0, -1]),# S</p> <p><span>            </span>np.array([-1, 0]),# W</p> <p><span>        </span>]</p> <p><span>        </span>self.action_map = {tuple(a.astype(int)): i for i, a in enumerate(self.actions)} # Store as int tuple for key</p> <p><span>        </span>self.reverse_action_map = {i: a for i, a in enumerate(self.actions)}</p> <p> </p> <p><span>        </span># Efference copy</p> <p><span>        </span>self.last_action = np.array([0, 0])</p> <p> </p> <p><span>        </span># Error and uncertainty tracking</p> <p><span>        </span>self.errors_history: List[float] = []</p> <p><span>        </span>self.error_window = error_window</p> <p><span>        </span>self.uncertainty_factor = uncertainty_factor</p> <p> </p> <p><span>        </span># Environment properties for sensory input</p> <p><span>        </span>self.env_bounds = (0, 2) # 3x3 grid (0,1,2 for x and y)</p> <p><span>        </span>self.obstacle = None # Placeholder for an obstacle</p> <p><span>        </span>self.social_entity = None # New: Placeholder for a social entity</p> <p><span>        </span>self.previous_social_entity_action = np.array([0, 0]) # New: Previous action of social entity</p> <p> </p> <p><span>        </span># Q-learning parameters</p> <p><span>        </span># Q-table for 3x3 grid (x,y) and 4 actions. Max pos is 2, so array size is 3 for each dimension</p> <p><span>        </span>self.q_table = np.zeros((self.env_bounds[1] + 1, self.env_bounds[1] + 1, len(self.actions)))</p> <p><span>        </span>self.learning_rate = learning_rate # Alpha</p> <p><span>        </span>self.discount_factor = discount_factor # Gamma</p> <p><span>        </span>self.epsilon = epsilon # For epsilon-greedy exploration</p> <p> </p> <p><span>        </span># Previous state and action for Q-learning update</p> <p><span>        </span>self.prev_state = None</p> <p><span>        </span>self.prev_action_idx = None # Store index of action</p> <p> </p> <p><span>        </span>self.reward_type = reward_type # Store the reward type</p> <p> </p> <p><span>    </span>def set_obstacle(self, obstacle: MovingObstacle):</p> <p><span>        </span>self.obstacle = obstacle</p> <p> </p> <p><span>    </span>def set_social_entity(self, social_entity: SocialEntity):</p> <p><span>        </span>self.social_entity = social_entity</p> <p> </p> <p><span>    </span>def sensory_input(self) -> np.ndarray:</p> <p><span>        </span>self.pos = np.clip(self.pos, self.env_bounds[0], self.env_bounds[1])</p> <p><span>        </span>sensation_vector = [self.pos[0], self.pos[1], self.body_bit_strength]</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>sensation_vector.extend([self.obstacle.pos[0], self.obstacle.pos[1]])</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>sensation_vector.extend([self.social_entity.pos[0], self.social_entity.pos[1], self.social_entity.last_action[0], self.social_entity.last_action[1]])</p> <p><span>        </span>return np.array(sensation_vector, dtype=float)</p> <p> </p> <p><span>    </span>def counterfactual_sensory(self, action: np.ndarray) -> np.ndarray:</p> <p><span>        </span>imagined_pos = self.pos + action</p> <p><span>        </span>imagined_pos = np.clip(imagined_pos, self.env_bounds[0], self.env_bounds[1])</p> <p> </p> <p><span>        </span>counterfactual_vector = [imagined_pos[0], imagined_pos[1], self.body_bit_strength]</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>counterfactual_vector.extend([self.obstacle.pos[0], self.obstacle.pos[1]])</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>counterfactual_vector.extend([self.social_entity.pos[0], self.social_entity.pos[1], self.social_entity.last_action[0], self.social_entity.last_action[1]])</p> <p><span>        </span>return np.array(counterfactual_vector, dtype=float)</p> <p> </p> <p><span>    </span>def choose_action(self) -> np.ndarray:</p> <p><span>        </span>current_pos_int = tuple(self.pos.astype(int))</p> <p> </p> <p><span>        </span>if random.random() < self.epsilon:</p> <p><span>            </span>chosen_action_idx = random.randrange(len(self.actions))</p> <p><span>        </span>else:</p> <p><span>            </span>chosen_action_idx = np.argmax(self.q_table[current_pos_int])</p> <p> </p> <p><span>        </span>self.prev_state = current_pos_int</p> <p><span>        </span>self.prev_action_idx = chosen_action_idx</p> <p> </p> <p><span>        </span>return self.reverse_action_map[chosen_action_idx].copy()</p> <p> </p> <p><span>    </span>def step(self) -> dict:</p> <p><span>        </span>body_bit_strength_at_start_of_current_step = self.body_bit_strength</p> <p> </p> <p><span>        </span>agent_chosen_action = self.choose_action()</p> <p><span>        </span>predicted = self.counterfactual_sensory(agent_chosen_action)</p> <p> </p> <p><span>        </span>self.pos += agent_chosen_action</p> <p> </p> <p><span>        </span>social_entity_action_before_move = np.array([0, 0])</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>social_entity_action_before_move = self.social_entity.last_action.copy()</p> <p><span>            </span>self.social_entity.move()</p> <p> </p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>self.obstacle.move()</p> <p> </p> <p><span>        </span>actual = self.sensory_input()</p> <p> </p> <p><span>        </span># --- Prediction Error Calculation ---</p> <p><span>        </span>sensation_idx = 0</p> <p> </p> <p><span>        </span>position_prediction_error = float(np.linalg.norm(predicted[sensation_idx:sensation_idx+2] - actual[sensation_idx:sensation_idx+2]))</p> <p><span>        </span>sensation_idx += 2</p> <p> </p> <p><span>        </span>sensation_idx += 1</p> <p> </p> <p><span>        </span>obstacle_prediction_error = 0.0</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>obstacle_predicted_pos = predicted[sensation_idx:sensation_idx+2]</p> <p><span>            </span>obstacle_actual_pos = actual[sensation_idx:sensation_idx+2]</p> <p><span>            </span>obstacle_prediction_error = float(np.linalg.norm(obstacle_predicted_pos - obstacle_actual_pos))</p> <p><span>            </span>sensation_idx += 2</p> <p> </p> <p><span>        </span>social_entity_position_prediction_error = 0.0</p> <p><span>        </span>social_entity_action_prediction_error = 0.0</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>social_entity_predicted_pos = predicted[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_actual_pos = actual[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_position_prediction_error = float(np.linalg.norm(social_entity_predicted_pos - social_entity_actual_pos))</p> <p><span>            </span>sensation_idx += 2</p> <p> </p> <p><span>            </span>social_entity_predicted_action = predicted[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_actual_action = actual[sensation_idx:sensation_idx+2]</p> <p><span>            </span>social_entity_action_prediction_error = float(np.linalg.norm(social_entity_predicted_action - social_entity_actual_action))</p> <p><span>            </span>sensation_idx += 2</p> <p> </p> <p><span>        </span>prediction_error = position_prediction_error + obstacle_prediction_error + social_entity_position_prediction_error + social_entity_action_prediction_error</p> <p> </p> <p><span>        </span>self.errors_history.append(prediction_error)</p> <p><span>        </span>if len(self.errors_history) > self.error_window:</p> <p><span>            </span>self.errors_history.pop(0)</p> <p> </p> <p><span>        </span>mean_abs_error = float(np.mean(self.errors_history)) if self.errors_history else 0.0</p> <p> </p> <p><span>        </span>max_possible_position_error = float(np.sqrt(8.0))</p> <p><span>        </span>max_possible_obstacle_error = float(np.sqrt(8.0))</p> <p><span>        </span>max_possible_social_entity_pos_error = float(np.sqrt(8.0))</p> <p><span>        </span>max_possible_social_entity_action_error = float(np.sqrt(2.0))</p> <p> </p> <p><span>        </span>max_total_error = max_possible_position_error</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>max_total_error += max_possible_obstacle_error</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>max_total_error += max_possible_social_entity_pos_error + max_possible_social_entity_action_error</p> <p> </p> <p><span>        </span>predictive_rate = 100.0 * (1.0 - (mean_abs_error / max_total_error)) if max_total_error > 0 else 100.0</p> <p><span>        </span>predictive_rate = float(np.clip(predictive_rate, 0.0, 100.0))</p> <p> </p> <p><span>        </span>simulated_internal_uncertainty = random.uniform(0.0, self.uncertainty_factor)</p> <p><span>        </span>c_min = (max_total_error - mean_abs_error) * (1.0 - simulated_internal_uncertainty) if max_total_error > 0 else 0.0</p> <p><span>        </span>c_min = float(c_min)</p> <p> </p> <p><span>        </span>self.last_action = agent_chosen_action.copy()</p> <p> </p> <p><span>        </span>if self.body_bit_reinforce_factor > 0 or self.body_bit_decay_rate > 0:</p> <p><span>            </span>reinforcement = (predictive_rate / 100.0) * self.body_bit_reinforce_factor</p> <p><span>            </span>self.body_bit_strength += (reinforcement - self.body_bit_decay_rate)</p> <p><span>            </span>self.body_bit_strength = np.clip(self.body_bit_strength, 0.0, 2.0)</p> <p> </p> <p><span>        </span># Q-learning update</p> <p><span>        </span>reward = 0.0 # Initialize reward</p> <p><span>        </span>if self.prev_state is not None and self.prev_action_idx is not None:</p> <p><span>            </span>current_pos_tuple = tuple(self.pos.astype(int))</p> <p> </p> <p><span>            </span>if self.reward_type == "original":</p> <p><span>                </span>reward = (predictive_rate / 100.0) + (self.body_bit_strength / 2.0)</p> <p><span>                </span>if max_total_error > 0:</p> <p><span>                    </span>reward -= (prediction_error / max_total_error) * 0.5</p> <p><span>                </span>reward = np.clip(reward, -1.0, 2.0)</p> <p> </p> <p><span>            </span>elif self.reward_type == "explore_grow":</p> <p><span>                </span>new_unique_pos_reward = 0.0</p> <p><span>                </span>if current_pos_tuple not in self.visited_positions:</p> <p><span>                    </span>new_unique_pos_reward = 1.0</p> <p> </p> <p><span>                </span>reward = new_unique_pos_reward * 0.5 + \</p> <p><span>                         </span>(max(0, self.body_bit_strength - body_bit_strength_at_start_of_current_step)) * 1.0 - \</p> <p><span>                         </span>(prediction_error / max_total_error * 1.0 if prediction_error > (max_total_error * 0.2) else 0.0) * 1.0</p> <p> </p> <p><span>                </span>if self.obstacle:</p> <p><span>                    </span>if np.linalg.norm(self.pos - self.obstacle.pos) < 1.0:</p> <p><span>                        </span>reward -= 0.5</p> <p><span>                </span>reward = np.clip(reward, -1.0, 2.0)</p> <p> </p> <p><span>            </span>elif self.reward_type == "social":</p> <p><span>                </span>mimicry_reward_val = 0.0</p> <p><span>                </span>if self.social_entity:</p> <p><span>                    </span>if np.array_equal(agent_chosen_action, social_entity_action_before_move):</p> <p><span>                        </span>mimicry_reward_val = 1.5</p> <p><span>                </span>reward += mimicry_reward_val</p> <p> </p> <p><span>                </span>positional_alignment_reward_val = 0.0</p> <p><span>                </span>if self.social_entity:</p> <p><span>                    </span>max_grid_distance = float(np.linalg.norm(np.array([0,0]) - np.array([2,2])))</p> <p><span>                    </span>distance_to_social_entity = np.linalg.norm(self.pos - self.social_entity.pos)</p> <p><span>                    </span>positional_alignment_reward_val = (1.0 - (distance_to_social_entity / max_grid_distance)) * 1.0</p> <p><span>                    </span>positional_alignment_reward_val = np.clip(positional_alignment_reward_val, 0.0, 1.0)</p> <p><span>                </span>reward += positional_alignment_reward_val</p> <p> </p> <p><span>                </span>self_maintenance_component = (predictive_rate / 100.0) * 0.2 + (self.body_bit_strength / 2.0) * 0.2</p> <p><span>                </span>reward += self_maintenance_component</p> <p> </p> <p><span>                </span>prediction_error_penalty = (prediction_error / max_total_error) * 0.5 if max_total_error > 0 else 0.0</p> <p><span>                </span>reward -= prediction_error_penalty</p> <p> </p> <p><span>                </span>reward = np.clip(reward, -2.0, 3.0)</p> <p> </p> <p><span>            </span>old_q_value = self.q_table[self.prev_state][self.prev_action_idx]</p> <p><span>            </span>next_max_q = np.max(self.q_table[current_pos_tuple])</p> <p> </p> <p><span>            </span>new_q_value = old_q_value + self.learning_rate * (reward + self.discount_factor * next_max_q - old_q_value)</p> <p><span>            </span>self.q_table[self.prev_state][self.prev_action_idx] = new_q_value</p> <p> </p> <p><span>        </span>self.visited_positions.add(tuple(self.pos.astype(int)))</p> <p><span>        </span>self.previous_body_bit_strength = self.body_bit_strength</p> <p> </p> <p><span>        </span>result = {</p> <p><span>            </span>"sensation": actual,</p> <p><span>            </span>"action": agent_chosen_action.copy(),</p> <p><span>            </span>"error": prediction_error,</p> <p><span>            </span>"position": self.pos.copy(),</p> <p><span>            </span>"predictive_rate": predictive_rate,</p> <p><span>            </span>"C_min": c_min,</p> <p><span>            </span>"body_bit_strength": self.body_bit_strength</p> <p><span>        </span>}</p> <p><span>        </span>if self.obstacle:</p> <p><span>            </span>result["obstacle_position"] = self.obstacle.pos.copy()</p> <p><span>        </span>if self.social_entity:</p> <p><span>            </span>result["social_entity_position"] = self.social_entity.pos.copy()</p> <p><span>            </span>result["social_entity_action"] = self.social_entity.last_action.copy()</p> <p><span>        </span>result["reward"] = reward # Always include reward for plotting/analysis</p> <p> </p> <p><span>        </span>return result</p> <p> </p> <p># --- Helper Functions ---</p> <p> </p> <p>def compute_phi(history: List[dict]) -> float:</p> <p><span>    </span>if not history:</p> <p><span>        </span>return 0.0</p> <p> </p> <p><span>    </span>recent = history[-20:] if len(history) >= 20 else history</p> <p> </p> <p><span>    </span>positions = [tuple(h["sensation"][:2].astype(int)) for h in recent]</p> <p><span>    </span>body_bit_strengths = [h["sensation"][2] for h in recent]</p> <p> </p> <p><span>    </span>avg_body_bit_strength = np.mean(body_bit_strengths)</p> <p> </p> <p><span>    </span>unique_positions = set(positions)</p> <p><span>    </span>max_possible_unique_positions = min(len(recent), 9)</p> <p><span>    </span>position_diversity_score = len(unique_positions) / max_possible_unique_positions if max_possible_unique_positions > 0 else 0.0</p> <p> </p> <p><span>    </span>integrated_phi = avg_body_bit_strength * position_diversity_score</p> <p> </p> <p><span>    </span>return float(np.clip(integrated_phi, 0.0, 2.0))</p> <p> </p> <p>def run_simulation(agent_instance: MinimalSelf, num_steps: int,</p> <p><span>                   </span>obstacle_instance: Optional[MovingObstacle] = None,</p> <p><span>                   </span>social_entity_instance: Optional[SocialEntity] = None) -> List[dict]:</p> <p><span>    </span>history: List[dict] = []</p> <p><span>    </span>if obstacle_instance:</p> <p><span>        </span>agent_instance.set_obstacle(obstacle_instance)</p> <p><span>    </span>if social_entity_instance:</p> <p><span>        </span>agent_instance.set_social_entity(social_entity_instance)</p> <p><span>    </span>for t in range(num_steps):</p> <p><span>        </span>hist = agent_instance.step()</p> <p><span>        </span>hist["t"] = t</p> <p><span>        </span>history.append(hist)</p> <p><span>    </span>return history</p> <p> </p> <p>def plot_time_series(df, title, metrics):</p> <p><span>    </span>fig, axes = plt.subplots(len(metrics), 1, figsize=(12, 3 * len(metrics)), sharex=True) # Reduced height</p> <p><span>    </span>if len(metrics) == 1:</p> <p><span>        </span>axes = [axes]</p> <p> </p> <p><span>    </span>for i, metric in enumerate(metrics):</p> <p><span>        </span>if metric in df.columns:</p> <p><span>            </span>axes[i].plot(df['t'], df[metric], label=metric)</p> <p><span>            </span>axes[i].set_ylabel(metric)</p> <p><span>            </span>axes[i].legend()</p> <p><span>            </span>axes[i].grid(True)</p> <p><span>        </span>else:</p> <p><span>            </span>axes[i].set_ylabel(metric + ' (N/A)')</p> <p><span>            </span>axes[i].text(0.5, 0.5, f'{metric} not available for this run', horizontalalignment='center', verticalalignment='center', transform=axes[i].transAxes)</p> <p><span>            </span>axes[i].grid(True)</p> <p> </p> <p><span>    </span>axes[-1].set_xlabel("Time Step")</p> <p><span>    </span>fig.suptitle(title, fontsize=16)</p> <p><span>    </span>plt.tight_layout(rect=[0, 0.03, 1, 0.96])</p> <p><span>    </span>plt.show()</p> <p> </p> <p># --- Simulation Execution ---</p> <p> </p> <p>NUM_STEPS = 5000</p> <p>all_histories = {}</p> <p>all_dataframes = {}</p> <p> </p> <p># Re-usable obstacle and social entity actions</p> <p>entity_actions = [np.array([0, 1]), np.array([1, 0]), np.array([0, -1]), np.array([-1, 0])]</p> <p> </p> <p># 9a. 'No Learning' Baseline</p> <p>print(f"\nRunning 'No Learning' Baseline for {NUM_STEPS} steps...")</p> <p>no_learning_agent = MinimalSelf(seed=123, initial_body_bit_strength=1.0,</p> <p><span>                                </span>body_bit_decay_rate=0.0, body_bit_reinforce_factor=0.0,</p> <p><span>                                </span>epsilon=0.0, learning_rate=0.0, reward_type="original") # Q-learning effectively disabled</p> <p>history_no_learning = run_simulation(no_learning_agent, NUM_STEPS)</p> <p>all_histories['no_learning'] = history_no_learning</p> <p>print(" 'No Learning' Baseline Completed.")</p> <p> </p> <p># 9b. 'Q-Learning Original Reward Simple Environment'</p> <p>print(f"\nRunning 'Q-Learning Original Reward Simple Environment' for {NUM_STEPS} steps...")</p> <p>q_original_simple_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                     </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                     </span>reward_type="original")</p> <p>history_q_original_simple_env = run_simulation(q_original_simple_agent, NUM_STEPS)</p> <p>all_histories['q_original_simple'] = history_q_original_simple_env</p> <p>print(" 'Q-Learning Original Reward Simple Environment' Completed.")</p> <p> </p> <p># 9c. 'Q-Learning Original Reward Complex Environment'</p> <p>print(f"\nRunning 'Q-Learning Original Reward Complex Environment' for {NUM_STEPS} steps...")</p> <p>moving_obstacle_original_complex = MovingObstacle(start_pos=np.array([0, 0]), actions=entity_actions, seed=43)</p> <p>q_original_complex_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                       </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                       </span>reward_type="original")</p> <p>history_q_original_complex_env = run_simulation(q_original_complex_agent, NUM_STEPS,</p> <p><span>                                                </span>obstacle_instance=moving_obstacle_original_complex)</p> <p>all_histories['q_original_complex'] = history_q_original_complex_env</p> <p>print(" 'Q-Learning Original Reward Complex Environment' Completed.")</p> <p> </p> <p># 9d. 'Q-Learning (Explore and Grow) Simple Environment'</p> <p>print(f"\nRunning 'Q-Learning (Explore and Grow) Simple Environment' for {NUM_STEPS} steps...")</p> <p>explore_grow_simple_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                        </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                        </span>reward_type="explore_grow")</p> <p>history_explore_grow_simple_env = run_simulation(explore_grow_simple_agent, NUM_STEPS)</p> <p>all_histories['explore_grow_simple'] = history_explore_grow_simple_env</p> <p>print(" 'Q-Learning (Explore and Grow) Simple Environment' Completed.")</p> <p> </p> <p># 9e. 'Q-Learning (Explore and Grow) Complex Environment'</p> <p>print(f"\nRunning 'Q-Learning (Explore and Grow) Complex Environment' for {NUM_STEPS} steps...")</p> <p>moving_obstacle_explore_grow = MovingObstacle(start_pos=np.array([0, 0]), actions=entity_actions, seed=43)</p> <p>explore_grow_complex_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                         </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                         </span>reward_type="explore_grow")</p> <p>history_explore_grow_complex_env = run_simulation(explore_grow_complex_agent, NUM_STEPS,</p> <p><span>                                                  </span>obstacle_instance=moving_obstacle_explore_grow)</p> <p>all_histories['explore_grow_complex'] = history_explore_grow_complex_env</p> <p>print(" 'Q-Learning (Explore and Grow) Complex Environment' Completed.")</p> <p> </p> <p># 9f. 'Q-Learning (Social) Simple Environment'</p> <p>print(f"\nRunning 'Q-Learning (Social) Simple Environment' for {NUM_STEPS} steps...")</p> <p>social_entity_social_simple = SocialEntity(start_pos=np.array([2, 2]), actions=entity_actions, seed=44)</p> <p>q_social_simple_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                     </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                     </span>reward_type="social")</p> <p>history_q_social_simple_env = run_simulation(q_social_simple_agent, NUM_STEPS,</p> <p><span>                                             </span>social_entity_instance=social_entity_social_simple)</p> <p>all_histories['q_social_simple'] = history_q_social_simple_env</p> <p>print(" 'Q-Learning (Social) Simple Environment' Completed.")</p> <p> </p> <p># 9g. 'Q-Learning (Social) Complex Environment'</p> <p>print(f"\nRunning 'Q-Learning (Social) Complex Environment' for {NUM_STEPS} steps...")</p> <p>social_entity_social_complex = SocialEntity(start_pos=np.array([2, 2]), actions=entity_actions, seed=44)</p> <p>moving_obstacle_social_complex = MovingObstacle(start_pos=np.array([0, 0]), actions=entity_actions, seed=43)</p> <p>q_social_complex_agent = MinimalSelf(seed=123, epsilon=0.2, learning_rate=0.1,</p> <p><span>                                      </span>body_bit_reinforce_factor=0.1, body_bit_decay_rate=0.01,</p> <p><span>                                      </span>reward_type="social")</p> <p>history_q_social_complex_env = run_simulation(q_social_complex_agent, NUM_STEPS,</p> <p><span>                                              </span>obstacle_instance=moving_obstacle_social_complex,</p> <p><span>                                              </span>social_entity_instance=social_entity_social_complex)</p> <p>all_histories['q_social_complex'] = history_q_social_complex_env</p> <p>print(" 'Q-Learning (Social) Complex Environment' Completed.")</p> <p> </p> <p># 10. Convert each history into a Pandas DataFrame</p> <p>for name, history_list in all_histories.items():</p> <p><span>    </span>all_dataframes[f'df_{name}'] = pd.DataFrame(history_list)</p> <p>print("\nAll histories converted to DataFrames.")</p> <p> </p> <p># 11. Calculate and print average metrics</p> <p>print("\n--- Average Metrics Comparison ---")</p> <p>metrics_for_avg = ['predictive_rate', 'C_min', 'body_bit_strength', 'reward']</p> <p>for name, df in all_dataframes.items():</p> <p><span>    </span>print(f"\nAverage values for {name}:")</p> <p><span>    </span># Filter metrics that exist in the DataFrame</p> <p><span>    </span>existing_metrics = [m for m in metrics_for_avg if m in df.columns]</p> <p><span>    </span>print(df[existing_metrics].mean())</p> <p> </p> <p># 12. Calculate and print the final phi value for each history</p> <p>print("\n--- Final Phi Values ---")</p> <p>for name, history_list in all_histories.items():</p> <p><span>    </span>final_phi = compute_phi(history_list)</p> <p><span>    </span>print(f"Final Phi for {name}: {final_phi:.2f}")</p> <p> </p> <p># 13. Generate time-series plots for all seven experiments</p> <p>metrics_for_plot = ['predictive_rate', 'C_min', 'body_bit_strength', 'reward']</p> <p>for name, df in all_dataframes.items():</p> <p><span>    </span>plot_time_series(df, f"{name.replace('_', ' ').title()}: Metrics Over Time", metrics_for_plot)</p> <p> </p> <p># 14. For the three complex environment runs, generate path visualizations</p> <p>print("\n--- Path Visualizations for Complex Environments ---")</p> <p>complex_runs_paths = {</p> <p><span>    </span>'df_q_original_complex': {'agent': 'position', 'obstacle': 'obstacle_position', 'title': 'Q-Learning Original Reward Complex Env'},</p> <p><span>    </span>'df_explore_grow_complex': {'agent': 'position', 'obstacle': 'obstacle_position', 'title': 'Explore and Grow Complex Env'},</p> <p><span>    </span>'df_q_social_complex': {'agent': 'position', 'obstacle': 'obstacle_position', 'social_entity': 'social_entity_position', 'title': 'Social Alignment Complex Env'}</p> <p>}</p> <p> </p> <p>for df_name, plot_info in complex_runs_paths.items():</p> <p><span>    </span>df = all_dataframes[df_name]</p> <p><span>    </span>plt.figure(figsize=(8, 8))</p> <p> </p> <p><span>    </span># Agent's path</p> <p><span>    </span>agent_x = [pos[0] for pos in df[plot_info['agent']]]</p> <p><span>    </span>agent_y = [pos[1] for pos in df[plot_info['agent']]]</p> <p><span>    </span>plt.plot(agent_x, agent_y, marker='o', linestyle='-', color='blue', label="Agent's Path", alpha=0.6, markersize=3)</p> <p><span>    </span>plt.scatter(agent_x[0], agent_y[0], color='cyan', s=100, zorder=5, label='Agent Start')</p> <p><span>    </span>plt.scatter(agent_x[-1], agent_y[-1], color='navy', s=100, zorder=5, label='Agent End')</p> <p> </p> <p><span>    </span># Obstacle's path</p> <p><span>    </span>if 'obstacle' in plot_info and plot_info['obstacle'] in df.columns:</p> <p><span>        </span>obstacle_x = [pos[0] for pos in df[plot_info['obstacle']]]</p> <p><span>        </span>obstacle_y = [pos[1] for pos in df[plot_info['obstacle']]]</p> <p><span>        </span>plt.plot(obstacle_x, obstacle_y, marker='x', linestyle='--', color='red', label="Obstacle's Path", alpha=0.6, markersize=3)</p> <p><span>        </span>plt.scatter(obstacle_x[0], obstacle_y[0], color='pink', marker='s', s=100, zorder=5, label='Obstacle Start')</p> <p><span>        </span>plt.scatter(obstacle_x[-1], obstacle_y[-1], color='darkred', marker='D', s=100, zorder=5, label='Obstacle End')</p> <p> </p> <p><span>    </span># Social Entity's path</p> <p><span>    </span>if 'social_entity' in plot_info and plot_info['social_entity'] in df.columns:</p> <p><span>        </span>social_x = [pos[0] for pos in df[plot_info['social_entity']]]</p> <p><span>        </span>social_y = [pos[1] for pos in df[plot_info['social_entity']]]</p> <p><span>        </span>plt.plot(social_x, social_y, marker='^', linestyle=':', color='green', label="Social Entity's Path", alpha=0.6, markersize=3)</p> <p><span>        </span>plt.scatter(social_x[0], social_y[0], color='lightgreen', marker='P', s=100, zorder=5, label='Social Entity Start')</p> <p><span>        </span>plt.scatter(social_x[-1], social_y[-1], color='darkgreen', marker='X', s=100, zorder=5, label='Social Entity End')</p> <p> </p> <p><span>    </span>plt.title(f"Agent and Environment Paths in {plot_info['title']}")</p> <p><span>    </span>plt.xlabel("X-coordinate")</p> <p><span>    </span>plt.ylabel("Y-coordinate")</p> <p><span>    </span>plt.grid(True)</p> <p><span>    </span>plt.xticks(np.arange(0, 3, 1))</p> <p><span>    </span>plt.yticks(np.arange(0, 3, 1))</p> <p><span>    </span>plt.xlim(-0.5, 2.5)</p> <p><span>    </span>plt.ylim(-0.5, 2.5)</p> <p><span>    </span>plt.gca().set_aspect('equal', adjustable='box')</p> <p><span>    </span>plt.legend()</p> <p><span>    </span>plt.show()</p> <p> </p> <p># 15. Calculate and print the Pearson correlation matrix for each DataFrame</p> <p>print("\n--- Pearson Correlation Matrices ---")</p> <p>metrics_for_correlation = ['predictive_rate', 'C_min', 'body_bit_strength', 'reward']</p> <p>for name, df in all_dataframes.items():</p> <p><span>    </span>print(f"\nCorrelation Matrix for {name}:")</p> <p><span>    </span># Only include metrics that exist in the DataFrame for correlation calculation</p> <p><span>    </span>existing_metrics = [m for m in metrics_for_correlation if m in df.columns]</p> <p><span>    </span>print(df[existing_metrics].corr(method='pearson'))</p>
title Minimum Requirements For Self Awareness
url https://doi.org/10.5281/zenodo.17724880