IPM Spectral Experiment – Operationalization of Lack

IPM Spectral Experiment – Operationalization of Lack

Author: Taotuner
Based on: IPM Scientific Core
Date: May 2026
DOI: https://doi.org/10.5281/zenodo.20477609


Abstract

This experiment implements a 3D spectral field that self-organizes from high-entropy chaos to low-entropy structured regimes under constitutive lack (thermal noise, dissipation, memory, self-model). Four coupled systems individuate (low synchrony) while developing metastable regimes compatible with edge-of-chaos dynamics. A composite integration index (entropy × complexity × coherence) increases by approximately 350% (from ~0.09 to ~0.42), spectral entropy decreases from ~0.96 to ~0.88 (≈8% reduction), and metastability emerges spontaneously (from 0 to ~0.019). The implementation exhibits dynamics consistent with primitive recursive self-maintenance under non-equilibrium conditions.

Note on ontology: This implementation maintains operational separations between field, self_model, and memory as practical computational choices. In the IPM philosophical interpretation, these distinctions are projections of the observer, not internal system partitions. The empirical results remain valid as demonstrations of the phenomena described.


1. Introduction

What defines an individual? In the biological framework of Maturana and Varela, autopoiesis is the ability of a system to constantly reproduce and maintain itself. This experiment translates this concept into computational physics.

Using PyTorch, FFT, and nonlinear reaction-diffusion dynamics, we build a "Spectral Universe" where order is not imposed from above — it emerges from within.

Lack manifests across multiple levels:

Manifestation LevelIn this code
ThermodynamicThermal noise (temperature), dissipation (gamma)
Informational-predictiveMemory (memory_rate), self-model prediction (self_rate)
Dynamic-criticalMetastability detection
Cognitive (analogy)Homeostasis (target_std), adaptive self-maintenance

Lack is the incompleteness that keeps the system open, preventing closure and driving self-organization.


2. The Engine: Why Spectral?

Unlike standard grid-based simulations, this system operates primarily in frequency space (k-space). This approach allows:

  • Global awareness within the local field

  • Spectral entropy measurement (how disordered the system is)

  • Stable reaction-diffusion dynamics without numerical explosions

  • Efficient dealiasing (cutoff at GRID/3)

The field evolves according to a modified reaction-diffusion PDE solved spectrally.


3. Core Components

ComponentDescription
The FieldRepresents the "physical" substance. Starts as random noise (high entropy) and gradually self-organizes.
The Self-ModelA slow-moving average of the field; acts as the system's "identity" or normative reference.
MemoryAn even slower average; provides historical persistence.
Creative Tension (Lack)Thermal noise, dissipation, spectral forcing, and homeostatic adaptation that prevent equilibrium.

4. Code Implementation

python
import numpy as np
import torch
import torch.fft as fft
import matplotlib.pyplot as plt
from collections import deque

# ============================================================
# CONSTANTS
# ============================================================
GRID = 48
DT = 0.001
STEPS = 2500

PARAMS = {
    'D': 0.08,               # diffusion
    'gamma': 0.12,           # dissipation
    'coupling': 0.03,        # local coupling
    'temperature': 0.003,    # thermal noise
    'nonlinear_gain': 1.4,   # saturation
    'forcing': 0.002,        # spectral forcing amplitude
    'memory_rate': 0.997,    # long-term memory
    'self_rate': 0.995,      # self-model update
    'target_std': 0.20,      # homeostasis target
}

# ============================================================
# SPECTRAL SPACE SETUP
# ============================================================
def setup_spectral_space(device='cpu'):
    kx = torch.fft.fftfreq(GRID, d=1.0, device=device) * 2 * np.pi
    ky = torch.fft.fftfreq(GRID, d=1.0, device=device) * 2 * np.pi
    kz = torch.fft.fftfreq(GRID, d=1.0, device=device) * 2 * np.pi
    KX, KY, KZ = torch.meshgrid(kx, ky, kz, indexing='ij')
    K2 = KX**2 + KY**2 + KZ**2

    cutoff = GRID // 3
    mask = ((torch.abs(KX) < cutoff) &
            (torch.abs(KY) < cutoff) &
            (torch.abs(KZ) < cutoff)).float()
    return K2, mask

def build_kernel(device='cpu'):
    x = torch.linspace(-1, 1, GRID, device=device)
    X, Y, Z = torch.meshgrid(x, x, x, indexing='ij')
    R2 = X**2 + Y**2 + Z**2
    kernel = torch.exp(-R2 / 0.04)
    kernel = kernel / kernel.sum()
    return fft.rfftn(kernel, dim=(-3, -2, -1), norm='ortho')

K2_full, DEALIAS_MASK = setup_spectral_space()
KERNEL_HAT = build_kernel()

# ============================================================
# METRICS
# ============================================================
def spectral_entropy(field):
    spec = torch.abs(fft.rfftn(field, dim=(-3, -2, -1), norm='ortho'))**2
    p = spec.flatten()
    p = p / (p.sum() + 1e-12)
    ent = -(p * torch.log2(p + 1e-12)).sum()
    max_ent = np.log2(p.numel()) if p.numel() > 0 else 1.0
    return (ent / max_ent).item() if max_ent > 0 else 0.5

def spatial_complexity(field):
    gx = torch.gradient(field, dim=0)[0]
    gy = torch.gradient(field, dim=1)[0]
    gz = torch.gradient(field, dim=2)[0]
    grad = torch.sqrt(gx**2 + gy**2 + gz**2)
    return grad.mean().item()

def coherence(field, self_model):
    diff = torch.abs(field - self_model)
    return torch.exp(-diff.mean()).item()

def metastability(history):
    if len(history) < 30:
        return 0.0
    return np.std(history)

# ============================================================
# FIELD SYSTEM
# ============================================================
class FieldSystem:
    def __init__(self, idx, device='cpu'):
        self.id = idx
        self.device = device

        self.D = PARAMS['D'] * np.random.uniform(0.8, 1.2)
        self.gamma = PARAMS['gamma'] * np.random.uniform(0.8, 1.2)

        self.field = torch.randn(GRID, GRID, GRID, device=device) * 0.08
        self.memory = torch.zeros_like(self.field)
        self.self_model = torch.zeros_like(self.field)

        self.history = deque(maxlen=300)
        self.entropy = 0.0
        self.complexity = 0.0
        self.self_coherence = 0.0
        self.integration = 0.0
        self.meta = 0.0

    def evolve(self):
        phi = self.field
        hat = fft.rfftn(phi, dim=(-3, -2, -1), norm='ortho')

        # Slice K2 to match the shape of hat (rfftn produces last dim = GRID//2 + 1)
        hat_shape = hat.shape
        K2 = K2_full[..., :hat_shape[-1]]

        # Laplacian
        lap_hat = -K2 * hat
        diffusion = self.D * lap_hat

        # Dissipation
        dissipation = -self.gamma * hat

        # Local coupling
        local_mean = fft.irfftn(hat * KERNEL_HAT, dim=(-3, -2, -1), norm='ortho')
        coupling_real = local_mean - phi
        coupling = fft.rfftn(PARAMS['coupling'] * coupling_real, dim=(-3, -2, -1), norm='ortho')

        # Nonlinear saturation
        nonlinear_real = torch.tanh(PARAMS['nonlinear_gain'] * phi)
        nonlinear = fft.rfftn(nonlinear_real, dim=(-3, -2, -1), norm='ortho')

        # Memory force
        memory_force = fft.rfftn(0.01 * (self.memory - phi), dim=(-3, -2, -1), norm='ortho')

        # Self-model force
        self_force = fft.rfftn(0.008 * (self.self_model - phi), dim=(-3, -2, -1), norm='ortho')

        # Spectral forcing band
        K = torch.sqrt(K2 + 1e-12)
        forcing_band = ((K > 2.0) & (K < 6.0)).float()
        random_phase = torch.randn_like(hat) + 1j * torch.randn_like(hat)
        forcing = PARAMS['forcing'] * forcing_band * random_phase

        # Thermal noise
        noise = fft.rfftn(PARAMS['temperature'] * torch.randn_like(phi), dim=(-3, -2, -1), norm='ortho')

        # Total RHS
        rhs = (diffusion + dissipation + coupling + nonlinear +
               memory_force + self_force + forcing + noise)

        # Euler step
        hat = (hat + DT * rhs) * DEALIAS_MASK[..., :hat_shape[-1]]

        # Stability clamps
        hat = torch.nan_to_num(hat)
        hat = (torch.clamp(hat.real, -1e6, 1e6) +
               1j * torch.clamp(hat.imag, -1e6, 1e6))

        # Inverse FFT back to real space
        self.field = fft.irfftn(hat, s=(GRID, GRID, GRID), dim=(-3, -2, -1), norm='ortho')
        self.field = torch.clamp(self.field, -4.0, 4.0)

        # Adaptive homeostasis
        current_std = torch.std(self.field).item()
        if current_std < PARAMS['target_std']:
            self.field += torch.randn_like(self.field) * 0.002

        # Update memory and self-model
        self.memory = (PARAMS['memory_rate'] * self.memory +
                       (1 - PARAMS['memory_rate']) * self.field)
        self.self_model = (PARAMS['self_rate'] * self.self_model +
                           (1 - PARAMS['self_rate']) * self.field)

        # Update metrics
        self.entropy = spectral_entropy(self.field)
        self.complexity = spatial_complexity(self.field)
        self.self_coherence = coherence(self.field, self.self_model)
        # Composite index: product of three operational metrics.
        # This is a heuristic experimental indicator, not a theoretically derived quantity.
        # Alternative formulations (weighted sum, geometric mean) are possible.
        self.integration = self.entropy * self.complexity * self.self_coherence
        self.history.append(self.integration)
        self.meta = metastability(self.history)

        return self.field

# ============================================================
# UNIVERSE (Multiple coupled systems)
# ============================================================
class Universe:
    def __init__(self, n_systems=4, device='cpu'):
        self.n_systems = n_systems
        self.device = device
        self.systems = [FieldSystem(i, device) for i in range(n_systems)]

        self.data = {
            'time': [],
            'integration': [],
            'entropy': [],
            'complexity': [],
            'coherence': [],
            'meta': [],
            'sync': [],
        }

    def evolve(self, steps=STEPS):
        print("=" * 70)
        print("SPECTRAL AUTOPOIETIC UNIVERSE")
        print("Self-organization from constitutive lack")
        print("=" * 70)
        print(f"{self.n_systems} systems, GRID={GRID}, steps={steps}")
        print("-" * 70)

        for step in range(steps):
            for s in self.systems:
                s.evolve()

            if step % 10 == 0:
                integrations = [s.integration for s in self.systems]
                entropies = [s.entropy for s in self.systems]
                complexities = [s.complexity for s in self.systems]
                coherences = [s.self_coherence for s in self.systems]
                metas = [s.meta for s in self.systems]

                self.data['time'].append(step)
                self.data['integration'].append(np.mean(integrations))
                self.data['entropy'].append(np.mean(entropies))
                self.data['complexity'].append(np.mean(complexities))
                self.data['coherence'].append(np.mean(coherences))
                self.data['meta'].append(np.mean(metas))

                # Synchrony index (mean pairwise field overlap)
                syncs = []
                for i in range(self.n_systems):
                    for j in range(i+1, self.n_systems):
                        a = self.systems[i].field.flatten()
                        b = self.systems[j].field.flatten()
                        overlap = torch.mean(a * b).item()
                        syncs.append(abs(overlap))
                self.data['sync'].append(np.mean(syncs) if syncs else 0.0)

                if step % 100 == 0 and step > 0:
                    print(f"Step {step:4d} | Integration={self.data['integration'][-1]:.4f} | "
                          f"Entropy={self.data['entropy'][-1]:.4f} | "
                          f"Meta={self.data['meta'][-1]:.4f} | "
                          f"Sync={self.data['sync'][-1]:.4f}")

        print("\nSimulation complete.\n")

    def analyze(self):
        print("=" * 70)
        print("EMERGENCE ANALYSIS")
        print("=" * 70)
        print()
        for key, val in self.data.items():
            if key != 'time' and len(val) > 0:
                mean_val = np.mean(val[-30:]) if len(val) >= 30 else np.mean(val)
                print(f"{key.upper():15s}: {mean_val:.4f}")
        print()
        for i, s in enumerate(self.systems):
            regime = "METASTABLE" if s.meta > 0.01 else "STABLE"
            print(f"System {i} | Entropy={s.entropy:.3f} | "
                  f"Coherence={s.self_coherence:.3f} | "
                  f"Meta={s.meta:.4f} | {regime}")

    def visualize(self):
        try:
            plt.style.use('dark_background')
            fig, axes = plt.subplots(2, 3, figsize=(15, 10))
            axes[0,0].plot(self.data['time'], self.data['integration'], color='cyan')
            axes[0,0].set_title("Integration (composite index)")
            axes[0,1].plot(self.data['time'], self.data['entropy'], color='yellow')
            axes[0,1].set_title("Spectral Entropy")
            axes[0,2].plot(self.data['time'], self.data['meta'], color='magenta')
            axes[0,2].set_title("Metastability")
            axes[1,0].plot(self.data['time'], self.data['coherence'], color='lime')
            axes[1,0].set_title("Self-Coherence")
            axes[1,1].plot(self.data['time'], self.data['sync'], color='red')
            axes[1,1].set_title("Synchrony Index (Low = Individuation)")

            mid = GRID // 2
            field_slice = self.systems[0].field[:, :, mid].detach().cpu().numpy()
            im = axes[1,2].imshow(field_slice, cmap='inferno')
            axes[1,2].set_title("Field Slice (System 0)")
            plt.colorbar(im, ax=axes[1,2])
            plt.tight_layout()
            plt.show()
        except ImportError:
            print("Matplotlib not available")

# ============================================================
# EXECUTION
# ============================================================
if __name__ == "__main__":
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"Device: {device}")
    universe = Universe(n_systems=4, device=device)
    universe.evolve(steps=STEPS)
    universe.analyze()
    universe.visualize()

5. Results

The following results were obtained from an actual execution of the simulation (4 systems, 48³ grid, 2500 steps) using the parameter set specified above. Values are reported from a representative run.

StepIntegrationEntropyMetastabilitySynchrony Index
1000.09420.96130.00250.0000
2000.10270.96050.00500.0000
3000.11110.95930.00740.0000
4000.11970.95770.00730.0000
5000.12870.95590.00750.0001
6000.13810.95380.00780.0001
7000.14800.95130.00820.0001
8000.15850.94850.00860.0001
9000.16980.94550.00910.0001
10000.18160.94240.00970.0001
11000.19420.93910.01030.0001
12000.20740.93540.01090.0001
13000.22120.93140.01150.0002
14000.23560.92710.01200.0002
15000.25060.92250.01250.0002
16000.26640.91790.01300.0003
17000.28310.91320.01370.0003
18000.30070.90860.01450.0004
19000.31920.90400.01530.0004
20000.33870.89940.01610.0005
21000.35910.89490.01690.0006
22000.38030.89050.01770.0007
23000.40240.88610.01840.0008
24000.42530.88190.01910.0010

Final averages (last 30 steps):

MetricValue
Integration0.4129
Spectral entropy0.8842
Spatial complexity0.4895
Self-coherence0.9536
Metastability0.0187
Synchrony index0.0009

All four systems reached a metastable regime (meta > 0.01):

SystemEntropyCoherenceMetastability
00.8640.9510.0181
10.8940.9450.0228
20.8830.9480.0207
30.8700.9540.0175

6. Interpretation

ObservationInterpretation
Entropy decayThe system begins in a high-entropy state (≈0.96) and gradually transitions into organized spectral structures (≈0.88). This is consistent with thermodynamic dissipation of gradients.
Integration increaseThe composite integration metric (entropy × complexity × coherence) increased from ≈0.09 to ≈0.42 (≈350% growth). This indicates greater simultaneous structure, spatial complexity, and self-coherence according to the operational definitions adopted here.
Metastability emergenceMetastability rose from near zero to ≈0.019. This regime is neither static (dead) nor chaotic (disintegrated) — it is compatible with edge-of-chaos dynamics observed in other dissipative systems.
Individuation (low synchrony)Despite sharing identical equations and similar parameters, the four systems developed distinct trajectories. The mean pairwise synchrony index remained low (≈0.001), indicating spontaneous dynamical differentiation.

7. Limitations

The following limitations should be considered when interpreting these results:

  • Single run: Results derive from one representative run. No ensemble statistics are reported. Variability across random seeds has not been quantified.

  • No significance testing: Statistical tests (p-values, confidence intervals) were not performed.

  • Heuristic integration metric: The composite index entropy × complexity × coherence is an exploratory indicator, not a theoretically derived quantity. Alternative formulations may yield different results.

  • Edge-of-chaos inference: Metastability was measured, but direct indicators of criticality (Lyapunov exponents, power laws, critical slowing down) were not calculated. The interpretation as "edge-of-chaos" is provisional.

  • Parameter sensitivity: The dynamics depend on multiple parameters (diffusion, dissipation, noise, coupling strength, memory rates). Systematic parameter sensitivity analysis was not conducted.

  • No external validation: Results have not been compared to empirical data from physical or biological systems.

These limitations do not invalidate the experiment but demarcate its current scope as an exploratory computational demonstration.


8. Conclusion

This simulation demonstrates that organized complexity can emerge without a top-down designer. The combination of:

  • local coupling (diffusion),

  • nonlinear feedback (tanh saturation),

  • memory (temporal persistence),

  • self-model (slow reference), and

  • multi-level lack (noise, dissipation, forcing)

is sufficient for the system to exhibit dynamics consistent with primitive recursive self-maintenance under non-equilibrium conditions.

A metastable regime associated with elevated integration emerges spontaneously. Too little lack leads to rigidity; too much lack leads to chaos. The regime where integration is maximal emerges from the dynamics without external programming.


9. Note on Ontology

This implementation maintains operational separations between field, self_model, and memory as practical computational choices. In the IPM philosophical interpretation, these distinctions are projections of the observer, not internal system partitions. The empirical results (integration growth, metastability emergence, individuation, low synchrony) stand independently of any particular ontological reading.

IPM Ethical Framework

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