UNPKG

@sschepis/resolang

Version:

ResoLang - Core quantum resonance computation library for browser and Node.js

1,170 lines (976 loc) 32.3 kB
/** * Discrete Observer Implementation * * Implements the full specification from discrete.pdf including: * - Prime-indexed oscillators with MODULAR phase dynamics (Section 3.2) * - Discrete coupling: Couple_p(t) = ⌊(K/|N(p)|) · Σ sin_M[(φ_q - φ_p) mod M]⌋ * - Histogram-based coherence (C_bin) and windowed stability (Var_C) * - Auxiliary weight registers (w_p) for Hebbian learning * - Endogenous time via coherence-triggered ticks * - Sedenion Memory Field (SMF) with composition vectors * * This replaces the continuous Kuramoto model in physics.ts with * the discrete modular formalism specified in the paper. */ import { JSONBuilder } from './core/serialization'; // ============================================================================ // Configuration (matches discrete.pdf Section 3.2) // ============================================================================ export class DiscreteObserverConfig { // Phase space modulus (discrete phase resolution) M: i32 = 1000; // Phase increment formula: ∆_p ≡ (c*p + d) mod M c: i32 = 1; d: i32 = 3; // Coupling strength K K: i32 = 50; // Integer trig scale factor scale: i32 = 100; // Amplitude parameters A_max: f64 = 100.0; delta: f64 = 0.2; // decay per step (STANDARD - keep for learning) // Coherence bins (B) - REDUCED for coarser binning, easier coherence B: i32 = 12; // Stability window (H) - SHORTER for faster response H: i32 = 15; // Tick thresholds (Section 5.4) - RELAXED for easier ticks during learning C_th: f64 = 0.15; // coherence threshold (REDUCED - easier ticks) epsilon_C: f64 = 0.15; // dC threshold for stability (RELAXED) tau_Var: f64 = 0.15; // variance threshold (RELAXED) // Lockup detection (Section 6.2) - RELAXED to preserve learning C_lock: f64 = 0.9; dC_lock: f64 = 0.001; tunnelCooldown: i32 = 100; // Entropy bounds (Section 6.3) entropyFloor: f64 = 0.5; entropyCeiling: f64 = 2.5; // Coupling graph bounds - INCREASED for stronger learning J_max: i32 = 15; // Auxiliary weight bounds W_max: i32 = 100; // SMF normalization bound L: i32 = 100; // Learning parameters (NEW - matches JS version) learningRate: f64 = 0.5; // Hebbian learning rate learnedCouplingWeight: f64 = 1.5; // Weight for learned coupling vs base coupling learningThreshold: f64 = 0.15; // Lower threshold for learning (15% of A_max) // Anti-lockup parameters (RELAXED to preserve learning) lockupDetectionWindow: i32 = 15; // How many ticks to detect repeating pattern perturbationStrength: f64 = 0.4; // How much to perturb phases when stuck maxTotalEnergy: f64 = 800.0; // Maximum total energy cap (8 * A_max) targetMaxActive: i32 = 7; // Target max active primes for stabilization } export const DISCRETE_CONFIG: DiscreteObserverConfig = new DiscreteObserverConfig(); // ============================================================================ // Default Primes (first 21 primes for Enochian compatibility) // ============================================================================ export const DEFAULT_PRIMES: i32[] = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73 ]; // Enochian sacred primes (7 primes per paper10) export const ENOCHIAN_PRIMES: i32[] = [7, 11, 13, 17, 19, 23, 29]; // ============================================================================ // Integer Trig Tables (precomputed for determinism) // ============================================================================ let _sinM: Int32Array = new Int32Array(0); let _cosM: Int32Array = new Int32Array(0); function ensureTrigTables(): void { if (_sinM.length > 0) return; const M = DISCRETE_CONFIG.M; _sinM = new Int32Array(M); _cosM = new Int32Array(M); const twoPi = 2.0 * Math.PI; for (let i: i32 = 0; i < M; i++) { const angle = twoPi * f64(i) / f64(M); _sinM[i] = i32(Math.round(f64(DISCRETE_CONFIG.scale) * Math.sin(angle))); _cosM[i] = i32(Math.round(f64(DISCRETE_CONFIG.scale) * Math.cos(angle))); } } // ============================================================================ // Discrete Observer State // ============================================================================ export class DiscreteObserverState { // Oscillator registers (n oscillators) phi: Int32Array; // phases (modular integers) A: Float64Array; // amplitudes w: Int32Array; // auxiliary weights // Coupling graph (n x n) - base coupling J: Int8Array; // Learned coupling weights (n x n) - additive to base J (NEW) learnedJ: Float32Array; // SMF state (16D sedenion memory field) s: Int32Array; // Configuration n: i32; primes: Int32Array; // Coherence history cohHist: Float64Array; cohHistIdx: i32; // Phase bins for histogram coherence phaseBins: Int32Array; coarseBins: Int32Array; // Time counters t: i64; tickCount: i64; // Metrics lastCoherence: f64; lastSmfEntropy: f64; lastHqeEntropy: f64; // Lockup detection lockupCounter: i32; tunnelCooldownRemaining: i32; watchdogNoveltyWindow: Float64Array; watchdogIdx: i32; watchdogDwellTime: i32; // Active oscillators from last tick lastActive: Int32Array; lastActiveCount: i32; // Anti-lockup pattern tracking (NEW) lastActiveSignature: string; stuckCounter: i32; constructor(primes: Int32Array | null = null) { ensureTrigTables(); // Determine n first let n: i32; if (primes == null) { n = DEFAULT_PRIMES.length; } else { n = primes.length; } this.n = n; // Initialize ALL typed arrays BEFORE any use of this.n this.primes = new Int32Array(n); this.phi = new Int32Array(n); this.A = new Float64Array(n); this.w = new Int32Array(n); this.J = new Int8Array(n * n); this.learnedJ = new Float32Array(n * n); // NEW: learned coupling weights this.s = new Int32Array(16); this.cohHist = new Float64Array(DISCRETE_CONFIG.H); this.phaseBins = new Int32Array(DISCRETE_CONFIG.M); this.coarseBins = new Int32Array(DISCRETE_CONFIG.B); this.watchdogNoveltyWindow = new Float64Array(DISCRETE_CONFIG.H); this.lastActive = new Int32Array(n); // Initialize scalar fields this.cohHistIdx = 0; this.t = 0; this.tickCount = 0; this.lastCoherence = 0.0; this.lastSmfEntropy = 0.0; this.lastHqeEntropy = 0.0; this.lockupCounter = 0; this.tunnelCooldownRemaining = 0; this.watchdogIdx = 0; this.watchdogDwellTime = 0; this.lastActiveCount = 0; this.lastActiveSignature = ""; // NEW this.stuckCounter = 0; // NEW // Now populate primes if (primes == null) { for (let i: i32 = 0; i < n; i++) { this.primes[i] = DEFAULT_PRIMES[i]; } } else { for (let i: i32 = 0; i < n; i++) { this.primes[i] = primes[i]; } } // Initialize oscillator values with sparse active set (first 4 active) for (let i: i32 = 0; i < n; i++) { // Random initial phase this.phi[i] = i32(Math.floor(Math.random() * 300.0)); if (i < 4) { this.A[i] = DISCRETE_CONFIG.A_max * 0.6; } else { this.A[i] = DISCRETE_CONFIG.A_max * 0.2; } this.w[i] = 0; } // Initialize coupling graph (default all 1) for (let i: i32 = 0; i < this.J.length; i++) { this.J[i] = 1; } // Initialize SMF (16 axes) for (let k: i32 = 0; k < 16; k++) { this.s[k] = (k % 5 - 2) * 10; } } } // ============================================================================ // Core Computation Functions // ============================================================================ /** * Compute discrete coupling term for oscillator i * Now includes learned coupling weights for Hebbian learning * Couple_p(t) = ⌊(K/|N(p)|) · Σ_q∈N(p) (J_pq + learnedJ_pq * weight) · sin_M[(φ_q - φ_p) mod M]⌋ */ export function computeDiscreteCoupling( state: DiscreteObserverState, i: i32 ): i32 { const M = DISCRETE_CONFIG.M; const K = DISCRETE_CONFIG.K; const scale = DISCRETE_CONFIG.scale; const A_max = DISCRETE_CONFIG.A_max; const learnedWeight = DISCRETE_CONFIG.learnedCouplingWeight; let sum: f64 = 0.0; let neighborCount: i32 = 0; for (let j: i32 = 0; j < state.n; j++) { if (i == j) continue; const idx = i * state.n + j; const baseWeight = f64(state.J[idx]); const learnedBias = f64(state.learnedJ[idx]) * learnedWeight; const effectiveWeight = baseWeight + learnedBias; if (effectiveWeight != 0.0) { neighborCount++; // Phase difference (modular) const diff = (state.phi[j] - state.phi[i] + M) % M; // Amplitude weighting const ampWeight = state.A[j] / A_max; // Discrete sine coupling with learned weights sum += effectiveWeight * f64(_sinM[diff]) * ampWeight; } } if (neighborCount == 0) return 0; // Floor division per spec return i32(Math.floor((f64(K) * sum) / (f64(neighborCount) * f64(scale)))); } /** * Compute histogram-based coherence (C_bin) * C_bin = max_b(histogram[b]) / n */ export function computeHistogramCoherence(state: DiscreteObserverState): f64 { const M = DISCRETE_CONFIG.M; const B = DISCRETE_CONFIG.B; const binSize = M / B; // Reset bins for (let i: i32 = 0; i < B; i++) { state.coarseBins[i] = 0; } // Fill coarse bins for (let i: i32 = 0; i < state.n; i++) { let bin = state.phi[i] / binSize; if (bin >= B) bin = B - 1; state.coarseBins[bin]++; } // Find max bin let maxBin: i32 = 0; for (let k: i32 = 0; k < B; k++) { if (state.coarseBins[k] > maxBin) { maxBin = state.coarseBins[k]; } } return f64(maxBin) / f64(state.n); } /** * Compute windowed stability (Var_C) */ export function computeWindowedStability(state: DiscreteObserverState): f64 { let count: i32 = 0; let sum: f64 = 0.0; for (let i: i32 = 0; i < DISCRETE_CONFIG.H; i++) { if (state.cohHist[i] > 0.0) { sum += state.cohHist[i]; count++; } } if (count < 2) return 0.0; const mean = sum / f64(count); let variance: f64 = 0.0; for (let i: i32 = 0; i < DISCRETE_CONFIG.H; i++) { if (state.cohHist[i] > 0.0) { const diff = state.cohHist[i] - mean; variance += diff * diff; } } return variance / f64(count); } /** * Get active oscillator indices (amplitude > A_max/2) */ export function getActiveIndices(state: DiscreteObserverState): Int32Array { const threshold = DISCRETE_CONFIG.A_max / 2.0; let count: i32 = 0; for (let i: i32 = 0; i < state.n; i++) { if (state.A[i] > threshold) { state.lastActive[count] = i; count++; } } state.lastActiveCount = count; // Return trimmed array const result = new Int32Array(count); for (let i: i32 = 0; i < count; i++) { result[i] = state.lastActive[i]; } return result; } /** * Get active oscillator indices for learning (lower threshold) * Uses learningThreshold (15% of A_max) so learning can occur * even when oscillators are decaying after a boost */ export function getActiveIndicesForLearning(state: DiscreteObserverState): Int32Array { const threshold = DISCRETE_CONFIG.A_max * DISCRETE_CONFIG.learningThreshold; let count: i32 = 0; // Use temporary array for learning-specific indices const tempActive = new Int32Array(state.n); for (let i: i32 = 0; i < state.n; i++) { if (state.A[i] > threshold) { tempActive[count] = i; count++; } } // Return trimmed array const result = new Int32Array(count); for (let i: i32 = 0; i < count; i++) { result[i] = tempActive[i]; } return result; } /** * Map prime to SMF axis (Section 4.3) */ export function primeToSMFAxis(prime: i32): i32 { // Use prime mod 16 for axis mapping // Special cases for Enochian primes if (prime == 2) return 0; // coherence if (prime == 3) return 1; // identity if (prime == 5) return 3; // structure if (prime == 7) return 6; // harmony (Enochian) if (prime == 11) return 10; // truth (Enochian) if (prime == 13) return 12; // power (Enochian) return prime % 16; } /** * Compute composition vector Comp(u, v) for SMF update */ export function compositionVector(u: i32, v: i32): Int8Array { const comp = new Int8Array(16); const seed = u * 17 + v * 31; for (let i: i32 = 0; i < 16; i++) { const val = ((seed * (i + 1) * 7) % 5) - 2; comp[i] = i8(val > 0 ? 1 : (val < 0 ? -1 : 0)); } return comp; } /** * Normalize SMF to bounded norm */ export function normalizeSMF(state: DiscreteObserverState): void { const L = DISCRETE_CONFIG.L; let normSq: i64 = 0; for (let k: i32 = 0; k < 16; k++) { normSq += i64(state.s[k]) * i64(state.s[k]); } const norm = Math.sqrt(f64(normSq)); if (norm > f64(L)) { const scale = f64(L) / norm; for (let k: i32 = 0; k < 16; k++) { state.s[k] = i32(Math.round(f64(state.s[k]) * scale)); if (state.s[k] > L) state.s[k] = L; if (state.s[k] < -L) state.s[k] = -L; } } } /** * Compute SMF entropy */ export function computeSmfEntropy(state: DiscreteObserverState): f64 { let absSum: f64 = 0.0; for (let k: i32 = 0; k < 16; k++) { absSum += Math.abs(f64(state.s[k])); } if (absSum < 0.001) return 0.0; let entropy: f64 = 0.0; for (let k: i32 = 0; k < 16; k++) { const pi_k = Math.abs(f64(state.s[k])) / absSum; if (pi_k > 0.0001) { entropy -= pi_k * Math.log(pi_k); } } return entropy; } /** * Update SMF with composition vectors from active chord */ export function updateSMF(state: DiscreteObserverState, activeIndices: Int32Array): void { if (activeIndices.length < 2) return; const L = DISCRETE_CONFIG.L; // Map active primes to SMF axes const axes = new Int32Array(activeIndices.length); for (let i: i32 = 0; i < activeIndices.length; i++) { axes[i] = primeToSMFAxis(state.primes[activeIndices[i]]); } // Apply pairwise composition vectors for (let i: i32 = 0; i < axes.length - 1; i++) { const u = axes[i]; const v = axes[i + 1]; const comp = compositionVector(u, v); for (let k: i32 = 0; k < 16; k++) { state.s[k] = state.s[k] + i32(comp[k]); if (state.s[k] > L) state.s[k] = L; if (state.s[k] < -L) state.s[k] = -L; } } normalizeSMF(state); } /** * Apply Hebbian learning on coupling graph * Now updates both base J and learned coupling weights * Uses amplitude-weighted learning for stronger patterns */ export function applyHebbianLearning( state: DiscreteObserverState, activeIndices: Int32Array ): bool { const J_max = i8(DISCRETE_CONFIG.J_max); const eta = DISCRETE_CONFIG.learningRate; const A_max = DISCRETE_CONFIG.A_max; let updated = false; // Update base coupling (original behavior) for (let ai: i32 = 0; ai < activeIndices.length; ai++) { const i = activeIndices[ai]; for (let aj: i32 = 0; aj < activeIndices.length; aj++) { const j = activeIndices[aj]; if (i == j) continue; const idx = i * state.n + j; if (state.J[idx] < J_max) { state.J[idx] = state.J[idx] + 1; updated = true; } } } // Update learned coupling weights (NEW - amplitude-weighted Hebbian learning) for (let ai: i32 = 0; ai < activeIndices.length; ai++) { const i = activeIndices[ai]; const amp_i = state.A[i] / A_max; for (let aj: i32 = ai + 1; aj < activeIndices.length; aj++) { const j = activeIndices[aj]; const amp_j = state.A[j] / A_max; // Both oscillators are active - strengthen connection const delta = f32(eta * amp_i * amp_j); const idx_ij = i * state.n + j; const idx_ji = j * state.n + i; // Update symmetric learned coupling (bounded to 5.0) state.learnedJ[idx_ij] = f32(Math.min(5.0, f64(state.learnedJ[idx_ij]) + f64(delta))); state.learnedJ[idx_ji] = f32(Math.min(5.0, f64(state.learnedJ[idx_ji]) + f64(delta))); updated = true; } } // Update auxiliary weights for active oscillators const W_max = DISCRETE_CONFIG.W_max; for (let ai: i32 = 0; ai < activeIndices.length; ai++) { const i = activeIndices[ai]; if (state.w[i] < W_max) { state.w[i] = state.w[i] + 1; } } return updated; } /** * Decay learned coupling weights (gentle decay to preserve patterns) */ export function decayLearnedCoupling(state: DiscreteObserverState, rate: f64 = 0.999): void { for (let i: i32 = 0; i < state.learnedJ.length; i++) { state.learnedJ[i] = f32(f64(state.learnedJ[i]) * rate); } } /** * Get learned coupling weight between two oscillators */ export function getLearnedCoupling(state: DiscreteObserverState, i: i32, j: i32): f32 { if (i < 0 || i >= state.n || j < 0 || j >= state.n) return 0.0; return state.learnedJ[i * state.n + j]; } /** * Get total learned coupling strength (sum of all positive weights) */ export function getLearnedCouplingStrength(state: DiscreteObserverState): f64 { let total: f64 = 0.0; for (let i: i32 = 0; i < state.learnedJ.length; i++) { if (state.learnedJ[i] > 0.0) { total += f64(state.learnedJ[i]); } } // Normalize by dividing by max possible (n*n*5) const maxPossible = f64(state.n * state.n) * 5.0; return total / maxPossible; } /** * Detect lockup condition */ export function detectLockup(state: DiscreteObserverState, dC: f64): bool { const highCoherence = state.lastCoherence >= DISCRETE_CONFIG.C_lock; // Track novelty in window state.watchdogNoveltyWindow[state.watchdogIdx] = dC; state.watchdogIdx = (state.watchdogIdx + 1) % DISCRETE_CONFIG.H; // Compute mean dC let meanDC: f64 = 0.0; for (let i: i32 = 0; i < DISCRETE_CONFIG.H; i++) { meanDC += state.watchdogNoveltyWindow[i]; } meanDC /= f64(DISCRETE_CONFIG.H); const lowNovelty = meanDC <= DISCRETE_CONFIG.dC_lock; if (highCoherence && lowNovelty) { state.lockupCounter++; } else { if (state.lockupCounter > 0) state.lockupCounter--; } return state.lockupCounter >= 5; } /** * Apply controlled tunneling to break lockup * GENTLER approach to preserve learned patterns */ export function applyControlledTunneling(state: DiscreteObserverState): void { const M = DISCRETE_CONFIG.M; const L = DISCRETE_CONFIG.L; const perturbStrength = DISCRETE_CONFIG.perturbationStrength; // Perturb SMF (gentle) for (let k: i32 = 0; k < 16; k++) { const eta = i32(Math.floor(Math.random() * 5.0)) - 2; state.s[k] = state.s[k] + eta; if (state.s[k] > L) state.s[k] = L; if (state.s[k] < -L) state.s[k] = -L; } normalizeSMF(state); // Count active oscillators const A_max = DISCRETE_CONFIG.A_max; let activeCount: i32 = 0; for (let i: i32 = 0; i < state.n; i++) { if (state.A[i] > A_max / 2.0) { activeCount++; } } // GENTLER: Only dampen if we have way too many active if (activeCount > 6) { for (let i: i32 = 0; i < state.n; i++) { if (state.A[i] > A_max / 2.0 && Math.random() < 0.3) { state.A[i] *= 0.5; // Moderate damping } } } // Smaller phase shifts to avoid disrupting coherent clusters for (let i: i32 = 0; i < state.n; i++) { if (Math.random() < perturbStrength * 0.5) { const direction: i32 = (i % 2 == 0) ? 1 : -1; const shift = i32(Math.floor(Math.random() * f64(M / 6))) * direction; state.phi[i] = (state.phi[i] + shift + M) % M; } } // IMPORTANT: Very gentle decay of learned coupling (0.995 = 0.5% decay) // This preserves learned patterns while still allowing escape from lockup decayLearnedCoupling(state, 0.995); state.lockupCounter = 0; state.stuckCounter = 0; } // ============================================================================ // Main Step Function // ============================================================================ export class DiscreteStepResult { coherence: f64; dC: f64; Var_C: f64; tick: bool; locked: bool; activeCount: i32; constructor( coherence: f64, dC: f64, Var_C: f64, tick: bool, locked: bool, activeCount: i32 ) { this.coherence = coherence; this.dC = dC; this.Var_C = Var_C; this.tick = tick; this.locked = locked; this.activeCount = activeCount; } } /** * Main discrete step function * Implements full discrete.pdf specification */ export function discreteStep( state: DiscreteObserverState, driveInput: Float64Array | null = null, plasticity: bool = false ): DiscreteStepResult { const M = DISCRETE_CONFIG.M; const B = DISCRETE_CONFIG.B; const A_max = DISCRETE_CONFIG.A_max; const delta = DISCRETE_CONFIG.delta; const c = DISCRETE_CONFIG.c; const d = DISCRETE_CONFIG.d; const W_max = DISCRETE_CONFIG.W_max; state.t++; if (state.tunnelCooldownRemaining > 0) { state.tunnelCooldownRemaining--; } // ======================================== // Phase 1: Update oscillators // ======================================== const nextPhi = new Int32Array(state.n); const binSize = M / B; const binCounts = new Int32Array(B); const oscillatorBins = new Int32Array(state.n); // Compute bin memberships for (let i: i32 = 0; i < state.n; i++) { let bin = state.phi[i] / binSize; if (bin >= B) bin = B - 1; oscillatorBins[i] = bin; binCounts[bin]++; } // Find dominant bin let maxBinCount: i32 = 0; let dominantBin: i32 = 0; for (let b: i32 = 0; b < B; b++) { if (binCounts[b] > maxBinCount) { maxBinCount = binCounts[b]; dominantBin = b; } } // Update each oscillator for (let i: i32 = 0; i < state.n; i++) { const p = state.primes[i]; // Discrete phase increment: ∆_p ≡ (c * ln(p) * 10 + d) mod M // Using log-scaled version for better frequency distribution const logP = Math.log(f64(p)); const delta_p = (i32(Math.floor(f64(c) * logP * 10.0)) + d) % M; // Discrete coupling term const coupling = computeDiscreteCoupling(state, i); // Update phase (modular) nextPhi[i] = (state.phi[i] + delta_p + coupling + M) % M; // Compute coherence bonus based on bin membership const myBin = oscillatorBins[i]; const inDominantBin = myBin == dominantBin; const samePhaseCount = binCounts[myBin]; let coherenceBonus: f64 = 0.0; if (inDominantBin) { if (samePhaseCount >= 7) { coherenceBonus = 2.5; } else if (samePhaseCount >= 5) { coherenceBonus = 2.0; } else if (samePhaseCount >= 3) { coherenceBonus = 1.5; } else if (samePhaseCount >= 2) { coherenceBonus = 0.8; } } // External drive let drive: f64 = 0.0; if (driveInput != null && i < driveInput.length) { drive = driveInput[i]; } // Update amplitude with decay, coherence bonus, and drive state.A[i] = state.A[i] - delta + coherenceBonus + drive; if (state.A[i] < 0.0) state.A[i] = 0.0; if (state.A[i] > A_max) state.A[i] = A_max; // Update auxiliary weight based on amplitude if (state.A[i] > A_max / 2.0) { if (state.w[i] < W_max) state.w[i]++; } else { if (state.w[i] > -W_max) state.w[i]--; } } // Commit phase updates for (let i: i32 = 0; i < state.n; i++) { state.phi[i] = nextPhi[i]; } // ======================================== // Phase 2: Compute coherence metrics // ======================================== const C_bin = computeHistogramCoherence(state); // Track in history const prevCoherence = state.lastCoherence; state.cohHist[state.cohHistIdx] = C_bin; state.cohHistIdx = (state.cohHistIdx + 1) % DISCRETE_CONFIG.H; state.lastCoherence = C_bin; const dC = Math.abs(C_bin - prevCoherence); const Var_C = computeWindowedStability(state); // ======================================== // Phase 3: Learning (uses lower threshold for better learning) // ======================================== if (plasticity) { // Use lower threshold for learning so it can occur even when oscillators are decaying const activeForLearning = getActiveIndicesForLearning(state); if (activeForLearning.length >= 2) { applyHebbianLearning(state, activeForLearning); } } // ======================================== // Phase 4: Check tick condition // ======================================== const tick = C_bin >= DISCRETE_CONFIG.C_th && dC <= DISCRETE_CONFIG.epsilon_C && Var_C <= DISCRETE_CONFIG.tau_Var; if (tick) { state.tickCount++; const activeIndices = getActiveIndices(state); // Update SMF with composition vectors updateSMF(state, activeIndices); // Check for lockup const locked = detectLockup(state, dC); if (locked && state.tunnelCooldownRemaining == 0) { applyControlledTunneling(state); state.tunnelCooldownRemaining = DISCRETE_CONFIG.tunnelCooldown; } // Update entropy metrics state.lastSmfEntropy = computeSmfEntropy(state); return new DiscreteStepResult(C_bin, dC, Var_C, true, locked, activeIndices.length); } return new DiscreteStepResult(C_bin, dC, Var_C, false, false, 0); } // ============================================================================ // Boost/Control Functions // ============================================================================ /** * Dampen all amplitudes */ export function dampenAll(state: DiscreteObserverState): void { for (let i: i32 = 0; i < state.n; i++) { state.A[i] *= 0.3; } } /** * Randomize coupling graph */ export function randomizeCoupling(state: DiscreteObserverState): void { for (let i: i32 = 0; i < state.J.length; i++) { state.J[i] = i8(i32(Math.floor(Math.random() * 10.0)) - 5); } } /** * Reset coupling graph to default */ export function resetCoupling(state: DiscreteObserverState): void { for (let i: i32 = 0; i < state.J.length; i++) { state.J[i] = 1; } } /** * Get state metrics */ export function getStateMetrics(state: DiscreteObserverState): Float64Array { const metrics = new Float64Array(8); metrics[0] = state.lastCoherence; metrics[1] = computeWindowedStability(state); metrics[2] = state.lastSmfEntropy; metrics[3] = f64(state.tickCount); metrics[4] = f64(state.lockupCounter); metrics[5] = f64(state.t); metrics[6] = f64(state.lastActiveCount); metrics[7] = state.lastHqeEntropy; return metrics; } /** * Get phases array */ export function getPhases(state: DiscreteObserverState): Int32Array { return state.phi; } /** * Get amplitudes array */ export function getAmplitudes(state: DiscreteObserverState): Float64Array { return state.A; } /** * Get SMF state */ export function getSMF(state: DiscreteObserverState): Int32Array { return state.s; } /** * Get auxiliary weights */ export function getWeights(state: DiscreteObserverState): Int32Array { return state.w; } /** * Check if locked up */ export function isLockedUp(state: DiscreteObserverState): bool { return state.lockupCounter >= 5; } // ============================================================================ // WASM Export Functions (Global state management) // ============================================================================ let _globalState: DiscreteObserverState = new DiscreteObserverState(null); /** * Create a new discrete observer with given primes */ export function createDiscreteObserver(numPrimes: i32): void { if (numPrimes <= 0) { _globalState = new DiscreteObserverState(null); return; } const primes = new Int32Array(numPrimes); let candidate: i32 = 2; let count: i32 = 0; while (count < numPrimes) { if (isPrime(candidate)) { primes[count] = candidate; count++; } candidate++; } _globalState = new DiscreteObserverState(primes); } function isPrime(n: i32): bool { if (n < 2) return false; if (n == 2) return true; if (n % 2 == 0) return false; const limit = i32(Math.sqrt(f64(n))) + 1; for (let i: i32 = 3; i <= limit; i += 2) { if (n % i == 0) return false; } return true; } /** * Execute a step */ export function discreteObserverStep(plasticity: i32): i32 { const result = discreteStep(_globalState, null, plasticity != 0); return result.tick ? 1 : 0; } /** * Boost an oscillator by index */ export function discreteObserverBoost(index: i32): void { boostIndex(_globalState, index); } /** * Get coherence */ export function discreteObserverGetCoherence(): f64 { return _globalState.lastCoherence; } /** * Get phase of oscillator */ export function discreteObserverGetPhase(index: i32): i32 { if (index >= 0 && index < _globalState.n) { return _globalState.phi[index]; } return 0; } /** * Get amplitude of oscillator */ export function discreteObserverGetAmplitude(index: i32): f64 { if (index >= 0 && index < _globalState.n) { return _globalState.A[index]; } return 0.0; } /** * Get SMF axis */ export function discreteObserverGetSMFAxis(index: i32): i32 { if (index >= 0 && index < 16) { return _globalState.s[index]; } return 0; } /** * Get tick count */ export function discreteObserverGetTickCount(): i64 { return _globalState.tickCount; } /** * Get entropy */ export function discreteObserverGetEntropy(): f64 { return computeSmfEntropy(_globalState); } /** * Reset the observer */ export function discreteObserverReset(): void { _globalState = new DiscreteObserverState(null); } /** * Get number of oscillators */ export function discreteObserverGetCount(): i32 { return _globalState.n; } /** * Get state as JSON */ export function discreteObserverGetState(): string { const builder = new JSONBuilder(); builder.startObject() .addNumberField("coherence", _globalState.lastCoherence) .addNumberField("entropy", computeSmfEntropy(_globalState)) .addNumberField("tickCount", f64(_globalState.tickCount)) .addNumberField("t", f64(_globalState.t)) .addNumberField("lockupCounter", f64(_globalState.lockupCounter)) .addBooleanField("locked", isLockedUp(_globalState)) .addNumberField("activeCount", f64(_globalState.lastActiveCount)) .addNumberField("learnedCouplingStrength", getLearnedCouplingStrength(_globalState)); // Add SMF axes let smfJson = "["; for (let i: i32 = 0; i < 16; i++) { if (i > 0) smfJson += ","; smfJson += _globalState.s[i].toString(); } smfJson += "]"; builder.addRawField("smf", smfJson); builder.endObject(); return builder.build(); } /** * Get learned coupling strength (WASM export) */ export function discreteObserverGetLearnedCouplingStrength(): f64 { return getLearnedCouplingStrength(_globalState); } /** * Get learned coupling between two oscillators (WASM export) */ export function discreteObserverGetLearnedCoupling(i: i32, j: i32): f32 { return getLearnedCoupling(_globalState, i, j); } /** * Apply Hebbian learning manually (WASM export) */ export function discreteObserverApplyHebbianLearning(): bool { const activeForLearning = getActiveIndicesForLearning(_globalState); if (activeForLearning.length >= 2) { return applyHebbianLearning(_globalState, activeForLearning); } return false; } /** * Decay learned coupling (WASM export) */ export function discreteObserverDecayLearnedCoupling(rate: f64): void { decayLearnedCoupling(_globalState, rate); } /** * Boost a specific prime's amplitude */ export function boostPrime(state: DiscreteObserverState, prime: i32): void { for (let i: i32 = 0; i < state.n; i++) { if (state.primes[i] == prime) { state.A[i] = Math.min(DISCRETE_CONFIG.A_max, state.A[i] + 50.0); return; } } } /** * Boost by index */ export function boostIndex(state: DiscreteObserverState, index: i32): void { if (index >= 0 && index < state.n) { state.A[index] = Math.min(DISCRETE_CONFIG.A_max, state.A[index] + 50.0); } }