"""Material memory effects: retention timescales and modality affinities (SPECTRAL A.4)."""
from dataclasses import dataclass
from enum import Enum
from typing import Dict
import numpy as np
[docs]
class Material(Enum):
QUARTZ = "quartz"
IRON = "iron"
LIMESTONE = "limestone"
WOOD = "wood"
WATER = "water"
AIR = "air"
[docs]
@dataclass
class MaterialSpec:
name: Material
retention_timescale: float # τ_retain [seconds]
modality_affinities: Dict[str, float]
decay_exponent: float # α in I(t) = I₀ exp(-(t/τ)^α)
MATERIAL_SPECS = {
Material.QUARTZ: MaterialSpec(Material.QUARTZ, 3.15e9, {"visual": 1.5, "affective": 1.3}, 0.8),
Material.IRON: MaterialSpec(Material.IRON, 3.15e8, {"affective": 1.4, "auditory": 1.2}, 0.7),
Material.LIMESTONE: MaterialSpec(
Material.LIMESTONE, 3.15e10, {"auditory": 1.5, "visual": 1.3}, 0.6
),
Material.WOOD: MaterialSpec(Material.WOOD, 3.15e7, {"olfactory": 1.6, "tactile": 1.2}, 0.9),
Material.WATER: MaterialSpec(Material.WATER, 86400, {}, 1.0),
Material.AIR: MaterialSpec(Material.AIR, 3600, {}, 1.2),
}
[docs]
class MaterialMemory:
def __init__(self, material: Material = Material.QUARTZ):
self.material = material
self.spec = MATERIAL_SPECS[material]
self.impression_intensity = 0.0 # normalized 0..1
self.age = 0.0
[docs]
def decay_factor(self, time: float) -> float:
if time <= 0:
return 1.0
return np.exp(-((time / self.spec.retention_timescale) ** self.spec.decay_exponent))
[docs]
def amplify_modality(self, modality: str, raw_strength: float) -> float:
gain = self.spec.modality_affinities.get(modality, 1.0)
return raw_strength * gain
[docs]
def update_impression(self, dt: float, influx_rate: float):
"""
dI/dt = influx_rate * (1 - I) - decay_rate * I
This bounds I between 0 and 1.
"""
decay_rate = 1.0 / self.spec.retention_timescale
# Saturation term: (1 - I) prevents exceeding 1
dI_dt = (
influx_rate * (1.0 - self.impression_intensity) - decay_rate * self.impression_intensity
)
self.impression_intensity += dI_dt * dt
self.impression_intensity = np.clip(self.impression_intensity, 0.0, 1.0)
self.age += dt
return self.impression_intensity
[docs]
def get_retention_percentage(self, time: float) -> float:
return self.decay_factor(time) * 100.0
[docs]
class CompositeMaterial:
def __init__(self, composition: Dict[Material, float]):
self.composition = composition
self.memories = {mat: MaterialMemory(mat) for mat in composition}
[docs]
def update_all(self, dt: float, influx_rate: float):
for mem in self.memories.values():
mem.update_impression(dt, influx_rate)
[docs]
def amplify_modality(self, modality: str, raw_strength: float) -> float:
total = 0.0
for mat, fraction in self.composition.items():
mem = self.memories[mat]
total += fraction * mem.amplify_modality(modality, raw_strength)
return total
[docs]
def overall_impression_intensity(self) -> float:
total = 0.0
for mat, fraction in self.composition.items():
total += fraction * self.memories[mat].impression_intensity
return total