Informational-Processual Monism (IPM) – Scientific Core

 

Informational-Processual Monism

Scientific Core — Metrics, Estimators & Falsification

 

 

Author: Taotuner

Date: June 2026

Published on Zenodo
https://doi.org/10.5281/zenodo.20582318

 

Companion to: IPM Philosophical Core (2026) — Taotuner

For the ontological interpretation of these regularities, see the Philosophical Core.

Simulation families: Lack Kernel, Spectral Experiment, IPM Protocol, Collective Regimes Framework.

 


 

1. Empirical Regularities (R1–R3)

Simulation families (Lack Kernel, Spectral Experiment, IPM Protocol, Collective Regimes) consistently produced three reproducible patterns under the tested conditions (100 runs, ε=0.15, bins=30, max_lag=20).

 

R1 — Lack-Degradation

Higher perturbation → lower coherence. Coherence drops from 0.97 to 0.55 as noise increases from 0.02 to 1.2. In the IPM Protocol, Φ* drops approximately 16% under thermal perturbation before recovering.

 

1.1 Minimal Model of Lack Dynamics

A one-dimensional system with slow memory mₜ follows:

 

xₜ₊₁ = xₜ + λ(mₜ − xₜ) + η,     η ~ N(0, σ²)

 

Parameter

Definition

Value / Domain

xₜ

System state at time t

mₜ

Memory (slow reference trajectory)

λ

Coupling pull toward memory

0.15, illustrative value used in the minimal model (stable domain: 0 < λ ≤ 1)

η

Stochastic perturbation

Gaussian, zero mean, variance σ², i.i.d. per step

σ

Perturbation intensity

Varied across runs (0.02 – 1.2)

 

Coherence is defined as:

 

cₜ = exp(−|xₜ − mₜ|)

 

This model is not proposed as a universal law. It serves as a minimal dynamical illustration of the Lack–Coupling relationship underlying R1.

Result: as σ increases, mean coherence decreases monotonically — illustrating R1. The term (mₜ − xₜ) operationalizes Lack as deviation between current state and memory; λ governs Coupling intensity. Φ* and 𝒞 are scalar compressions derived from the statistics of this process.

 

R2 — Integration-Persistence

Higher integration → longer persistence under perturbation. Higher integration yields longer metastability under moderate perturbation.

 

R3 — Observed Clustering Under Specific Projections

Under the tested observer projections (CCI, DIG-proxy, LMS) and simulation conditions, three coupling regimes formed separable clusters. Whether this reflects a property of the systems or an artifact of the chosen projections is not determined. Generalization not established.

 

These are computational regularities, not universal invariants.

 

2. Estimators

One Possible Regime Marker: Φ*

As an example of a scalar compression, define:

 

Φ*(t) = [ε(t) + h(t)] / [1 + D(t)]

 

Term

Definition

ε(t)

k-NN prediction error in embedded space (Takens)

h(t)

Local transition entropy

D(t)

Penalty combining Lyapunov exponent + correlation dimension

 

This is one functional form among many that satisfy the same boundary conditions (monotonicity in ε and h, rigidity penalty, chaos penalty, interior peak). Other compressions are possible. No claim is made that this specific form preserves all relevant information or that ε, h, D share a common dimension.

 

Temporal Compressibility: 𝒞

A complementary estimator based on inter-event intervals:

 

𝒞 = E[ log( ψ(τᵢ | Hᵢ₋¹) / ψ(τᵢ) ) ]

 

τᵢ = inter-event interval. 𝒞 is scale-dependent (discretization, resolution). Under specific conditions it reduces to transfer entropy, mutual information rate, or excess entropy.

 

3. Falsification (Programmatic)

The framework is weakened by:

       Systematic non-replication of R1–R3 in new simulation families or labs.

       Loss of inverted-U pattern under parameter variation.

       Φ* > 0 in thermodynamic equilibrium (no gradients).

 

4. Known Limitations

Limitation

Description

Mitigation / Direction

Empirical base

Four simulation families only

Independent validation on real-world data required

Φ* embedding dependence

Parameters (dimension, delay, k) affect stability

Sensitivity analysis required per application

𝒞 and long-range memory

Fails for non-stationary long-range memory

Use block entropy estimators, Lempel-Ziv complexity, or fractal dimension methods — domain-specific choice

Cross-domain generalization

Not established

Remains a working hypothesis

 

5. The 𝒞 Reduction

Under specific stationarity and process conditions, 𝒞 reduces to known information-theoretic quantities:

 

Condition

Reduces To

History = immediate past

Transfer entropy

Markovian order k

Mutual information rate (standard form)

Stationary, ergodic

Excess entropy

Otherwise

𝒞 is a scale-dependent estimator, not an invariant.

 

References

Bateson, G. (1972). Steps to an ecology of mind. Chandler.

Friston, K. (2010). The free-energy principle: A unified brain theory? Nature Reviews Neuroscience, 11(2), 127–138.

Kondepudi, D., & Prigogine, I. (1998). Modern thermodynamics. Wiley.

Maturana, H. R., & Varela, F. J. (1980). Autopoiesis and cognition. D. Reidel.

Prigogine, I., & Stengers, I. (1984). Order out of chaos. Bantam.

Simondon, G. (2020). Individuation in light of notions of form and information. University of Minnesota Press.

Tononi, G. (2004). An information integration theory of consciousness. BMC Neuroscience, 5, 42.

IPM Protocol: Dynamic Regime Sensitivity Analysis

IPM Protocol: Dynamic Regime Sensitivity Analysis

Author: Taotuner
Based on: IPM Scientific Core
DOI: https://doi.org/10.5281/zenodo.20477438


Abstract

This experiment analyzes how dynamical metrics respond to controlled perturbations in coupled systems. A controlled increase in thermal noise is applied during a specific interval. The experiment measures three projection functionals: Φ* (spectral organization), DIG (temporal autocorrelation), and coherence (self-model alignment). Results show consistent metric changes during perturbation (Φ*: -15.9%, DIG: -25.3%) and correlation (0.594) between Φ* and DIG. The experiment demonstrates co-responsiveness to global noise, not dimensional independence or epistemological validation of metrics.

Note on scope: This is a preliminary sensitivity analysis. The observed correlation may arise from shared dependence on global noise, system energy, or latent field dynamics. This experiment does not distinguish these possibilities.


1. What Is This Experiment?

The experiment analyzes how dynamical metrics respond to controlled perturbations in a coupled system. It addresses a methodological question: do these metrics change consistently when the dynamical regime shifts?

The approach: apply a controlled perturbation and observe whether metrics change consistently. Consistent response demonstrates co-responsiveness to regime shifts.


2. What the Experiment Contains

The code simulates a minimal universe with two types of systems that evolve together:

ComponentDescription
Continuous spectral fieldThe collective substrate. Dynamics governed by reaction-diffusion PDE in Fourier space.
Multiple discrete agents (toy model)Each with internal state, synaptic plasticity (Hebbian learning), and self-model.

The coupling: The two scales interact — field influences agents, agents influence field. Perturbation affects both scales simultaneously.


3. What Is the Perturbation?

The perturbation is a controlled increase in thermal noise applied during a specific interval (turns 700-820). During this period, systems continue to evolve but temporarily lose coherence and predictability.

The hypothesis: If metrics are sensitive to dynamical regime shifts, they must change consistently during perturbation.


4. The Metrics (Projection Functionals)

MetricDefinitionWhat It Captures
Φ (Phi Asterisk)*Φ* = Φ - λ·Δ, where Φ = 1 - spectral entropySpectral organization (spatial structure)
DIG (Dynamical Independence Gap)Autocorrelation (lag-1 and lag-2) of agent statesTemporal memory / predictability
CoherenceAlignment between agent state and self-modelSelf-model stability

Important note: Φ* and DIG are low-order statistics on correlated states of the same system. Their correlation (~0.59) may arise from:

  • Shared dependence on the same driver (global noise)

  • Shared dependence on system energy/entropy

  • Different filtering of the same latent field

This experiment does not distinguish these possibilities.


5. Limitations of This Experiment (Explicit)

LimitationImplication
Global noise onlyDoes not separate thermal sensitivity from structural sensitivity
Correlation by constructionMetrics may correlate because they all depend on system energy/entropy
No structural variationTopology of coupling is fixed; only noise intensity varies
Low-order statisticsBoth metrics are filtered versions of the same underlying dynamics
Preliminary scopeDemonstrates co-responsiveness, not dimensional independence

What this experiment does NOT show:

  • That metrics capture "integration" rather than just global noise

  • Separation between thermal and structural regimes

  • That Φ* and DIG measure independent dimensions of the system

  • Epistemological adequacy across contexts


6. How to Run It

Run in any Python environment with: numpy, torch, matplotlib.

Main parameters:

ParameterDescription
temperatureBackground noise level
lack_strengthPerturbation intensity
lack_durationDuration of perturbation
lambda_starλ factor for Φ*

Experiment configuration:

  • Runs for 1500 turns

  • Perturbation from turns 700 to 820

  • Data collected every 20 turns


7. Code Implementation

python
import numpy as np
import torch
import torch.fft as fft
import matplotlib.pyplot as plt
from collections import deque
import warnings
warnings.filterwarnings('ignore')

# =============================================================================
# CONSTANTS
# =============================================================================

CONSTANTS = {
    'temperature': 0.006,
    'gamma': 0.10,
    'D': 0.07,
    'coupling_strength': 0.012,
    'coupling_max': 0.5,
    'memory_rate': 0.95,
    'self_rate': 0.94,
    'lack_strength': 2.0,
    'lack_noise_boost': 6.0,
    'lack_duration': 120,
    'lambda_star': 0.25,
    'phi_star_min': 0.10,
    'phi_star_max': 0.70,
    'recovery_rate': 0.98,
}

# =============================================================================
# HYBRID SYSTEM
# =============================================================================

class HybridSystem:
    def __init__(self, system_id, grid_size=16, n_degrees=12, device='cpu'):
        self.id = system_id
        self.grid_size = grid_size
        self.n_degrees = n_degrees
        self.device = device
        
        # Spectral field
        self.field = self._create_structured_field()
        
        # Toy model agents
        self.v = torch.randn(n_degrees, device=device) * 0.5
        self.W = torch.randn(n_degrees, n_degrees, device=device) * 0.3 / np.sqrt(n_degrees)
        self.W.fill_diagonal_(0)
        
        # Memory and Self
        self.memory = self.field.clone()
        self.self_model = self.field.clone()
        self.auto_modelo = self.v.clone()
        
        # Perturbation state
        self.lack_memory = 0.0
        
        # Metrics
        self.coherence = 0.6
        self.phi = 0.0
        self.delta = 0.0
        self.phi_star = 0.4
        self.dig = 0.5
        
        # History
        self.v_history = deque(maxlen=80)
        self.phi_star_history = deque(maxlen=15)
        self.dig_history = deque(maxlen=15)
        
        self._setup_spectral_space()
    
    def _create_structured_field(self):
        x = torch.linspace(-2, 2, self.grid_size, device=self.device)
        y = torch.linspace(-2, 2, self.grid_size, device=self.device)
        z = torch.linspace(-2, 2, self.grid_size, device=self.device)
        
        X, Y, Z = torch.meshgrid(x, y, z, indexing='ij')
        
        field = (torch.sin(X * 0.6) * torch.cos(Y * 0.5) + 
                 torch.sin(Y * 0.5) * torch.cos(Z * 0.4) +
                 torch.sin(Z * 0.7) * torch.cos(X * 0.5)) * 0.5
        
        field += torch.randn_like(field) * 0.08
        return field
    
    def _setup_spectral_space(self):
        kx = torch.fft.fftfreq(self.grid_size, device=self.device) * 2 * np.pi
        ky = torch.fft.fftfreq(self.grid_size, device=self.device) * 2 * np.pi
        kz = torch.fft.rfftfreq(self.grid_size, device=self.device) * 2 * np.pi
        
        KX, KY, KZ = torch.meshgrid(kx, ky, kz, indexing='ij')
        self.K2 = KX**2 + KY**2 + KZ**2
        
        cutoff = self.grid_size // 3
        self.mask = ((torch.abs(KX) < cutoff) & 
                     (torch.abs(KY) < cutoff) & 
                     (torch.abs(KZ) < cutoff)).float()
        
        x = torch.linspace(-1, 1, self.grid_size, device=self.device)
        X, Y, Z = torch.meshgrid(x, x, x, indexing='ij')
        R2 = X**2 + Y**2 + Z**2
        kernel = torch.exp(-R2 / 0.06)
        kernel = kernel / kernel.sum()
        self.kernel_hat = fft.rfftn(kernel, dim=(-3,-2,-1), norm='ortho')
    
    def spectral_evolution(self, dt=0.001, external_forcing=0.0):
        phi = self.field
        hat = fft.rfftn(phi, dim=(-3,-2,-1), norm='ortho')
        
        lap_hat = -self.K2 * hat
        diffusion = CONSTANTS['D'] * lap_hat
        dissipation = -CONSTANTS['gamma'] * hat
        
        local_mean = fft.irfftn(hat * self.kernel_hat, dim=(-3,-2,-1), norm='ortho')
        coupling_real = CONSTANTS['coupling_strength'] * (local_mean - phi)
        coupling_real = torch.clamp(coupling_real, -CONSTANTS['coupling_max'], CONSTANTS['coupling_max'])
        coupling = fft.rfftn(coupling_real, dim=(-3,-2,-1), norm='ortho')
        
        nonlinear_real = torch.tanh(1.3 * phi)
        nonlinear = fft.rfftn(nonlinear_real, dim=(-3,-2,-1), norm='ortho')
        
        memory_force = fft.rfftn(0.025 * (self.memory - phi), dim=(-3,-2,-1), norm='ortho')
        self_force = fft.rfftn(0.02 * (self.self_model - phi), dim=(-3,-2,-1), norm='ortho')
        
        K = torch.sqrt(self.K2 + 1e-12)
        forcing_band = ((K > 1.5) & (K < 5.0)).float()
        random_phase = torch.randn_like(hat) + 1j * torch.randn_like(hat)
        forcing = external_forcing * forcing_band * random_phase
        
        noise = fft.rfftn(torch.randn_like(phi) * CONSTANTS['temperature'], 
                         dim=(-3,-2,-1), norm='ortho')
        
        rhs = diffusion + dissipation + coupling + nonlinear + memory_force + self_force + forcing + noise
        hat = (hat + dt * rhs) * self.mask
        
        self.field = fft.irfftn(hat, dim=(-3,-2,-1), norm='ortho')
        self.field = torch.clamp(self.field, -2.5, 2.5)
        
        recovery = 1.0 - self.lack_memory * 0.1
        self.memory = CONSTANTS['memory_rate'] * self.memory + (1 - CONSTANTS['memory_rate']) * self.field
        self.self_model = CONSTANTS['self_rate'] * self.self_model + (1 - CONSTANTS['self_rate']) * self.field
        
        self.lack_memory *= CONSTANTS['recovery_rate']
    
    def toy_evolution(self, external_input=0.0, coupling_from_others=0.0):
        noise = torch.randn(self.n_degrees, device=self.device) * CONSTANTS['temperature'] * 0.3
        synaptic = torch.tanh(self.W @ self.v)
        
        coupling_scaled = coupling_from_others * 0.3
        coupling_scaled = max(-0.5, min(0.5, coupling_scaled))
        
        tau = 0.32
        self.v += (-CONSTANTS['gamma'] * 0.6 * self.v + 
                   synaptic * 0.5 + 
                   external_input * 0.12 + 
                   noise * 0.6 +
                   coupling_scaled) * tau
        
        self.v = torch.tanh(self.v) * 0.75
        
        new_coherence = 1 - torch.mean(torch.abs(self.v - self.auto_modelo)).item() / 1.2
        self.coherence = 0.85 * self.coherence + 0.15 * max(0, min(1, new_coherence))
        
        self.auto_modelo = CONSTANTS['self_rate'] * self.auto_modelo + (1 - CONSTANTS['self_rate']) * self.v
        
        hebb = CONSTANTS['coupling_strength'] * 0.4 * torch.outer(self.v, self.v)
        oja = -CONSTANTS['coupling_strength'] * 0.12 * torch.outer(self.v, self.W.T @ self.v)
        self.W += hebb + oja
        self.W = torch.clamp(self.W, -0.9, 0.9)
        self.W.fill_diagonal_(0)
        
        row_sums = self.W.abs().sum(dim=1, keepdim=True)
        row_sums = torch.clamp(row_sums, min=1e-6)
        self.W = self.W / row_sums * 0.7
    
    def calculate_phi_star(self):
        """Φ* as spectral organization functional."""
        spec = torch.abs(fft.rfftn(self.field, dim=(-3,-2,-1), norm='ortho'))**2
        p = spec.flatten()
        p = p / (p.sum() + 1e-12)
        
        spectral_entropy = -(p * torch.log2(p + 1e-12)).sum()
        max_ent = np.log2(len(p))
        
        if max_ent > 0:
            entropy_ratio = (spectral_entropy / max_ent).item()
            self.phi = 1.0 - entropy_ratio
            self.phi = np.clip(self.phi, 0.25, 0.75)
        else:
            self.phi = 0.5
        
        self.delta = max(0, 1 - self.phi)
        
        phi_star_raw = self.phi - CONSTANTS['lambda_star'] * self.delta
        phi_star_raw = phi_star_raw * (1 - self.lack_memory * 0.3)
        self.phi_star = np.clip(phi_star_raw, CONSTANTS['phi_star_min'], CONSTANTS['phi_star_max'])
        
        self.phi_star_history.append(self.phi_star)
        if len(self.phi_star_history) > 5:
            self.phi_star = np.mean(list(self.phi_star_history)[-5:])
    
    def calculate_dig(self):
        """DIG as temporal autocorrelation functional."""
        if len(self.v_history) < 15:
            return self.dig
        
        v_list = list(self.v_history)[-30:]
        v_array = np.array([v.cpu().numpy() for v in v_list])
        
        if len(v_array) > 3:
            corr_sum = 0
            n_corrs = 0
            
            for i in range(min(6, self.n_degrees)):
                if len(v_array) > 2:
                    corr1 = np.corrcoef(v_array[:-1, i], v_array[1:, i])[0, 1]
                    if not np.isnan(corr1) and corr1 > 0:
                        corr_sum += corr1
                        n_corrs += 1
                
                if len(v_array) > 3:
                    corr2 = np.corrcoef(v_array[:-2, i], v_array[2:, i])[0, 1]
                    if not np.isnan(corr2) and corr2 > 0:
                        corr_sum += corr2
                        n_corrs += 1
            
            if n_corrs > 0:
                dig_raw = corr_sum / n_corrs
                dig_raw = dig_raw * (1 - self.lack_memory * 0.25)
                dig_raw = max(0.15, min(0.85, dig_raw))
                self.dig = 0.7 * self.dig + 0.3 * dig_raw
                return self.dig
        
        return 0.35
    
    def update_history(self):
        self.v_history.append(self.v.clone())
    
    def apply_perturbation(self, intensity):
        """Apply controlled perturbation (increased thermal noise)."""
        noise_level = CONSTANTS['temperature'] * (1.0 + intensity * CONSTANTS['lack_noise_boost'])
        
        self.lack_memory = min(1.0, self.lack_memory + intensity * 0.15)
        
        # Affects spectral field
        self.field += torch.randn_like(self.field) * noise_level * 0.8
        self.field = torch.clamp(self.field, -2.5, 2.5)
        
        # Affects agents
        self.v += torch.randn_like(self.v) * noise_level * 0.6
        self.v = torch.tanh(self.v) * 0.75
        
        # Reduces coherence
        self.coherence *= (1 - intensity * 0.2)
        self.coherence = max(0.2, self.coherence)
        
        # Partial memory reset
        self.memory = self.memory * (1 - intensity * 0.25) + self.field * (intensity * 0.25)
        self.auto_modelo = self.auto_modelo * (1 - intensity * 0.25) + self.v * (intensity * 0.25)
    
    def step(self, external_input=0.0, coupling=0.0, perturbation_intensity=0.0):
        self.spectral_evolution(external_forcing=external_input)
        self.toy_evolution(external_input=external_input, coupling_from_others=coupling)
        
        if perturbation_intensity > 0:
            self.apply_perturbation(perturbation_intensity)
        
        self.update_history()
        self.calculate_phi_star()
        self.calculate_dig()
        
        return {
            'phi_star': self.phi_star,
            'dig': self.dig,
            'coherence': self.coherence,
            'lack_memory': self.lack_memory
        }

# =============================================================================
# UNIVERSE
# =============================================================================

class HybridUniverse:
    def __init__(self, n_systems=4, grid_size=16, n_degrees=12, device='cpu'):
        self.n_systems = n_systems
        self.device = device
        self.systems = [HybridSystem(i, grid_size, n_degrees, device) for i in range(n_systems)]
        
        self.data = {
            'time': [],
            'phi_star_mean': [],
            'dig_mean': [],
            'coherence_mean': [],
            'lack_memory_mean': [],
        }
        
        self.perturbation_start = 700
        self.perturbation_end = 820
    
    def _compute_coupling(self, perturbation_active=False):
        n = self.n_systems
        coupling_matrix = torch.zeros((n, n), device=self.device)
        
        if perturbation_active:
            return coupling_matrix
        
        for i in range(n):
            for j in range(n):
                if i != j:
                    field_i = self.systems[i].field
                    field_j = self.systems[j].field
                    corr = torch.mean(field_i * field_j).item()
                    coupling = CONSTANTS['coupling_strength'] * corr
                    coupling = max(-CONSTANTS['coupling_max'], min(CONSTANTS['coupling_max'], coupling))
                    coupling_matrix[i, j] = coupling
        
        return coupling_matrix
    
    def evolve(self, n_steps=1500):
        print("\n" + "="*70)
        print("IPM PROTOCOL - DYNAMIC REGIME SENSITIVITY ANALYSIS")
        print("="*70)
        print(f"Systems: {self.n_systems}")
        print(f"Perturbation: turns {self.perturbation_start} to {self.perturbation_end}")
        print(f"λ* = {CONSTANTS['lambda_star']}")
        print("-"*70)
        
        for step in range(n_steps):
            perturbation_active = (self.perturbation_start <= step < self.perturbation_end)
            
            if perturbation_active:
                progress = min(1.0, (step - self.perturbation_start) / CONSTANTS['lack_duration'])
                perturbation_intensity = CONSTANTS['lack_strength'] * progress
            else:
                perturbation_intensity = 0.0
            
            coupling_matrix = self._compute_coupling(perturbation_active)
            external_signal = 0.1 * np.sin(step * 0.018) + 0.05 * np.sin(step * 0.055)
            
            for i, sys in enumerate(self.systems):
                coupling = torch.sum(coupling_matrix[i, :]).item()
                sys.step(external_input=external_signal, coupling=coupling, 
                         perturbation_intensity=perturbation_intensity)
            
            if step % 20 == 0:
                self.data['time'].append(step)
                self.data['phi_star_mean'].append(np.mean([s.phi_star for s in self.systems]))
                self.data['dig_mean'].append(np.mean([s.dig for s in self.systems]))
                self.data['coherence_mean'].append(np.mean([s.coherence for s in self.systems]))
                self.data['lack_memory_mean'].append(np.mean([s.lack_memory for s in self.systems]))
            
            if step % 150 == 0 and step > 0:
                status = "🔴 PERTURBATION" if perturbation_active else "🟢 NORMAL"
                dig_val = self.data['dig_mean'][-1] if self.data['dig_mean'] else 0
                phi_val = self.data['phi_star_mean'][-1] if self.data['phi_star_mean'] else 0
                print(f"Step {step:4d} | {status:14} | Φ*={phi_val:.4f} | DIG={dig_val:.4f}")
        
        print("\n✅ Simulation complete.\n")
    
    def analyze(self):
        print("="*70)
        print("REGIME SHIFT ANALYSIS - METRIC RESPONSE TO PERTURBATION")
        print("="*70)
        
        if len(self.data['time']) < 10:
            print("Insufficient data.")
            return
        
        times = np.array(self.data['time'])
        perturbation_idx = np.argmin(np.abs(times - self.perturbation_start))
        
        pre_start = max(0, perturbation_idx - 10)
        pre_end = perturbation_idx
        post_start = perturbation_idx
        post_end = min(len(self.data['dig_mean']), perturbation_idx + 15)
        
        pre_dig = np.mean(self.data['dig_mean'][pre_start:pre_end])
        post_dig = np.mean(self.data['dig_mean'][post_start:post_end])
        pre_phi = np.mean(self.data['phi_star_mean'][pre_start:pre_end])
        post_phi = np.mean(self.data['phi_star_mean'][post_start:post_end])
        
        corr_all = np.corrcoef(self.data['phi_star_mean'], self.data['dig_mean'])[0, 1]
        
        post_phi_data = self.data['phi_star_mean'][post_start:post_end+20]
        post_dig_data = self.data['dig_mean'][post_start:post_end+20]
        corr_post = np.corrcoef(post_phi_data, post_dig_data)[0, 1] if len(post_phi_data) > 5 else 0
        
        print(f"\n📊 PRE-PERTURBATION:")
        print(f"   Φ* = {pre_phi:.4f} | DIG = {pre_dig:.4f}")
        
        print(f"\n📊 POST-PERTURBATION:")
        print(f"   Φ* = {post_phi:.4f} | DIG = {post_dig:.4f}")
        
        print(f"\n📈 VARIATIONS:")
        print(f"   Φ*:  {pre_phi:.4f}{post_phi:.4f} ({(post_phi-pre_phi)*100:+.1f}%)")
        print(f"   DIG: {pre_dig:.4f}{post_dig:.4f} ({(post_dig-pre_dig)*100:+.1f}%)")
        
        print(f"\n📊 CORRELATIONS:")
        print(f"   Global (all data): {corr_all:.3f}")
        print(f"   Post-perturbation: {corr_post:.3f}")
        
        print(f"\n{'─'*50}")
        
        dig_change = (post_dig - pre_dig) * 100
        phi_change = (post_phi - pre_phi) * 100
        
        print("\n📋 PRELIMINARY ASSESSMENT:")
        print(f"   • DIG change: {dig_change:+.1f}%")
        print(f"   • Φ* change: {phi_change:+.1f}%")
        print(f"   • Φ* × DIG correlation: {corr_all:.3f}")
        
        print("\n" + "="*70)
        print("LIMITATIONS (EXPLICIT):")
        print("   • Demonstrates co-responsiveness to global noise only")
        print("   • Correlation (~0.59) may arise from shared dependence on:")
        print("        - Same driver (global noise)")
        print("        - System energy/entropy")
        print("        - Different filtering of the same latent field")
        print("   • Does not separate thermal from structural regimes")
        print("   • Metrics are low-order statistics on correlated states")
        print("   • Preliminary sensitivity analysis, not dimensional validation")
        print("="*70)
        
        self.results = {
            'dig_change': dig_change,
            'phi_change': phi_change,
            'correlation': corr_all,
            'pre_dig': pre_dig,
            'post_dig': post_dig,
            'pre_phi': pre_phi,
            'post_phi': post_phi
        }
    
    def visualize(self):
        if len(self.data['time']) < 10:
            return
        
        plt.style.use('dark_background')
        fig, axes = plt.subplots(2, 2, figsize=(14, 10))
        
        # Φ* and DIG together
        axes[0,0].plot(self.data['time'], self.data['phi_star_mean'], color='cyan', linewidth=2, label='Φ*')
        axes[0,0].plot(self.data['time'], self.data['dig_mean'], color='lime', linewidth=2, label='DIG')
        axes[0,0].axvline(x=self.perturbation_start, color='red', linestyle='--', linewidth=2, label='Perturbation')
        axes[0,0].axvline(x=self.perturbation_end, color='orange', linestyle='--', linewidth=2, label='End')
        axes[0,0].set_title("Metric Response to Perturbation")
        axes[0,0].set_xlabel("Turns")
        axes[0,0].set_ylim(0, 0.9)
        axes[0,0].legend()
        axes[0,0].grid(True, alpha=0.3)
        
        # Φ* only
        axes[0,1].plot(self.data['time'], self.data['phi_star_mean'], color='cyan', linewidth=2)
        axes[0,1].axvline(x=self.perturbation_start, color='red', linestyle='--', linewidth=2)
        axes[0,1].axvline(x=self.perturbation_end, color='orange', linestyle='--', linewidth=2)
        axes[0,1].fill_between(self.data['time'], 
                               np.array(self.data['phi_star_mean']) - 0.05,
                               np.array(self.data['phi_star_mean']) + 0.05,
                               alpha=0.2, color='cyan')
        axes[0,1].set_title("Φ* (Spectral Organization)")
        axes[0,1].set_xlabel("Turns")
        axes[0,1].set_ylim(0, 0.8)
        axes[0,1].grid(True, alpha=0.3)
        
        # DIG only
        axes[1,0].plot(self.data['time'], self.data['dig_mean'], color='lime', linewidth=2)
        axes[1,0].axvline(x=self.perturbation_start, color='red', linestyle='--', linewidth=2)
        axes[1,0].axvline(x=self.perturbation_end, color='orange', linestyle='--', linewidth=2)
        axes[1,0].set_title("DIG (Temporal Autocorrelation)")
        axes[1,0].set_xlabel("Turns")
        axes[1,0].set_ylim(0, 0.9)
        axes[1,0].grid(True, alpha=0.3)
        
        # Scatter with regression
        corr_val = np.corrcoef(self.data['phi_star_mean'], self.data['dig_mean'])[0, 1]
        axes[1,1].scatter(self.data['phi_star_mean'], self.data['dig_mean'], 
                         c=self.data['time'], cmap='coolwarm', alpha=0.6, s=40)
        axes[1,1].set_xlabel("Φ*")
        axes[1,1].set_ylabel("DIG")
        axes[1,1].set_title(f"Φ* vs DIG (correlation = {corr_val:.3f})")
        axes[1,1].grid(True, alpha=0.3)
        
        if len(self.data['phi_star_mean']) > 1:
            z = np.polyfit(self.data['phi_star_mean'], self.data['dig_mean'], 1)
            p = np.poly1d(z)
            x_trend = np.linspace(min(self.data['phi_star_mean']), max(self.data['phi_star_mean']), 100)
            axes[1,1].plot(x_trend, p(x_trend), 'r--', alpha=0.8, linewidth=2)
        
        plt.colorbar(axes[1,1].collections[0], ax=axes[1,1], label='Turn')
        plt.suptitle(f"METRIC RESPONSE ANALYSIS - Φ* × DIG CORRELATION = {corr_val:.3f}", fontsize=14)
        plt.tight_layout()
        plt.show()

# =============================================================================
# EXECUTION
# =============================================================================

if __name__ == "__main__":
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"Device: {device}")
    
    universe = HybridUniverse(n_systems=4, grid_size=16, n_degrees=12, device=device)
    universe.evolve(n_steps=1500)
    universe.analyze()
    universe.visualize()

8. Results

Simulation output:

text
Step 150 | 🟢 NORMAL       | Φ*=0.5167 | DIG=0.8500
Step 300 | 🟢 NORMAL       | Φ*=0.5243 | DIG=0.8500
Step 450 | 🟢 NORMAL       | Φ*=0.5294 | DIG=0.8204
Step 600 | 🟢 NORMAL       | Φ*=0.5338 | DIG=0.8500
Step 750 | 🔴 PERTURBATION | Φ*=0.3669 | DIG=0.2783
Step 900 | 🟢 NORMAL       | Φ*=0.3765 | DIG=0.8500
Step 1050| 🟢 NORMAL       | Φ*=0.4226 | DIG=0.8500
Step 1200| 🟢 NORMAL       | Φ*=0.4454 | DIG=0.8499
Step 1350| 🟢 NORMAL       | Φ*=0.4603 | DIG=0.8500

Final analysis:

MetricPre-PerturbationPost-PerturbationChange
Φ*0.53350.3749-15.9%
DIG0.84510.5918-25.3%

Correlations:

  • Global (all data): 0.594

  • Post-perturbation: 0.654


9. Interpretation

ObservationInterpretation
Φ and DIG drop during perturbation*Both metrics show co-responsiveness to increased global noise
Metrics recover after perturbationSystem returns to previous regime when noise decreases
Correlation (0.594) between Φ and DIG*Metrics correlate under global noise — not evidence of dimensional independence
**DIG drops more (-25.3%) than Φ* (-15.9%)**Temporal autocorrelation is more sensitive to noise than spectral organization

10. What This Experiment Demonstrates

Demonstrated:

  • Metrics respond consistently to increased global noise

  • Metrics correlate with each other under perturbation

  • Metrics recover when perturbation ceases

Not demonstrated (explicit limitations):

  • That metrics capture "integration" rather than just global noise

  • Separation between thermal and structural regimes

  • That Φ* and DIG measure independent dimensions of the system

  • Epistemological adequacy across contexts

Important note on correlation: The observed correlation (~0.59) may arise from shared dependence on:

  • The same driver (global noise)

  • System energy/entropy

  • Different filtering of the same latent field

This experiment does not distinguish these possibilities.


11. Next Steps for Deeper Analysis

To separate sensitivity to thermal regime from structural regime, the next experiment should:

ConditionWhat variesWhat is fixed
ANoise intensityCoupling topology
BCoupling topologyNoise intensity

This would allow asking:

  • Do Φ* and DIG respond differently to thermal vs structural changes?

  • Do they desynchronize under certain conditions?

  • Which metric is more sensitive to each dimension?

This is the critical experiment to determine whether metrics capture structure or just energy.


12. Conclusion

The IPM Protocol demonstrates that Φ* and DIG show co-responsiveness to controlled perturbation in a coupled dynamical system. The experiment shows:

  1. *Φ changes by -15.9%** during increased noise

  2. DIG changes by -25.3% during increased noise

  3. Φ and DIG correlate at 0.594* under global noise

  4. Metrics recover after perturbation — system returns to previous regime

This is a preliminary sensitivity analysis, not epistemological validation. The experiment demonstrates consistent metric response to global noise, not that metrics capture "ontological integration" or separate thermal from structural regimes.

The observed correlation may arise from shared dependence on global noise, system energy, or latent field dynamics. Distinguishing these possibilities requires the next experiment (varying coupling topology independently of noise).



My Results:

Device: cpu

======================================================================
IPM PROTOCOL - DYNAMIC REGIME SENSITIVITY ANALYSIS
======================================================================
Systems: 4
Perturbation: turns 700 to 820
λ* = 0.25
----------------------------------------------------------------------
Step  150 | 🟢 NORMAL       | Φ*=0.5165 | DIG=0.8500
Step  300 | 🟢 NORMAL       | Φ*=0.5241 | DIG=0.8500
Step  450 | 🟢 NORMAL       | Φ*=0.5293 | DIG=0.8019
Step  600 | 🟢 NORMAL       | Φ*=0.5338 | DIG=0.8500
Step  750 | 🔴 PERTURBATION | Φ*=0.3666 | DIG=0.3085
Step  900 | 🟢 NORMAL       | Φ*=0.3766 | DIG=0.8500
Step 1050 | 🟢 NORMAL       | Φ*=0.4223 | DIG=0.8500
Step 1200 | 🟢 NORMAL       | Φ*=0.4449 | DIG=0.8499
Step 1350 | 🟢 NORMAL       | Φ*=0.4596 | DIG=0.8500

✅ Simulation complete.

======================================================================
REGIME SHIFT ANALYSIS - METRIC RESPONSE TO PERTURBATION
======================================================================

📊 PRE-PERTURBATION:
   Φ* = 0.5334 | DIG = 0.8436

📊 POST-PERTURBATION:
   Φ* = 0.3749 | DIG = 0.5951

📈 VARIATIONS:
   Φ*:  0.5334 → 0.3749 (-15.9%)
   DIG: 0.8436 → 0.5951 (-24.8%)

📊 CORRELATIONS:
   Global (all data): 0.596
   Post-perturbation: 0.668

──────────────────────────────────────────────────

📋 PRELIMINARY ASSESSMENT:
   • DIG change: -24.8%
   • Φ* change: -15.9%
   • Φ* × DIG correlation: 0.596

======================================================================
LIMITATIONS (EXPLICIT):
   • Demonstrates co-responsiveness to global noise only
   • Correlation (~0.59) may arise from shared dependence on:
        - Same driver (global noise)
        - System energy/entropy
        - Different filtering of the same latent field
   • Does not separate thermal from structural regimes
   • Metrics are low-order statistics on correlated states
   • Preliminary sensitivity analysis, not dimensional validation
======================================================================

IPM Ethical Framework

  IPM Ethical Framework   Operationalization of the Gradient Precautionary Heuristic     Author: Taotuner Date: June 2026 ...