Этот коммит содержится в:
Daniel Ari Friedman 2025-02-07 13:49:44 -08:00
родитель 621b613624
Коммит ca645167d2
31 изменённых файлов: 2343 добавлений и 72 удалений

2
.obsidian/graph.json поставляемый
Просмотреть файл

@ -17,6 +17,6 @@
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 2.2318821973563394,
"scale": 0.10124266675309304,
"close": false
}

73
.obsidian/workspace.json поставляемый
Просмотреть файл

@ -6,6 +6,7 @@
{
"id": "dea337cf76935a08",
"type": "tabs",
"dimension": 37.73006134969325,
"children": [
{
"id": "0ba8a1b9b9dd949a",
@ -26,19 +27,16 @@
{
"id": "99b33ec97f002ea4",
"type": "tabs",
"dimension": 62.26993865030674,
"children": [
{
"id": "283b4112120e60a0",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Things/BioFirm/BioFirm_README.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "BioFirm_README"
"type": "graph",
"state": {},
"icon": "lucide-git-fork",
"title": "Graph view"
}
}
]
@ -114,7 +112,6 @@
"state": {
"type": "backlink",
"state": {
"file": "Things/Continuous_Generic/Continuous_Generic_README.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
@ -124,7 +121,7 @@
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks for Continuous_Generic_README"
"title": "Backlinks"
}
},
{
@ -133,12 +130,11 @@
"state": {
"type": "outgoing-link",
"state": {
"file": "Things/Continuous_Generic/Continuous_Generic_README.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links from Continuous_Generic_README"
"title": "Outgoing links"
}
},
{
@ -162,13 +158,12 @@
"state": {
"type": "outline",
"state": {
"file": "Things/Continuous_Generic/Continuous_Generic_README.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of Continuous_Generic_README"
"title": "Outline"
}
}
]
@ -188,32 +183,34 @@
"command-palette:Open command palette": false
}
},
"active": "0ba8a1b9b9dd949a",
"active": "283b4112120e60a0",
"lastOpenFiles": [
"ant_colony/agents/nestmate.py",
"ant_colony/agents",
"ant_colony",
"Things/Ant_Colony/ant_colony/__pycache__/main.cpython-310.pyc",
"Things/Ant_Colony/ant_colony/utils/__pycache__/data_collection.cpython-310.pyc",
"Things/Ant_Colony/ant_colony/utils/__pycache__/__init__.cpython-310.pyc",
"Things/Ant_Colony/ant_colony/utils/__pycache__",
"Things/Ant_Colony/ant_colony/environment/__pycache__/world.cpython-310.pyc",
"Things/Ant_Colony/ant_colony/utils/__init__.py",
"Things/Ant_Colony/ant_colony/utils/data_collection.py",
"Things/Ant_Colony/ant_colony/utils",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/top_level.txt",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/entry_points.txt",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/WHEEL",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/REQUESTED",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/RECORD",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/METADATA",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/LICENSE.txt",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/AUTHORS.txt",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info/INSTALLER",
"Things/Path_Network/venv/lib/python3.10/site-packages/pip-25.0.dist-info",
"Things/Path_Network/venv/bin/Activate.ps1",
"Things/Path_Network/output/20250207_134114/agent_height_variations.png",
"Things/Path_Network/output/20250207_134114/water_level_distribution.png",
"Things/Path_Network/output/20250207_134114/final_state.png",
"Things/Path_Network/output/20250207_134114/initial_state.png",
"Things/Path_Network/output/20250207_134014/agent_height_variations.png",
"Things/Path_Network/output/20250207_134014/water_level_distribution.png",
"Things/Path_Network/output/20250207_134014/final_state.png",
"Things/Path_Network/output/20250207_134014/initial_state.png",
"Things/Path_Network/README.md",
"Things/Path_Network/Path_Network_README.md",
"Things/BioFirm/BioFirm_README.md",
"Things/Continuous_Generic/Continuous_Generic_README.md",
"Output/tests/visualization/detailed_summary.png",
"Output/tests/visualization/summary.png",
"Output/tests/visualization/action_phase_space.png",
"Output/tests/visualization/action_evolution.png",
"Output/tests/visualization/free_energy_landscape.png",
"Output/tests/visualization/belief_animation.gif",
"Things/BioFirm/BioFirm_README.md",
"knowledge_base/cognitive/continuous_time_active_inference.md",
"Output/tests/visualization/generalized_coordinates.png",
"Output/tests/visualization/energy_ratio.png",
"Output/tests/visualization/energy_components.png",
"Output/tests/visualization/phase_space_energy.png",
"Things/Continuous_Generic/Continuous_Generic_README.md",
"Things/Generic_POMDP/Generic_POMDP_README.md",
"src/models/active_inference/docs/biofirm_schema.md",
"knowledge_base/agents/GenericPOMDP/matrices/A_matrix.md",
@ -234,8 +231,6 @@
"knowledge_base/mathematics/expected_free_energy.md",
"knowledge_base/mathematics/active_inference_pomdp.md",
"knowledge_base/mathematics/compute_efe.md",
"knowledge_base/mathematics/belief_updating.md",
"knowledge_base/cognitive/perceptual_inference.md",
"knowledge_base/cognitive/metacognition.md"
"knowledge_base/mathematics/belief_updating.md"
]
}

Просмотреть файл

@ -34,6 +34,9 @@ class Nestmate:
"""Initialize the agent."""
self.config = config
# Initialize beliefs first
self.beliefs = Belief()
# Physical state
self.position = None
self.orientation = 0.0
@ -47,6 +50,13 @@ class Nestmate:
self.current_task = TaskType.EXPLORATION
self.task_time = 0.0
# Memory state
self.memory = {
'temporal': [], # List of temporal memories (past states/actions)
'spatial': {}, # Dict of spatial memories (locations)
'social': {} # Dict of social memories (interactions)
}
# Sensory state
self.observations = {
'pheromones': {},
@ -56,7 +66,6 @@ class Nestmate:
}
# Internal model
self.beliefs = Belief()
self.preferences = {task: 1.0 for task in TaskType}
# Learning parameters
@ -77,6 +86,31 @@ class Nestmate:
# Select and execute actions
self._select_actions()
# Update memory with current state
self._update_memory()
def _update_memory(self) -> None:
"""Update agent's memory with current state."""
# Add current state to temporal memory
current_state = {
'state': self.current_task,
'position': self.position,
'energy': self.energy,
'time': self.task_time
}
self.memory['temporal'].append(current_state)
# Limit memory size to prevent unbounded growth
max_memory = 100
if len(self.memory['temporal']) > max_memory:
self.memory['temporal'] = self.memory['temporal'][-max_memory:]
# Update spatial memory if we found something interesting
if self.beliefs.food_location:
self.memory['spatial']['food'] = self.beliefs.food_location
# Could add more memory updates here (social interactions, etc.)
def _update_physical_state(self, dt: float) -> None:
"""Update physical state of the agent."""
# Update position based on velocity and orientation

Просмотреть файл

@ -10,8 +10,8 @@ import numpy as np
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
import networkx as nx
from agents.nestmate import Nestmate, TaskType
from environment.world import World, Position, Resource
from ant_colony.agents.nestmate import Nestmate, TaskType
from ant_colony.environment.world import World, Position, Resource
@dataclass
class ColonyStats:
@ -46,6 +46,10 @@ class Colony:
'building_materials': 0.0
}
# Initialize task needs and allocation
self.task_needs = {task: 0.0 for task in TaskType}
self.task_allocation = {task: set() for task in TaskType}
# Initialize agents
self.agents: List[Nestmate] = []
self._initialize_agents()
@ -91,6 +95,8 @@ class Colony:
agent = Nestmate(agent_config)
agent.position = pos
# Initialize beliefs after creating agent
agent.beliefs.nest_location = self.nest_position
self.agents.append(agent)
@ -181,11 +187,19 @@ class Colony:
'energy_efficiency': self._compute_energy_efficiency()
}
# Compute coordination metrics
coordination = {
'task_specialization': self._calculate_specialization(),
'social_network_density': nx.density(self.interaction_network),
'task_balance': self._compute_task_balance(task_dist)
}
return ColonyStats(
population=len(self.agents),
task_distribution=task_dist,
resource_levels=self.resources.copy(),
efficiency_metrics=efficiency
efficiency_metrics=efficiency,
coordination_metrics=coordination
)
def _compute_food_efficiency(self) -> float:
@ -459,4 +473,17 @@ class Colony:
break
if agent not in current_defenders:
agent.current_task = TaskType.DEFENSE
current_defenders.add(agent)
current_defenders.add(agent)
def _compute_task_balance(self, task_dist: Dict[TaskType, int]) -> float:
"""Compute how well-balanced the task distribution is."""
if not self.agents:
return 0.0
# Ideal distribution would be uniform across tasks
ideal_per_task = len(self.agents) / len(TaskType)
deviations = [abs(count - ideal_per_task) for count in task_dist.values()]
max_deviation = len(self.agents) # Worst case: all agents on one task
# Return normalized score (1.0 = perfect balance, 0.0 = worst balance)
return 1.0 - sum(deviations) / (2 * max_deviation)

Просмотреть файл

@ -78,6 +78,7 @@ class World:
for _ in range(self.config['food_sources']['count']):
pos = self._random_position()
amount = np.random.uniform(*self.config['food_sources']['value_range'])
size = np.random.uniform(*self.config['food_sources']['size_range'])
resource = Resource(
position=pos,
@ -137,6 +138,14 @@ class World:
resource.max_amount
)
def get_state(self) -> dict:
"""Get the current state of the world."""
return {
'pheromones': self.pheromones,
'resources': [resource for resources in self.resources.values() for resource in resources],
'terrain': self.terrain
}
def get_local_state(self, position: Position, radius: float) -> dict:
"""Get the local state around a position within given radius."""
x, y = position.x, position.y

Просмотреть файл

@ -6,6 +6,8 @@ import yaml
import numpy as np
from pathlib import Path
from typing import Optional
import matplotlib
matplotlib.use('Agg') # Use non-GUI backend
import matplotlib.pyplot as plt
from ant_colony.environment import World
@ -27,8 +29,18 @@ class Simulation:
# Initialize components
self.environment = World(self.config['environment'])
self.colony = Colony(self.config['colony'], self.environment)
self.visualizer = ColonyVisualizer(self.config['visualization'])
self.data_collector = DataCollector(self.config['data_collection'])
# Initialize visualization if enabled
if self.config.get('visualization', {}).get('enabled', True):
self.visualizer = ColonyVisualizer(self.config)
else:
self.visualizer = None
# Initialize data collection if enabled
if self.config.get('data_collection', {}).get('enabled', True):
self.data_collector = DataCollector(self.config['data_collection'])
else:
self.data_collector = None
# Simulation state
self.current_step = 0
@ -55,7 +67,7 @@ class Simulation:
self.colony.update(dt)
# Update visualization if enabled
if self.config['visualization']['enabled']:
if self.visualizer is not None:
world_state = {
'agents': self.colony.agents,
'resources': self.environment.resources,
@ -64,8 +76,8 @@ class Simulation:
}
self.visualizer.update(world_state)
# Collect data
if self.config['data_collection']['enabled']:
# Collect data if enabled
if self.data_collector is not None:
self.data_collector.collect(self.current_time, world_state)
# Save data periodically
@ -89,7 +101,7 @@ class Simulation:
print(f"Step {self.current_step}/{max_steps}")
# Update visualization
if not headless and self.config['visualization']['enabled']:
if not headless and self.visualizer is not None:
plt.pause(0.001) # Allow GUI to update
except KeyboardInterrupt:
@ -97,11 +109,11 @@ class Simulation:
finally:
# Save final data
if self.config['data_collection']['enabled']:
if self.data_collector is not None:
self.data_collector.save_data()
# Show final visualization
if not headless and self.config['visualization']['enabled']:
if not headless and self.visualizer is not None:
plt.show()
def pause(self) -> None:
@ -123,5 +135,5 @@ class Simulation:
self.colony = Colony(self.config['colony'], self.environment)
# Clear data
if self.config['data_collection']['enabled']:
if self.data_collector is not None:
self.data_collector = DataCollector(self.config['data_collection'])

Просмотреть файл

@ -134,13 +134,13 @@ class DataCollector:
'run_id': self.run_id,
'duration': len(self.data['time']),
'colony_stats': {
'mean_population': float(colony_stats['population'].mean()),
'peak_population': int(colony_stats['population'].max()),
'total_food_collected': float(colony_stats['total_food_collected'].sum())
'mean_population': float(np.mean([stats['population'] for stats in self.data['colony_stats']])),
'peak_population': int(max(stats['population'] for stats in self.data['colony_stats'])),
'total_food_collected': float(sum(stats['total_food_collected'] for stats in self.data['colony_stats']))
},
'task_distribution': {
task: float(task_dist[task].mean())
for task in task_dist.columns
task: float(np.mean([dist.get(task, 0) for dist in self.data['task_distribution']]))
for task in set().union(*self.data['task_distribution'])
}
}

Просмотреть файл

@ -14,9 +14,33 @@ class SimulationRenderer:
def __init__(self, config: dict):
"""Initialize the renderer with configuration settings."""
self.config = config
self.viz_config = config['visualization']
self.viz_config = config.get('visualization', {})
self.env_size = config['environment']['size']
# Default colors if not specified in config
self.colors = {
'nest': 'brown',
'food': 'green',
'obstacles': 'gray',
'agents': {
'foraging': 'red',
'maintenance': 'blue',
'nursing': 'pink',
'defense': 'darkred',
'exploration': 'orange'
},
'pheromones': {
'food': 'green',
'home': 'red',
'alarm': 'orange',
'trail': 'blue'
}
}
# Override defaults with config values if present
if 'colors' in self.viz_config:
self.colors.update(self.viz_config['colors'])
# Setup the figure and axis
self.fig, self.ax = plt.subplots(figsize=(10, 10))
self.ax.set_xlim(0, self.env_size[0])
@ -31,7 +55,7 @@ class SimulationRenderer:
# Setup the nest
nest_loc = config['environment']['nest_location']
nest = Circle(nest_loc, 5, color=self.viz_config['colors']['nest'])
nest = Circle(nest_loc, 5, color=self.colors['nest'])
self.ax.add_patch(nest)
def update(self, world_state: dict) -> None:
@ -46,7 +70,7 @@ class SimulationRenderer:
# Update pheromones
for p_type, grid in world_state['pheromones'].items():
if p_type not in self.pheromone_plots:
color = self.viz_config['colors']['pheromones'][p_type]
color = self.colors['pheromones'][p_type]
self.pheromone_plots[p_type] = self.ax.imshow(
grid,
extent=[0, self.env_size[0], 0, self.env_size[1]],
@ -60,7 +84,7 @@ class SimulationRenderer:
# Draw agents
for agent in world_state['agents']:
color = self.viz_config['colors']['agents'][agent.current_task.value]
color = self.colors['agents'][agent.current_task.value]
agent_patch = self._create_agent_patch(agent, color)
self.ax.add_patch(agent_patch)
self.agent_patches.append(agent_patch)
@ -69,8 +93,8 @@ class SimulationRenderer:
for food in world_state['resources']:
food_patch = Circle(
(food.position.x, food.position.y),
food.size,
color=self.viz_config['colors']['food']
food.size if hasattr(food, 'size') else 1.0,
color=self.colors['food']
)
self.ax.add_patch(food_patch)
self.food_patches.append(food_patch)
@ -79,8 +103,8 @@ class SimulationRenderer:
for obstacle in world_state['obstacles']:
obstacle_patch = Circle(
(obstacle.position.x, obstacle.position.y),
obstacle.size,
color=self.viz_config['colors']['obstacles']
obstacle.size if hasattr(obstacle, 'size') else 1.0,
color=self.colors['obstacles']
)
self.ax.add_patch(obstacle_patch)
self.obstacle_patches.append(obstacle_patch)

Двоичные данные
Things/Ant_Colony/output/simulation_20250207_132721.h5 Обычный файл

Двоичный файл не отображается.

Двоичные данные
Things/Ant_Colony/output/simulation_20250207_132802.h5 Обычный файл

Двоичный файл не отображается.

21
Things/Path_Network/LICENSE Обычный файл
Просмотреть файл

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Path Network Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

137
Things/Path_Network/Path_Network_README.md Обычный файл
Просмотреть файл

@ -0,0 +1,137 @@
# Path Network: Active Inference Agents in a Dynamic Topology
## Project Overview
We want a simulation where an arbitrary topology (randomly initially generated) of nodes, reflecting their communicative effective causal mutual influence, is generated and simulated. Each node is a Continuous time, Active Inference agent.
Different nodes can have different relations with the same underlying inference methods, for example different time delta of integration, different levels of Taylor Series expansion for generalized coordinate anticipatory inference.
At a big picture level, it is like there are nested Sin waves that are the "level of water in the world". All of the nodes are like ships or towers, that are doing this real-time inference on how much to ascend or descend in order to stay within a tolerable limited range of the overall sea level (which floats all boats).
## Project Structure
```
path_network/
├── core/
│ ├── __init__.py
│ ├── agent.py # Active Inference agent implementation
│ ├── network.py # Network topology and communication
│ ├── dynamics.py # Sin wave and environmental dynamics
│ └── inference.py # Generalized coordinates and inference methods
├── utils/
│ ├── __init__.py
│ ├── visualization.py # Network and dynamics visualization
│ └── math_utils.py # Mathematical utilities
├── config/
│ ├── __init__.py
│ └── settings.py # Simulation parameters
└── simulation/
├── __init__.py
└── runner.py # Main simulation orchestration
```
## Core Components
### 1. Active Inference Agent (agent.py)
- Continuous-time state estimation
- Free energy minimization
- Generalized coordinates for anticipatory inference
- Adaptive response mechanisms
- Individual parameter settings for:
- Integration time delta
- Taylor series expansion levels
- Tolerance ranges
- Response dynamics
### 2. Network Topology (network.py)
- Random topology generation
- Inter-agent communication protocols
- Influence weight matrices
- Dynamic topology updates
- Neighborhood relationships
### 3. Environmental Dynamics (dynamics.py)
- Nested sinusoidal wave generation
- Global state management
- Environmental perturbations
- Time evolution systems
### 4. Inference Methods (inference.py)
- Generalized coordinates implementation
- Free energy computation
- Variational inference algorithms
- Prediction error minimization
## Implementation Plan
1. **Phase 1: Core Infrastructure**
- Set up project structure
- Implement basic mathematical utilities
- Create configuration management
- Develop testing framework
2. **Phase 2: Agent Implementation**
- Implement core Active Inference mathematics
- Create base agent class
- Add generalized coordinate systems
- Implement free energy minimization
3. **Phase 3: Network Development**
- Create topology generation
- Implement inter-agent communication
- Develop influence propagation
- Add dynamic network updates
4. **Phase 4: Environmental Systems**
- Implement sinusoidal wave systems
- Create global state management
- Add perturbation mechanisms
- Develop state tracking
5. **Phase 5: Visualization and Analysis**
- Create network visualization
- Implement state space plotting
- Add analysis tools
- Create performance metrics
6. **Phase 6: Integration and Testing**
- Integrate all components
- Comprehensive testing
- Performance optimization
- Documentation
## Key Features
1. **Dynamic Topology**
- Random initial generation
- Adaptive connectivity
- Influence-based relationships
- Real-time updates
2. **Active Inference Implementation**
- Continuous time processing
- Generalized coordinate inference
- Variable integration time steps
- Adaptive response mechanisms
3. **Environmental Simulation**
- Multi-frequency sin waves
- Global state management
- Perturbation systems
- State tracking
4. **Visualization and Analysis**
- Network topology visualization
- State space plotting
- Performance metrics
- Real-time monitoring
## Dependencies
- NumPy: Numerical computations
- NetworkX: Graph topology management
- SciPy: Scientific computations
- Matplotlib: Visualization
- PyTorch: Deep learning and optimization
- Pandas: Data management
## Getting Started
[Implementation to follow]

114
Things/Path_Network/README.md Обычный файл
Просмотреть файл

@ -0,0 +1,114 @@
# Path Network Simulation
A simulation of Active Inference agents in a dynamic network topology, where agents adapt their heights in response to changing water levels and network influences.
## Features
- **Active Inference Agents**: Each node implements continuous-time active inference
- **Dynamic Network Topology**: Network connections evolve based on agent performance
- **Environmental Dynamics**: Complex water level patterns through nested sinusoidal waves
- **Real-time Visualization**: Interactive visualization of network state and agent behavior
- **Comprehensive Analysis**: Detailed metrics and visualizations of simulation results
## Requirements
### System Dependencies
- Python 3.8+
- Qt5 or Tk (for visualization)
- Required system libraries (installed automatically by setup script)
### Python Dependencies
All Python dependencies are listed in `requirements.txt` and will be installed automatically by the setup script.
## Quick Start
The easiest way to run the simulation is using the provided setup script:
```bash
./run_simulation.sh
```
This script will:
1. Create a Python virtual environment
2. Install required system dependencies
3. Install Python dependencies
4. Run the simulation
5. Save results to the `output` directory
## Manual Setup
If you prefer to set up manually:
1. Create and activate a virtual environment:
```bash
python3 -m venv venv
source venv/bin/activate
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Run the simulation:
```bash
python example.py
```
## Output
The simulation creates a timestamped output directory containing:
- `simulation.log`: Detailed log of the simulation run
- `initial_state.png`: Network state visualization after initial period
- `final_state.png`: Network state visualization after perturbation
- `water_level_distribution.png`: Distribution of water levels
- `agent_height_variations.png`: Time series of agent heights
- `water_levels.npy`: Raw water level data
- `agent_heights.npy`: Raw agent height data
## Visualization
The simulation provides real-time visualization of:
- Network topology with node colors representing heights
- Current agent heights compared to water level
- Historical evolution of heights and water levels
## Configuration
Key parameters can be adjusted in `example.py`:
- Network parameters (number of nodes, connectivity, etc.)
- Wave components (frequencies, amplitudes, phases)
- Simulation duration and visualization intervals
- Perturbation parameters
## Troubleshooting
### Visualization Issues
If you encounter visualization problems:
1. The script will automatically try different backends (Qt5 → Tk → Agg)
2. Check if Qt5 or Tk is properly installed
3. Look for error messages in the simulation log
### System Dependencies
If system dependencies fail to install:
1. Check your system's package manager
2. Install required packages manually:
- For Ubuntu/Debian: `sudo apt-get install python3-tk python3-qt5 libxcb-cursor0`
- For Fedora: `sudo dnf install python3-tkinter python3-qt5 xcb-util-cursor`
- For Arch: `sudo pacman -S python-tk qt5-base xcb-util-cursor`
## Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
## License
This project is licensed under the MIT License - see the LICENSE file for details.

91
Things/Path_Network/config.yaml Обычный файл
Просмотреть файл

@ -0,0 +1,91 @@
# Path Network Simulation Configuration
# Network Configuration
network:
num_nodes: 15
initial_connectivity: 0.3
min_weight: 0.1
max_weight: 1.0
dynamic_topology: true
topology_update_interval: 50
# Environmental Dynamics
dynamics:
wave_components:
- name: "slow_wave"
amplitude: 1.0
frequency: 0.1
phase: 0.0
- name: "medium_wave"
amplitude: 0.5
frequency: 0.5
phase: 0.0
- name: "fast_wave"
amplitude: 0.2
frequency: 2.0
phase: 0.0
noise_std: 0.05
time_scale: 1.0
# Simulation Parameters
simulation:
total_steps: 1000
initial_period: 200
save_interval: 10
visualization_interval: 20
# Agent Parameters
agent:
dt: 0.01
taylor_order: 2
tolerance: 0.1
learning_rate: 0.01
# Perturbation Settings
perturbation:
magnitude: 2.0
duration: 100
decay: 1.0
# Visualization Settings
visualization:
# General settings
style: "seaborn-whitegrid"
font_scale: 1.2
dpi: 300
fps: 30 # For animations
# Color schemes
network_cmap: "coolwarm"
agent_cmap: "viridis"
# Plot sizes
network_figsize: [15, 10]
timeseries_figsize: [12, 6]
animation_figsize: [10, 10]
# Output formats
save_formats: ["png", "pdf"]
create_animations: true
# Animation settings
animation_duration: 10 # seconds
# Plot types to generate
plots:
network_topology: true
height_distribution: true
height_variations: true
phase_space: true
prediction_errors: true
correlation_matrix: true
free_energy: true
spectral_analysis: true
# 3D visualization
enable_3d: true
3d_rotation_speed: 2 # degrees per frame
# Interactive features
enable_interactive: true
update_interval: 100 # milliseconds

228
Things/Path_Network/example.py Обычный файл
Просмотреть файл

@ -0,0 +1,228 @@
"""
Example script demonstrating the Path Network simulation.
"""
import os
import sys
import logging
import numpy as np
import matplotlib
from datetime import datetime
from pathlib import Path
import yaml
from path_network.core.network import NetworkConfig
from path_network.core.dynamics import DynamicsConfig, WaveComponent
from path_network.simulation.runner import SimulationRunner, SimulationConfig
from path_network.utils.visualization import NetworkVisualizer
from path_network.utils.advanced_visualization import AdvancedVisualizer
def load_config(config_path: str = "config.yaml") -> dict:
"""Load configuration from YAML file."""
with open(config_path, 'r') as f:
return yaml.safe_load(f)
def setup_logging(output_dir: Path) -> logging.Logger:
"""Set up logging configuration."""
logger = logging.getLogger('path_network')
logger.setLevel(logging.INFO)
# Create handlers
console_handler = logging.StreamHandler()
file_handler = logging.FileHandler(output_dir / 'simulation.log')
# Create formatters and add it to handlers
log_format = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
console_handler.setFormatter(log_format)
file_handler.setFormatter(log_format)
# Add handlers to the logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
def setup_matplotlib_backend(logger: logging.Logger) -> bool:
"""
Set up appropriate matplotlib backend.
Returns:
bool: True if interactive backend is available
"""
interactive = True
# First try Qt5Agg
try:
matplotlib.use('Qt5Agg')
logger.info("Using Qt5Agg backend")
return interactive
except Exception:
logger.warning("Could not use Qt5Agg backend")
# Then try TkAgg
try:
matplotlib.use('TkAgg')
logger.info("Using TkAgg backend")
return interactive
except Exception:
logger.warning("Could not use TkAgg backend")
# Finally fallback to Agg
try:
matplotlib.use('Agg')
logger.info("Using non-interactive Agg backend")
interactive = False
return interactive
except Exception as e:
logger.error(f"Could not set up any matplotlib backend: {e}")
raise
def create_output_directory() -> Path:
"""Create and return output directory for simulation results."""
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
output_dir = Path('output') / timestamp
output_dir.mkdir(parents=True, exist_ok=True)
return output_dir
def run_simulation(
config: dict,
logger: logging.Logger,
output_dir: Path,
interactive: bool
):
"""Run the main simulation with comprehensive logging and visualization."""
logger.info("Initializing simulation configurations...")
# Create network configuration
network_config = NetworkConfig(
num_nodes=config['network']['num_nodes'],
initial_connectivity=config['network']['initial_connectivity'],
min_weight=config['network']['min_weight'],
max_weight=config['network']['max_weight'],
dynamic_topology=config['network']['dynamic_topology'],
topology_update_interval=config['network']['topology_update_interval']
)
# Create dynamics configuration
dynamics_config = DynamicsConfig(
base_components=[
WaveComponent(
amplitude=comp['amplitude'],
frequency=comp['frequency'],
phase=comp['phase']
)
for comp in config['dynamics']['wave_components']
],
noise_std=config['dynamics']['noise_std'],
time_scale=config['dynamics']['time_scale']
)
# Create simulation configuration
sim_config = SimulationConfig(
network_config=network_config,
dynamics_config=dynamics_config,
num_steps=config['simulation']['total_steps'],
save_interval=config['simulation']['save_interval'],
visualization_interval=(
config['simulation']['visualization_interval']
if interactive
else config['simulation']['total_steps'] // 10
)
)
logger.info("Creating visualizers...")
basic_visualizer = NetworkVisualizer()
advanced_visualizer = AdvancedVisualizer()
logger.info("Initializing simulation runner...")
runner = SimulationRunner(sim_config, basic_visualizer)
# Initial simulation period
logger.info("Running initial simulation period...")
initial_states = runner.run(config['simulation']['initial_period'])
# Save initial period results
basic_visualizer.save(output_dir / 'initial_state.png')
logger.info("Saved initial state visualization")
# Add perturbation
logger.info("Adding perturbation to the system...")
runner.add_perturbation(
magnitude=config['perturbation']['magnitude'],
duration=config['perturbation']['duration'],
decay=config['perturbation']['decay']
)
# Continue simulation
logger.info("Running post-perturbation simulation...")
final_states = runner.run(
config['simulation']['total_steps'] -
config['simulation']['initial_period']
)
# Save final basic visualization
basic_visualizer.save(output_dir / 'final_state.png')
logger.info("Saved final state visualization")
# Create advanced visualizations
logger.info("Creating advanced visualizations...")
# Get full history
history = runner.get_history()
# Create animations
if config['visualization']['create_animations']:
logger.info("Creating animations...")
advanced_visualizer.create_network_animation(history, output_dir)
advanced_visualizer.create_phase_space_animation(history, output_dir)
# Create static visualizations
if config['visualization']['plots']['spectral_analysis']:
logger.info("Creating spectral analysis...")
advanced_visualizer.create_spectral_analysis(history, output_dir)
if config['visualization']['plots']['correlation_matrix']:
logger.info("Creating correlation analysis...")
advanced_visualizer.create_correlation_analysis(history, output_dir)
if config['visualization']['enable_interactive']:
logger.info("Creating interactive dashboard...")
advanced_visualizer.create_interactive_dashboard(history, output_dir)
basic_visualizer.close()
logger.info("Simulation complete!")
return initial_states, final_states
def main():
"""Main entry point for the simulation."""
# Load configuration
config = load_config()
# Create output directory
output_dir = create_output_directory()
# Setup logging
logger = setup_logging(output_dir)
try:
# Setup matplotlib backend
interactive = setup_matplotlib_backend(logger)
# Run simulation
initial_states, final_states = run_simulation(
config,
logger,
output_dir,
interactive
)
logger.info(f"Results saved to: {output_dir}")
except Exception as e:
logger.error(f"Simulation failed: {str(e)}", exc_info=True)
sys.exit(1)
if __name__ == '__main__':
main()

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,112 @@
"""
Active Inference Agent implementation for the Path Network simulation.
This module contains the core agent class that performs continuous-time active inference.
"""
import numpy as np
import torch
from dataclasses import dataclass
from typing import List, Optional, Tuple
@dataclass
class AgentConfig:
"""Configuration parameters for an Active Inference agent."""
dt: float = 0.01 # Integration time step
taylor_order: int = 2 # Order of Taylor expansion for generalized coordinates
tolerance: float = 0.1 # Tolerance range for water level
learning_rate: float = 0.01 # Learning rate for parameter updates
initial_height: Optional[float] = None # Initial height of the agent
class ActiveInferenceAgent:
"""
An Active Inference agent that infers and responds to water level changes.
The agent uses generalized coordinates and continuous-time inference to
maintain its height within a tolerable range of the global water level.
"""
def __init__(self, config: AgentConfig):
self.config = config
self.height = (
config.initial_height
if config.initial_height is not None
else np.random.normal(0, 0.1)
)
# Generalized coordinates for position (height)
self.gen_coords = torch.zeros(config.taylor_order)
self.gen_coords[0] = self.height
# Free energy components
self.mu = torch.zeros(config.taylor_order) # Expected states
self.pi = torch.ones(config.taylor_order) # Precision (inverse variance)
# History for analysis
self.height_history: List[float] = [self.height]
self.prediction_error_history: List[float] = []
def update_beliefs(self, sensory_input: float) -> None:
"""
Update beliefs about the environment based on sensory input.
Args:
sensory_input: The current water level measurement
"""
# Compute prediction error
prediction_error = sensory_input - self.mu[0]
self.prediction_error_history.append(prediction_error.item())
# Update generalized coordinates
for i in range(self.config.taylor_order - 1):
self.gen_coords[i] += (
self.gen_coords[i + 1] * self.config.dt +
self.config.learning_rate * prediction_error * self.pi[i]
)
# Update expectations
self.mu = self.gen_coords.clone()
def act(self, water_level: float) -> float:
"""
Generate an action (height adjustment) based on current beliefs.
Args:
water_level: Current global water level
Returns:
float: The new height of the agent
"""
# Update beliefs based on sensory input
self.update_beliefs(water_level)
# Compute desired height adjustment
error = water_level - self.height
adjustment = np.clip(
error * self.config.learning_rate,
-self.config.tolerance,
self.config.tolerance
)
# Update height
self.height += adjustment
self.height_history.append(self.height)
return self.height
def get_state(self) -> Tuple[float, List[float]]:
"""
Get the current state of the agent.
Returns:
Tuple containing current height and generalized coordinates
"""
return self.height, self.gen_coords.tolist()
def get_history(self) -> Tuple[List[float], List[float]]:
"""
Get the agent's history of heights and prediction errors.
Returns:
Tuple containing height history and prediction error history
"""
return self.height_history, self.prediction_error_history

Просмотреть файл

@ -0,0 +1,126 @@
"""
Environmental dynamics for the Path Network simulation.
Generates nested sinusoidal waves that represent the global water level.
"""
import numpy as np
from dataclasses import dataclass
from typing import List, Optional, Tuple
@dataclass
class WaveComponent:
"""Parameters for a single sinusoidal wave component."""
amplitude: float
frequency: float
phase: float = 0.0
@dataclass
class DynamicsConfig:
"""Configuration for the environmental dynamics."""
base_components: List[WaveComponent] = None
noise_std: float = 0.05
time_scale: float = 1.0
def __post_init__(self):
if self.base_components is None:
# Default wave components
self.base_components = [
WaveComponent(amplitude=1.0, frequency=0.1), # Slow wave
WaveComponent(amplitude=0.5, frequency=0.5), # Medium wave
WaveComponent(amplitude=0.2, frequency=2.0) # Fast wave
]
class EnvironmentalDynamics:
"""
Generates and manages the environmental dynamics (water level) through
nested sinusoidal waves with optional noise and perturbations.
"""
def __init__(self, config: DynamicsConfig):
self.config = config
self.time = 0.0
self.history: List[float] = []
self.perturbation: Optional[Tuple[float, float, float]] = None
def step(self) -> float:
"""
Compute the next water level value.
Returns:
float: The current global water level
"""
# Base level from superposition of waves
level = sum(
component.amplitude * np.sin(
2 * np.pi * component.frequency * self.time * self.config.time_scale
+ component.phase
)
for component in self.config.base_components
)
# Add noise
if self.config.noise_std > 0:
level += np.random.normal(0, self.config.noise_std)
# Add any active perturbation
if self.perturbation is not None:
magnitude, duration, decay = self.perturbation
if self.time < duration:
level += magnitude * (1 - self.time / duration)
else:
# Remove perturbation after it expires
self.perturbation = None
# Update time and history
self.time += 1
self.history.append(level)
return level
def add_perturbation(
self,
magnitude: float,
duration: float,
decay: float = 1.0
) -> None:
"""
Add a temporary perturbation to the water level.
Args:
magnitude: Size of the perturbation
duration: How long the perturbation lasts
decay: How quickly the perturbation decays (1.0 = linear)
"""
self.perturbation = (magnitude, duration, decay)
def add_wave_component(
self,
amplitude: float,
frequency: float,
phase: float = 0.0
) -> None:
"""
Add a new wave component to the dynamics.
Args:
amplitude: Wave amplitude
frequency: Wave frequency
phase: Wave phase shift
"""
self.config.base_components.append(
WaveComponent(amplitude, frequency, phase)
)
def get_history(self) -> List[float]:
"""Get the history of water levels."""
return self.history.copy()
def get_wave_components(self) -> List[WaveComponent]:
"""Get the current wave components."""
return self.config.base_components.copy()
def reset(self) -> None:
"""Reset the dynamics to initial state."""
self.time = 0.0
self.history.clear()
self.perturbation = None

Просмотреть файл

@ -0,0 +1,173 @@
"""
Network topology management for the Path Network simulation.
Handles the creation and management of the network of Active Inference agents.
"""
import networkx as nx
import numpy as np
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from .agent import ActiveInferenceAgent, AgentConfig
@dataclass
class NetworkConfig:
"""Configuration parameters for the network topology."""
num_nodes: int = 10
initial_connectivity: float = 0.3 # Probability of edge creation
min_weight: float = 0.1
max_weight: float = 1.0
dynamic_topology: bool = True
topology_update_interval: int = 100 # Steps between topology updates
class PathNetwork:
"""
Manages a network of Active Inference agents and their interactions.
The network topology is represented as a weighted directed graph where
edges represent influence relationships between agents.
"""
def __init__(self, network_config: NetworkConfig):
self.config = network_config
self.graph = nx.DiGraph()
self.agents: Dict[int, ActiveInferenceAgent] = {}
self.step_counter = 0
# Initialize network
self._initialize_network()
def _initialize_network(self) -> None:
"""Initialize the network topology and create agents."""
# Create nodes with agents
for i in range(self.config.num_nodes):
agent_config = AgentConfig(
initial_height=np.random.normal(0, 0.1)
)
self.agents[i] = ActiveInferenceAgent(agent_config)
self.graph.add_node(i)
# Create random edges
for i in range(self.config.num_nodes):
for j in range(self.config.num_nodes):
if i != j and np.random.random() < self.config.initial_connectivity:
weight = np.random.uniform(
self.config.min_weight,
self.config.max_weight
)
self.graph.add_edge(i, j, weight=weight)
def update_topology(self) -> None:
"""
Update network topology based on agent interactions and performance.
This is called periodically to evolve the network structure.
"""
if not self.config.dynamic_topology:
return
# Update edge weights based on prediction error correlation
for i, j in self.graph.edges():
agent_i = self.agents[i]
agent_j = self.agents[j]
# Compute correlation between agents' prediction errors
corr = np.corrcoef(
agent_i.prediction_error_history[-100:],
agent_j.prediction_error_history[-100:]
)[0, 1]
# Update edge weight
new_weight = np.clip(
abs(corr),
self.config.min_weight,
self.config.max_weight
)
self.graph[i][j]['weight'] = new_weight
# Potentially add or remove edges based on agent performance
self._update_connections()
def _update_connections(self) -> None:
"""Update network connections based on agent performance."""
# Add edges between well-performing agents
for i in range(self.config.num_nodes):
for j in range(self.config.num_nodes):
if i != j and not self.graph.has_edge(i, j):
# Add edge if agents have similar performance
if self._should_connect(i, j):
weight = np.random.uniform(
self.config.min_weight,
self.config.max_weight
)
self.graph.add_edge(i, j, weight=weight)
# Remove edges with very low weights
edges_to_remove = [
(i, j) for i, j, w in self.graph.edges(data='weight')
if w < self.config.min_weight
]
self.graph.remove_edges_from(edges_to_remove)
def _should_connect(self, i: int, j: int) -> bool:
"""Determine if two agents should be connected based on their performance."""
agent_i = self.agents[i]
agent_j = self.agents[j]
# Compare recent performance
errors_i = np.mean(agent_i.prediction_error_history[-50:])
errors_j = np.mean(agent_j.prediction_error_history[-50:])
# Connect if agents have similar error levels
return abs(errors_i - errors_j) < 0.1
def step(self, global_water_level: float) -> Dict[int, float]:
"""
Perform one step of network simulation.
Args:
global_water_level: The current global water level
Returns:
Dict mapping node IDs to their new heights
"""
self.step_counter += 1
# Update agent states
new_heights = {}
for node_id, agent in self.agents.items():
# Compute weighted average of neighbors' heights
neighbor_influence = 0.0
total_weight = 0.0
for neighbor_id in self.graph.predecessors(node_id):
weight = self.graph[neighbor_id][node_id]['weight']
neighbor_height = self.agents[neighbor_id].height
neighbor_influence += weight * neighbor_height
total_weight += weight
if total_weight > 0:
effective_level = (
0.7 * global_water_level +
0.3 * (neighbor_influence / total_weight)
)
else:
effective_level = global_water_level
# Update agent
new_height = agent.act(effective_level)
new_heights[node_id] = new_height
# Periodically update topology
if (self.step_counter % self.config.topology_update_interval) == 0:
self.update_topology()
return new_heights
def get_network_state(self) -> Tuple[nx.DiGraph, Dict[int, float]]:
"""
Get the current state of the network.
Returns:
Tuple containing the network graph and current agent heights
"""
heights = {i: agent.height for i, agent in self.agents.items()}
return self.graph, heights

Просмотреть файл

Просмотреть файл

@ -0,0 +1,120 @@
"""
Main simulation runner for the Path Network.
Orchestrates the interaction between the network of agents and environmental dynamics.
"""
import numpy as np
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from ..core.network import PathNetwork, NetworkConfig
from ..core.dynamics import EnvironmentalDynamics, DynamicsConfig
from ..utils.visualization import NetworkVisualizer
@dataclass
class SimulationConfig:
"""Configuration for the simulation."""
network_config: NetworkConfig
dynamics_config: DynamicsConfig
num_steps: int = 1000
save_interval: int = 10
visualization_interval: int = 50
class SimulationRunner:
"""
Main simulation runner that coordinates the network of agents
and environmental dynamics.
"""
def __init__(
self,
config: SimulationConfig,
visualizer: Optional[NetworkVisualizer] = None
):
self.config = config
self.network = PathNetwork(config.network_config)
self.dynamics = EnvironmentalDynamics(config.dynamics_config)
self.visualizer = visualizer
self.current_step = 0
self.history: List[Dict] = []
def step(self) -> Dict:
"""
Perform one step of the simulation.
Returns:
Dict containing the current state of the simulation
"""
# Get current water level
water_level = self.dynamics.step()
# Update network
agent_heights = self.network.step(water_level)
# Record state
state = {
'step': self.current_step,
'water_level': water_level,
'agent_heights': agent_heights
}
if self.current_step % self.config.save_interval == 0:
self.history.append(state)
# Visualize if needed
if (
self.visualizer is not None and
self.current_step % self.config.visualization_interval == 0
):
self.visualizer.update(self.network, water_level)
self.current_step += 1
return state
def run(self, num_steps: Optional[int] = None) -> List[Dict]:
"""
Run the simulation for a specified number of steps.
Args:
num_steps: Number of steps to run (default: config.num_steps)
Returns:
List of recorded states
"""
steps_to_run = num_steps or self.config.num_steps
for _ in range(steps_to_run):
self.step()
return self.history
def add_perturbation(
self,
magnitude: float,
duration: float,
decay: float = 1.0
) -> None:
"""Add a perturbation to the environmental dynamics."""
self.dynamics.add_perturbation(magnitude, duration, decay)
def get_network_state(self) -> Tuple[Dict[int, float], float]:
"""
Get the current state of the network and environment.
Returns:
Tuple of agent heights and current water level
"""
_, heights = self.network.get_network_state()
water_level = self.dynamics.history[-1] if self.dynamics.history else 0.0
return heights, water_level
def get_history(self) -> List[Dict]:
"""Get the simulation history."""
return self.history.copy()
def reset(self) -> None:
"""Reset the simulation to initial state."""
self.current_step = 0
self.history.clear()
self.dynamics.reset()
# Note: Network is not reset as it maintains its learned structure

Просмотреть файл

Просмотреть файл

@ -0,0 +1,390 @@
"""
Advanced visualization utilities for the Path Network simulation.
Provides comprehensive visualization options including animations and 3D plots.
"""
import os
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import seaborn as sns
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
from matplotlib.animation import FuncAnimation, PillowWriter
from mpl_toolkits.mplot3d import Axes3D
from scipy import signal
from scipy.stats import gaussian_kde
from typing import Dict, List, Optional, Tuple, Any
import yaml
from pathlib import Path
from tqdm import tqdm
import imageio
from sklearn.decomposition import PCA
from ..core.network import PathNetwork
class AdvancedVisualizer:
"""Advanced visualization tools for the Path Network simulation."""
def __init__(self, config_path: str = "config.yaml"):
# Load configuration
with open(config_path, 'r') as f:
self.config = yaml.safe_load(f)['visualization']
# Set style
plt.style.use(self.config['style'])
sns.set_context("notebook", font_scale=self.config['font_scale'])
# Initialize storage for animation frames
self.network_frames: List[plt.Figure] = []
self.phase_space_frames: List[plt.Figure] = []
def create_network_animation(
self,
history: List[Dict],
output_path: Path
) -> None:
"""Create an animated visualization of the network evolution."""
fig = plt.figure(figsize=self.config['animation_figsize'])
def update(frame):
plt.clf()
state = history[frame]
self._draw_network_state(state, fig.gca())
return fig.gca()
anim = FuncAnimation(
fig,
update,
frames=len(history),
interval=1000/self.config['fps']
)
# Save as GIF
writer = PillowWriter(fps=self.config['fps'])
anim.save(output_path / 'network_evolution.gif', writer=writer)
plt.close()
def create_phase_space_animation(
self,
history: List[Dict],
output_path: Path
) -> None:
"""Create an animated 3D phase space visualization."""
if not self.config['enable_3d']:
return
fig = plt.figure(figsize=self.config['animation_figsize'])
ax = fig.add_subplot(111, projection='3d')
# Compute PCA for dimensionality reduction
heights_matrix = []
for state in history:
heights = list(state['agent_heights'].values())
heights_matrix.append(heights)
pca = PCA(n_components=3)
transformed = pca.fit_transform(heights_matrix)
def update(frame):
ax.clear()
ax.scatter(
transformed[:frame+1, 0],
transformed[:frame+1, 1],
transformed[:frame+1, 2],
c=range(frame+1),
cmap='viridis'
)
ax.view_init(
elev=30,
azim=frame * self.config['3d_rotation_speed']
)
return ax
anim = FuncAnimation(
fig,
update,
frames=len(history),
interval=1000/self.config['fps']
)
anim.save(output_path / 'phase_space.gif', writer=PillowWriter(fps=self.config['fps']))
plt.close()
def create_spectral_analysis(
self,
history: List[Dict],
output_path: Path
) -> None:
"""Create spectral analysis visualizations."""
water_levels = [state['water_level'] for state in history]
# Compute spectrograms for water level
f, t, Sxx = signal.spectrogram(water_levels, fs=1.0)
plt.figure(figsize=self.config['timeseries_figsize'])
plt.pcolormesh(t, f, Sxx, shading='gouraud')
plt.ylabel('Frequency')
plt.xlabel('Time')
plt.title('Water Level Spectrogram')
plt.colorbar(label='Intensity')
plt.savefig(output_path / 'water_level_spectrogram.png', dpi=self.config['dpi'])
plt.close()
# Compute and plot power spectrum
plt.figure(figsize=self.config['timeseries_figsize'])
f, Pxx = signal.welch(water_levels)
plt.semilogy(f, Pxx)
plt.xlabel('Frequency')
plt.ylabel('Power Spectral Density')
plt.title('Water Level Power Spectrum')
plt.savefig(output_path / 'power_spectrum.png', dpi=self.config['dpi'])
plt.close()
def create_correlation_analysis(
self,
history: List[Dict],
output_path: Path
) -> None:
"""Create correlation analysis visualizations."""
# Extract agent height histories
agent_histories = {}
for state in history:
for agent_id, height in state['agent_heights'].items():
if agent_id not in agent_histories:
agent_histories[agent_id] = []
agent_histories[agent_id].append(height)
# Compute correlation matrix
num_agents = len(agent_histories)
corr_matrix = np.zeros((num_agents, num_agents))
for i in range(num_agents):
for j in range(num_agents):
corr = np.corrcoef(
agent_histories[i],
agent_histories[j]
)[0, 1]
corr_matrix[i, j] = corr
# Plot correlation matrix
plt.figure(figsize=self.config['network_figsize'])
sns.heatmap(
corr_matrix,
cmap='RdBu_r',
center=0,
vmin=-1,
vmax=1,
annot=True,
fmt='.2f'
)
plt.title('Agent Height Correlation Matrix')
plt.savefig(output_path / 'correlation_matrix.png', dpi=self.config['dpi'])
plt.close()
def create_interactive_dashboard(
self,
history: List[Dict],
output_path: Path
) -> None:
"""Create an interactive HTML dashboard using plotly."""
if not self.config['enable_interactive']:
return
# Create subplot figure
fig = make_subplots(
rows=2,
cols=2,
specs=[[{'type': 'scatter3d'}, {'type': 'heatmap'}],
[{'type': 'scatter'}, {'type': 'histogram'}]],
subplot_titles=(
'3D Phase Space',
'Agent Correlation',
'Height Evolution',
'Height Distribution'
)
)
# Add traces
self._add_phase_space_trace(fig, history, row=1, col=1)
self._add_correlation_trace(fig, history, row=1, col=2)
self._add_height_evolution_trace(fig, history, row=2, col=1)
self._add_height_distribution_trace(fig, history, row=2, col=2)
# Update layout
fig.update_layout(height=1000, showlegend=True)
# Save
fig.write_html(output_path / 'interactive_dashboard.html')
def _add_phase_space_trace(
self,
fig: go.Figure,
history: List[Dict],
row: int,
col: int
) -> None:
"""Add 3D phase space trace to plotly figure."""
heights_matrix = []
for state in history:
heights = list(state['agent_heights'].values())
heights_matrix.append(heights)
pca = PCA(n_components=3)
transformed = pca.fit_transform(heights_matrix)
fig.add_trace(
go.Scatter3d(
x=transformed[:, 0],
y=transformed[:, 1],
z=transformed[:, 2],
mode='lines+markers',
marker=dict(
size=2,
color=range(len(transformed)),
colorscale='Viridis',
opacity=0.8
),
name='Phase Space'
),
row=row,
col=col
)
def _add_correlation_trace(
self,
fig: go.Figure,
history: List[Dict],
row: int,
col: int
) -> None:
"""Add correlation matrix trace to plotly figure."""
agent_histories = {}
for state in history:
for agent_id, height in state['agent_heights'].items():
if agent_id not in agent_histories:
agent_histories[agent_id] = []
agent_histories[agent_id].append(height)
num_agents = len(agent_histories)
corr_matrix = np.zeros((num_agents, num_agents))
for i in range(num_agents):
for j in range(num_agents):
corr = np.corrcoef(
agent_histories[i],
agent_histories[j]
)[0, 1]
corr_matrix[i, j] = corr
fig.add_trace(
go.Heatmap(
z=corr_matrix,
colorscale='RdBu',
zmid=0,
name='Correlation'
),
row=row,
col=col
)
def _add_height_evolution_trace(
self,
fig: go.Figure,
history: List[Dict],
row: int,
col: int
) -> None:
"""Add height evolution traces to plotly figure."""
water_levels = [state['water_level'] for state in history]
time_points = list(range(len(history)))
fig.add_trace(
go.Scatter(
x=time_points,
y=water_levels,
mode='lines',
name='Water Level',
line=dict(color='red', dash='dash')
),
row=row,
col=col
)
for agent_id in history[0]['agent_heights'].keys():
heights = [
state['agent_heights'][agent_id]
for state in history
]
fig.add_trace(
go.Scatter(
x=time_points,
y=heights,
mode='lines',
name=f'Agent {agent_id}',
opacity=0.6
),
row=row,
col=col
)
def _add_height_distribution_trace(
self,
fig: go.Figure,
history: List[Dict],
row: int,
col: int
) -> None:
"""Add height distribution trace to plotly figure."""
all_heights = []
for state in history:
all_heights.extend(state['agent_heights'].values())
fig.add_trace(
go.Histogram(
x=all_heights,
nbinsx=30,
name='Height Distribution'
),
row=row,
col=col
)
def _draw_network_state(
self,
state: Dict,
ax: plt.Axes
) -> None:
"""Draw network state for animation frame."""
graph = state['network']
heights = state['agent_heights']
pos = nx.spring_layout(graph, seed=42)
nx.draw_networkx_nodes(
graph,
pos,
node_color=[heights[node] for node in graph.nodes()],
node_size=500,
cmap=self.config['network_cmap'],
ax=ax
)
nx.draw_networkx_edges(
graph,
pos,
edge_color='gray',
width=[
graph[u][v]['weight'] * 2
for u, v in graph.edges()
],
alpha=0.6,
ax=ax
)
nx.draw_networkx_labels(
graph,
pos,
{node: str(node) for node in graph.nodes()},
ax=ax
)

Просмотреть файл

@ -0,0 +1,150 @@
"""
Mathematical utilities for the Path Network simulation.
"""
import numpy as np
from typing import List, Tuple, Union
import torch
def compute_free_energy(
mu: torch.Tensor,
sigma: torch.Tensor,
sensory_input: torch.Tensor
) -> torch.Tensor:
"""
Compute the variational free energy.
Args:
mu: Expected state
sigma: Variance of state estimation
sensory_input: Observed sensory input
Returns:
Free energy value
"""
precision = 1.0 / sigma
prediction_error = sensory_input - mu
return 0.5 * (prediction_error**2 * precision + torch.log(sigma))
def generalized_coordinates_update(
coords: torch.Tensor,
dt: float,
order: int
) -> torch.Tensor:
"""
Update generalized coordinates using Taylor series expansion.
Args:
coords: Current generalized coordinates
dt: Time step
order: Order of Taylor expansion
Returns:
Updated generalized coordinates
"""
new_coords = coords.clone()
for i in range(order - 1):
new_coords[i] += coords[i + 1] * dt
return new_coords
def compute_correlation_matrix(
time_series: List[np.ndarray]
) -> np.ndarray:
"""
Compute correlation matrix between multiple time series.
Args:
time_series: List of time series data
Returns:
Correlation matrix
"""
n = len(time_series)
corr_matrix = np.zeros((n, n))
for i in range(n):
for j in range(n):
corr_matrix[i, j] = np.corrcoef(time_series[i], time_series[j])[0, 1]
return corr_matrix
def compute_prediction_error_metrics(
predictions: np.ndarray,
observations: np.ndarray
) -> Tuple[float, float, float]:
"""
Compute various prediction error metrics.
Args:
predictions: Predicted values
observations: Observed values
Returns:
Tuple of (MSE, MAE, RMSE)
"""
mse = np.mean((predictions - observations) ** 2)
mae = np.mean(np.abs(predictions - observations))
rmse = np.sqrt(mse)
return mse, mae, rmse
def sigmoid_scale(
x: Union[float, np.ndarray],
scale: float = 1.0,
offset: float = 0.0
) -> Union[float, np.ndarray]:
"""
Apply sigmoid scaling to a value or array.
Args:
x: Input value(s)
scale: Scaling factor
offset: Offset value
Returns:
Scaled value(s)
"""
return 1 / (1 + np.exp(-(x - offset) / scale))
def compute_entropy(
probabilities: np.ndarray,
epsilon: float = 1e-10
) -> float:
"""
Compute the entropy of a probability distribution.
Args:
probabilities: Probability distribution
epsilon: Small value to avoid log(0)
Returns:
Entropy value
"""
# Ensure probabilities sum to 1 and are positive
p = np.clip(probabilities, epsilon, 1.0)
p = p / np.sum(p)
return -np.sum(p * np.log(p))
def exponential_moving_average(
data: np.ndarray,
alpha: float = 0.1
) -> np.ndarray:
"""
Compute exponential moving average of a time series.
Args:
data: Input time series
alpha: Smoothing factor (0 < alpha < 1)
Returns:
Smoothed time series
"""
result = np.zeros_like(data)
result[0] = data[0]
for t in range(1, len(data)):
result[t] = alpha * data[t] + (1 - alpha) * result[t-1]
return result

Просмотреть файл

@ -0,0 +1,233 @@
"""
Visualization utilities for the Path Network simulation.
Provides real-time visualization of the network topology and agent states.
"""
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import seaborn as sns
from matplotlib.animation import FuncAnimation
from typing import Dict, List, Optional, Tuple
from ..core.network import PathNetwork
class NetworkVisualizer:
"""
Visualizes the network topology and agent states in real-time.
"""
def __init__(self, figsize: Tuple[int, int] = (15, 10)):
# Set up style
try:
sns.set_style("whitegrid")
sns.set_context("notebook", font_scale=1.2)
except Exception as e:
print(f"Warning: Could not set seaborn style: {e}")
plt.style.use('default')
self.fig = plt.figure(figsize=figsize)
# Create subplots
self.network_ax = self.fig.add_subplot(221)
self.heights_ax = self.fig.add_subplot(222)
self.history_ax = self.fig.add_subplot(212)
# Initialize data storage
self.water_level_history: List[float] = []
self.height_histories: Dict[int, List[float]] = {}
self.time_points: List[int] = []
# Style settings
self.fig.tight_layout(pad=3.0)
def update(self, network: PathNetwork, water_level: float) -> None:
"""
Update the visualization with current network state.
Args:
network: The current network state
water_level: Current global water level
"""
self._clear_axes()
# Get current network state
graph, heights = network.get_network_state()
# Update histories
self.water_level_history.append(water_level)
self.time_points.append(len(self.water_level_history))
for node_id, height in heights.items():
if node_id not in self.height_histories:
self.height_histories[node_id] = []
self.height_histories[node_id].append(height)
# Draw network topology
self._draw_network(graph, heights)
# Draw current heights
self._draw_heights(heights, water_level)
# Draw history
self._draw_history()
# Update display
try:
plt.draw()
plt.pause(0.01)
except Exception as e:
print(f"Warning: Could not update display in real-time: {e}")
def _clear_axes(self) -> None:
"""Clear all axes for redrawing."""
for ax in [self.network_ax, self.heights_ax, self.history_ax]:
ax.clear()
def _draw_network(self, graph: nx.DiGraph, heights: Dict[int, float]) -> None:
"""Draw the network topology with node colors based on heights."""
self.network_ax.set_title('Network Topology')
# Calculate node colors based on heights
vmin = min(heights.values())
vmax = max(heights.values())
node_colors = [heights[node] for node in graph.nodes()]
# Calculate edge weights for width
edge_weights = [
graph[u][v]['weight'] * 2 for u, v in graph.edges()
]
# Draw the network
pos = nx.spring_layout(graph, seed=42) # Fixed seed for consistency
nx.draw_networkx_nodes(
graph,
pos,
node_color=node_colors,
node_size=500,
cmap='coolwarm',
vmin=vmin,
vmax=vmax,
ax=self.network_ax
)
nx.draw_networkx_edges(
graph,
pos,
edge_color='gray',
width=edge_weights,
alpha=0.6,
ax=self.network_ax
)
nx.draw_networkx_labels(
graph,
pos,
{node: str(node) for node in graph.nodes()},
ax=self.network_ax
)
# Add colorbar
sm = plt.cm.ScalarMappable(
cmap='coolwarm',
norm=plt.Normalize(vmin=vmin, vmax=vmax)
)
plt.colorbar(sm, ax=self.network_ax, label='Height')
def _draw_heights(
self,
heights: Dict[int, float],
water_level: float
) -> None:
"""Draw current agent heights and water level."""
self.heights_ax.set_title('Current Heights')
# Plot agent heights
nodes = list(heights.keys())
height_values = [heights[node] for node in nodes]
# Create bar plot with custom colors based on height
colors = plt.cm.coolwarm(
plt.Normalize()(height_values)
)
self.heights_ax.bar(
nodes,
height_values,
alpha=0.6,
color=colors
)
# Plot water level
self.heights_ax.axhline(
y=water_level,
color='r',
linestyle='--',
label='Water Level'
)
self.heights_ax.set_xlabel('Agent ID')
self.heights_ax.set_ylabel('Height')
self.heights_ax.legend()
# Set y-limits with some padding
ymin = min(min(height_values), water_level)
ymax = max(max(height_values), water_level)
padding = (ymax - ymin) * 0.1
self.heights_ax.set_ylim(ymin - padding, ymax + padding)
def _draw_history(self) -> None:
"""Draw the history of water level and agent heights."""
self.history_ax.set_title('Height History')
# Create color palette for agents
num_agents = len(self.height_histories)
colors = plt.cm.viridis(np.linspace(0, 1, num_agents))
# Plot agent height histories
for (node_id, history), color in zip(
self.height_histories.items(), colors
):
self.history_ax.plot(
self.time_points[-len(history):],
history,
alpha=0.6,
label=f'Agent {node_id}',
color=color
)
# Plot water level history
self.history_ax.plot(
self.time_points,
self.water_level_history,
'r--',
label='Water Level',
linewidth=2,
alpha=0.8
)
self.history_ax.set_xlabel('Time Step')
self.history_ax.set_ylabel('Height')
# Adjust legend
self.history_ax.legend(
bbox_to_anchor=(1.05, 1),
loc='upper left',
borderaxespad=0.,
ncol=2
)
def save(self, filename: str) -> None:
"""Save the current figure to a file."""
try:
# Adjust layout before saving
self.fig.tight_layout()
self.fig.savefig(
filename,
bbox_inches='tight',
dpi=300,
facecolor='white',
edgecolor='none'
)
except Exception as e:
print(f"Warning: Could not save figure to {filename}: {e}")
def close(self) -> None:
"""Close the figure."""
plt.close(self.fig)

19
Things/Path_Network/requirements.txt Обычный файл
Просмотреть файл

@ -0,0 +1,19 @@
numpy>=1.21.0
networkx>=2.6.0
scipy>=1.7.0
matplotlib>=3.4.0
torch>=1.9.0
pandas>=1.3.0
seaborn>=0.11.0
pytest>=6.2.0
black>=21.5b2
mypy>=0.910
pylint>=2.8.0
pyyaml>=5.4.1
plotly>=5.3.0
imageio>=2.9.0
tqdm>=4.62.0
scikit-learn>=0.24.0
ipywidgets>=7.6.0
moviepy>=1.0.3
mayavi>=4.7.0

166
Things/Path_Network/run_simulation.sh Исполняемый файл
Просмотреть файл

@ -0,0 +1,166 @@
#!/bin/bash
# Exit on error
set -e
# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to setup Python virtual environment
setup_venv() {
echo "Setting up Python virtual environment..."
# Check if python3-venv is installed
if command_exists apt-get; then
if ! dpkg -l | grep -q python3-venv; then
echo "Installing python3-venv..."
sudo apt-get update
sudo apt-get install -y python3-venv
fi
fi
# Create and activate virtual environment
if [ ! -d "venv" ]; then
python3 -m venv venv
fi
source venv/bin/activate
# Upgrade pip
python3 -m pip install --upgrade pip
}
# Function to install system dependencies
install_system_deps() {
echo "Checking system dependencies..."
if command_exists apt-get; then
echo "Debian/Ubuntu system detected"
sudo apt-get update
sudo apt-get install -y \
python3-tk \
python3-qt5 \
libxcb-cursor0 \
python3-dev \
build-essential \
ffmpeg \
python3-vtk7 \
libvtk7-dev \
mayavi2 \
python3-mayavi \
imagemagick
elif command_exists dnf; then
echo "Fedora system detected"
sudo dnf install -y \
python3-tkinter \
python3-qt5 \
xcb-util-cursor \
python3-devel \
gcc \
ffmpeg \
vtk \
vtk-devel \
mayavi \
ImageMagick
elif command_exists pacman; then
echo "Arch system detected"
sudo pacman -S --noconfirm \
python-tk \
qt5-base \
xcb-util-cursor \
python-dev \
base-devel \
ffmpeg \
vtk \
mayavi \
imagemagick
else
echo "Warning: Unknown package manager. Please install dependencies manually if needed."
fi
# Configure ImageMagick to allow PDF operations if needed
if command_exists convert; then
sudo sed -i 's/rights="none" pattern="PDF"/rights="read|write" pattern="PDF"/' /etc/ImageMagick-6/policy.xml 2>/dev/null || true
sudo sed -i 's/rights="none" pattern="PDF"/rights="read|write" pattern="PDF"/' /etc/ImageMagick-7/policy.xml 2>/dev/null || true
fi
}
# Function to install Python dependencies
install_python_deps() {
echo "Installing Python dependencies..."
# First install numpy and other core dependencies
pip install numpy wheel setuptools
# Install VTK separately if needed
if ! python3 -c "import vtk" 2>/dev/null; then
pip install vtk
fi
# Then install other dependencies
pip install -r requirements.txt
}
# Function to check installation
check_installation() {
echo "Checking installation..."
# Try importing required packages
python3 -c "
import numpy
import torch
import matplotlib
import networkx
import seaborn
import plotly
import imageio
import vtk
import mayavi.mlab
print('All required packages are installed correctly!')
"
}
# Function to check output directory
check_output_dir() {
echo "Checking output directory..."
if [ ! -d "output" ]; then
mkdir output
fi
# Test write permissions
if ! touch output/.test 2>/dev/null; then
echo "Error: Cannot write to output directory"
exit 1
fi
rm output/.test
}
# Function to run the simulation
run_simulation() {
echo "Running simulation..."
python example.py
}
# Main execution
echo "=== Path Network Simulation Setup ==="
# Setup virtual environment
setup_venv
# Install dependencies
install_system_deps
install_python_deps
# Check installation
check_installation
# Check output directory
check_output_dir
# Run simulation
echo "=== Starting Simulation ==="
run_simulation
echo "=== Simulation Complete ==="
echo "Check the 'output' directory for results and visualizations."

68
ant_colony/agents/nestmate.py Обычный файл
Просмотреть файл

@ -0,0 +1,68 @@
"""
Nestmate agent implementation using active inference.
"""
from enum import Enum
import numpy as np
from typing import Dict, Optional, List
from dataclasses import dataclass, field
from ant_colony.environment.world import Position, Resource
class TaskType(Enum):
"""Types of tasks an ant can perform."""
FORAGING = 'foraging'
MAINTENANCE = 'maintenance'
NURSING = 'nursing'
DEFENSE = 'defense'
EXPLORATION = 'exploration'
@dataclass
class Belief:
"""Represents an agent's beliefs about the world state."""
food_location: Optional[Position] = None
nest_location: Optional[Position] = None
danger_level: float = 0.0
energy_level: float = 1.0
task_urgency: Dict[TaskType, float] = field(default_factory=lambda: {task: 0.0 for task in TaskType})
class Nestmate:
"""
Implementation of an ant agent using active inference principles.
"""
def __init__(self, config: dict):
"""Initialize the agent."""
self.config = config
# Initialize beliefs first
self.beliefs = Belief()
# Physical state
self.position = None
self.orientation = 0.0
self.speed = 0.0
self.energy = config['physical']['energy']['initial']
# Carrying state
self.carrying: Optional[Resource] = None
# Task state
self.current_task = TaskType.EXPLORATION
self.task_time = 0.0
# Sensory state
self.observations = {
'pheromones': {},
'resources': [],
'nestmates': [],
'terrain': None
}
# Internal model
self.preferences = {task: 1.0 for task in TaskType}
# Learning parameters
self.learning_rate = config['behavior'].get('learning_rate', 0.1)
self.exploration_rate = config['behavior'].get('exploration_rate', 0.2)
# ... existing code ...

18
ant_colony/simulation.py Обычный файл
Просмотреть файл

@ -0,0 +1,18 @@
"""
Main simulation coordinator for ant colony simulation.
"""
import yaml
import numpy as np
from pathlib import Path
from typing import Optional
import matplotlib
matplotlib.use('Agg') # Use non-GUI backend
import matplotlib.pyplot as plt
from ant_colony.environment import World
from ant_colony.colony import Colony
from ant_colony.visualization import ColonyVisualizer
from ant_colony.utils.data_collection import DataCollector
# ... existing code ...

Просмотреть файл

@ -16,16 +16,16 @@ pomegranate>=0.14.0
matplotlib>=3.4.0
seaborn>=0.11.0
networkx>=2.6.0
plotly>=5.1.0
plotly>=5.3.0
# Data Processing
pyyaml>=5.4.0
pyyaml>=5.4.1
jinja2>=3.0.0
markdown>=3.3.0
# Testing and Development
pytest>=6.2.0
black>=21.6b0
black>=21.5b2
flake8>=3.9.0
mypy>=0.910
@ -34,7 +34,7 @@ sphinx>=4.0.0
sphinx-rtd-theme>=0.5.0
# Utilities
tqdm>=4.61.0
tqdm>=4.62.0
click>=8.0.0
python-dotenv>=0.19.0
@ -44,4 +44,8 @@ hyperopt>=0.2.5
# Monitoring and Logging
wandb>=0.12.0
mlflow>=1.19.0
mlflow>=1.19.0
# Additional dependencies
pylint>=2.8.0
imageio>=2.9.0