cognitive/tests/visualization/test_continuous_generic.py
Daniel Ari Friedman 621b613624 Push
2025-02-07 13:20:57 -08:00

393 строки
13 KiB
Python

"""Comprehensive visualization tests for continuous-time active inference."""
import pytest
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
import sys
import os
# Add the parent directory to the Python path
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from Things.Continuous_Generic.continuous_generic import ContinuousActiveInference
from Things.Continuous_Generic.visualization import ContinuousVisualizer
# Test output directory
TEST_OUTPUT_DIR = Path("Output/tests/visualization")
TEST_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
@pytest.fixture
def visualizer():
"""Create test visualizer."""
return ContinuousVisualizer(TEST_OUTPUT_DIR)
@pytest.fixture
def agent():
"""Create test agent with rich dynamics."""
return ContinuousActiveInference(
n_states=2,
n_obs=2,
n_orders=3,
dt=0.001,
alpha=0.1
)
def generate_oscillatory_data(agent, n_steps=1000):
"""Generate oscillatory test data."""
# Initialize with harmonic oscillator state
agent.state.belief_means[:, 0] = np.array([0.1, 0.0]) # Position
agent.state.belief_means[:, 1] = np.array([0.0, 0.1]) # Velocity
agent.state.belief_means[:, 2] = np.array([-0.1, 0.0]) # Acceleration
history = {
'belief_means': [],
'belief_precisions': [],
'free_energy': [],
'actions': [],
'time': []
}
for t in range(n_steps):
# Record state
history['belief_means'].append(agent.state.belief_means.copy())
history['belief_precisions'].append(agent.state.belief_precisions.copy())
history['time'].append(t * agent.dt)
# Generate observation (harmonic motion)
obs = agent._sensory_mapping(agent.state.belief_means)
# Take step
action, F = agent.step(obs)
history['actions'].append(action)
history['free_energy'].append(F)
return history
@pytest.mark.visualization
class TestBasicVisualization:
"""Test basic visualization capabilities."""
def test_belief_evolution(self, agent, visualizer):
"""Test belief evolution visualization."""
history = generate_oscillatory_data(agent)
# Basic belief evolution plot
visualizer.plot_belief_evolution(
history['belief_means'],
history['belief_precisions'],
TEST_OUTPUT_DIR / 'belief_evolution.png'
)
# Enhanced belief evolution with uncertainty
plt.figure(figsize=(15, 10))
means = np.array([b[:,0] for b in history['belief_means']])
precisions = np.array([b[:,0] for b in history['belief_precisions']])
time = np.array(history['time'])
for i in range(means.shape[1]):
plt.plot(time, means[:,i], label=f'State {i+1}')
std = 1.0 / np.sqrt(precisions[:,i])
plt.fill_between(time, means[:,i] - 2*std, means[:,i] + 2*std, alpha=0.2)
plt.title('Belief Evolution with Uncertainty')
plt.xlabel('Time')
plt.ylabel('State Value')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'belief_evolution_with_uncertainty.png')
plt.close()
@pytest.mark.visualization
class TestPhaseSpaceVisualization:
"""Test phase space visualization capabilities."""
def test_phase_space_plots(self, agent, visualizer):
"""Test phase space visualization."""
history = generate_oscillatory_data(agent)
# Basic phase space
plt.figure(figsize=(10, 10))
means = np.array([b[:,0] for b in history['belief_means']])
plt.plot(means[:,0], means[:,1], 'b-', alpha=0.5)
plt.plot(means[0,0], means[0,1], 'go', label='Start')
plt.plot(means[-1,0], means[-1,1], 'ro', label='End')
# Add velocity vectors
step = len(means) // 20 # Plot 20 vectors
for i in range(0, len(means), step):
plt.arrow(means[i,0], means[i,1],
means[i,1]*0.1, -means[i,0]*0.1,
head_width=0.01, head_length=0.02,
fc='k', ec='k', alpha=0.5)
plt.title('Phase Space with Velocity Vectors')
plt.xlabel('Position')
plt.ylabel('Velocity')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'phase_space_with_vectors.png')
plt.close()
# Energy contours in phase space
plt.figure(figsize=(10, 10))
x = np.linspace(-0.15, 0.15, 100)
y = np.linspace(-0.15, 0.15, 100)
X, Y = np.meshgrid(x, y)
E = 0.5 * (X**2 + Y**2) # Harmonic oscillator energy
plt.contour(X, Y, E, levels=20, cmap='viridis', alpha=0.3)
plt.plot(means[:,0], means[:,1], 'r-', alpha=0.7, label='Trajectory')
plt.colorbar(label='Energy')
plt.title('Phase Space with Energy Contours')
plt.xlabel('Position')
plt.ylabel('Velocity')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'phase_space_energy.png')
plt.close()
@pytest.mark.visualization
class TestEnergyVisualization:
"""Test energy-related visualization capabilities."""
def test_energy_plots(self, agent, visualizer):
"""Test energy visualization."""
history = generate_oscillatory_data(agent)
means = np.array([b[:,0] for b in history['belief_means']])
time = np.array(history['time'])
# Calculate energies
ke = 0.5 * means[:,1]**2 # Kinetic energy
pe = 0.5 * means[:,0]**2 # Potential energy
total = ke + pe
# Energy components plot
plt.figure(figsize=(15, 5))
plt.plot(time, ke, 'r-', label='Kinetic', alpha=0.7)
plt.plot(time, pe, 'b-', label='Potential', alpha=0.7)
plt.plot(time, total, 'k--', label='Total')
plt.title('Energy Components')
plt.xlabel('Time')
plt.ylabel('Energy')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'energy_components.png')
plt.close()
# Energy ratio plot
plt.figure(figsize=(15, 5))
plt.plot(time, ke/pe, 'g-', label='KE/PE Ratio')
plt.axhline(y=1.0, color='r', linestyle='--', label='Equipartition')
plt.title('Kinetic to Potential Energy Ratio')
plt.xlabel('Time')
plt.ylabel('KE/PE Ratio')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'energy_ratio.png')
plt.close()
@pytest.mark.visualization
class TestGeneralizedCoordinates:
"""Test visualization of generalized coordinates relationships."""
def test_coordinate_relationships(self, agent, visualizer):
"""Test generalized coordinates visualization."""
history = generate_oscillatory_data(agent)
# Plot relationships between orders
visualizer.plot_generalized_coordinates_relationships(
history['belief_means'],
np.array(history['time']),
TEST_OUTPUT_DIR / 'generalized_coordinates.png'
)
# Create animation
visualizer.save_animation(
history,
TEST_OUTPUT_DIR / 'belief_animation.gif',
fps=30
)
@pytest.mark.visualization
class TestFreeEnergyLandscape:
"""Test visualization of free energy landscape."""
def test_free_energy_landscape(self, agent, visualizer):
"""Test free energy landscape visualization."""
# Generate grid of beliefs
x = np.linspace(-0.2, 0.2, 50)
y = np.linspace(-0.2, 0.2, 50)
X, Y = np.meshgrid(x, y)
F = np.zeros_like(X)
# Calculate free energy for each point
for i in range(len(x)):
for j in range(len(y)):
agent.state.belief_means[:,0] = np.array([X[i,j], Y[i,j]])
obs = agent._sensory_mapping(agent.state.belief_means)
F[i,j] = agent._compute_free_energy(obs, agent.state.belief_means)
# Plot free energy landscape
plt.figure(figsize=(12, 10))
plt.contour(X, Y, F, levels=30, cmap='viridis')
plt.colorbar(label='Free Energy')
# Add minimum point
min_idx = np.unravel_index(np.argmin(F), F.shape)
plt.plot(X[min_idx], Y[min_idx], 'r*', markersize=15, label='Minimum')
plt.title('Free Energy Landscape')
plt.xlabel('State 1')
plt.ylabel('State 2')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'free_energy_landscape.png')
plt.close()
@pytest.mark.visualization
class TestActionVisualization:
"""Test visualization of action selection."""
def test_action_plots(self, agent, visualizer):
"""Test action visualization."""
history = generate_oscillatory_data(agent)
actions = np.array(history['actions'])
time = np.array(history['time'])
# Action evolution
plt.figure(figsize=(15, 5))
for i in range(actions.shape[1]):
plt.plot(time, actions[:,i], label=f'Action {i+1}')
plt.title('Action Evolution')
plt.xlabel('Time')
plt.ylabel('Action Value')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'action_evolution.png')
plt.close()
# Action phase space
if actions.shape[1] >= 2:
plt.figure(figsize=(10, 10))
plt.plot(actions[:,0], actions[:,1], 'b-', alpha=0.5)
plt.plot(actions[0,0], actions[0,1], 'go', label='Start')
plt.plot(actions[-1,0], actions[-1,1], 'ro', label='End')
plt.title('Action Phase Space')
plt.xlabel('Action 1')
plt.ylabel('Action 2')
plt.legend()
plt.grid(True)
plt.savefig(TEST_OUTPUT_DIR / 'action_phase_space.png')
plt.close()
@pytest.mark.visualization
class TestSummaryVisualization:
"""Test comprehensive summary visualization."""
def test_summary_plots(self, agent, visualizer):
"""Test summary visualization."""
history = generate_oscillatory_data(agent)
# Create comprehensive summary plot
visualizer.create_summary_plot(
history,
TEST_OUTPUT_DIR / 'summary.png'
)
# Create detailed diagnostic plots
plt.figure(figsize=(20, 15))
# Belief evolution with uncertainty
plt.subplot(3, 2, 1)
means = np.array([b[:,0] for b in history['belief_means']])
precisions = np.array([b[:,0] for b in history['belief_precisions']])
time = np.array(history['time'])
for i in range(means.shape[1]):
plt.plot(time, means[:,i], label=f'State {i+1}')
std = 1.0 / np.sqrt(precisions[:,i])
plt.fill_between(time, means[:,i] - 2*std, means[:,i] + 2*std, alpha=0.2)
plt.title('Belief Evolution')
plt.xlabel('Time')
plt.ylabel('State Value')
plt.legend()
plt.grid(True)
# Phase space
plt.subplot(3, 2, 2)
plt.plot(means[:,0], means[:,1], 'b-', alpha=0.5)
plt.plot(means[0,0], means[0,1], 'go', label='Start')
plt.plot(means[-1,0], means[-1,1], 'ro', label='End')
plt.title('Phase Space')
plt.xlabel('Position')
plt.ylabel('Velocity')
plt.legend()
plt.grid(True)
# Free energy
plt.subplot(3, 2, 3)
plt.plot(time, history['free_energy'])
plt.title('Free Energy Evolution')
plt.xlabel('Time')
plt.ylabel('Free Energy')
plt.grid(True)
# Actions
plt.subplot(3, 2, 4)
actions = np.array(history['actions'])
for i in range(actions.shape[1]):
plt.plot(time, actions[:,i], label=f'Action {i+1}')
plt.title('Action Evolution')
plt.xlabel('Time')
plt.ylabel('Action Value')
plt.legend()
plt.grid(True)
# Energy components
plt.subplot(3, 2, 5)
ke = 0.5 * means[:,1]**2
pe = 0.5 * means[:,0]**2
total = ke + pe
plt.plot(time, ke, 'r-', label='Kinetic', alpha=0.7)
plt.plot(time, pe, 'b-', label='Potential', alpha=0.7)
plt.plot(time, total, 'k--', label='Total')
plt.title('Energy Components')
plt.xlabel('Time')
plt.ylabel('Energy')
plt.legend()
plt.grid(True)
# Generalized coordinates relationships
plt.subplot(3, 2, 6)
dt = time[1] - time[0]
dx = np.diff(means[:,0]) / dt
v = means[:-1,1]
plt.scatter(v, dx, alpha=0.5, s=10)
lims = [
min(plt.xlim()[0], plt.ylim()[0]),
max(plt.xlim()[1], plt.ylim()[1])
]
plt.plot(lims, lims, 'r--', alpha=0.5)
plt.title('Velocity vs Position Derivative')
plt.xlabel('Velocity')
plt.ylabel('Position Derivative')
plt.grid(True)
plt.tight_layout()
plt.savefig(TEST_OUTPUT_DIR / 'detailed_summary.png')
plt.close()