← Natura·Docs/Modules/Source

sync-test.js

62 lines · in natura-src-1.0.0.zip as natura/sync-test.js · a test

// sync-test.js -- assertions for SYNCHRONIZATION (the Kuramoto model). Run: node sync-test.js
const N = require('./natura.js');
const { makeSync } = require('./sync.js');
const { World, UNITS } = N;
const DAY = UNITS.DAY;
let pass = 0, fail = 0;
const ok = (c, m) => { if (c) pass++; else { fail++; console.log('  FAIL: ' + m); } };
const build = (cfg) => World({ seed: 6, systems: [makeSync(Object.assign({ N: 200, seed: 6, meanOmega: 6, freqSpread: 1.0 }, cfg))] });
const run = (w, days) => { for (let i = 0; i < days / 10; i++) w.step(10 * DAY); };

console.log('sync assertions:');

// 1. DETERMINISM
{
  const a = build({ K: 3 }), b = build({ K: 3 }); run(a, 300); run(b, 300);
  const fa = a.fields().sync.phase, fb = b.fields().sync.phase; let same = true;
  for (let k = 0; k < 200; k++) if (fa[k] !== fb[k]) { same = false; break; }
  ok(same, 'same seed -> identical phases (reproducible)');
}

// 2. THE SYNCHRONIZATION TRANSITION -- weak coupling stays NOISE, strong coupling LOCKS into one beat
{
  const weak = build({ K: 0.2 }), strong = build({ K: 6 }); run(weak, 400); run(strong, 400);
  const rw = weak.fields().sync.R, rs = strong.fields().sync.R;
  ok(rw < 0.4, 'below the critical coupling the crowd stays INCOHERENT (R ' + rw + ')');
  ok(rs > 0.9, 'above it the crowd spontaneously LOCKS into one rhythm (R ' + rs + ')');
  ok(rs > rw + 0.5, 'coupling drives the order parameter up (R ' + rw + ' -> ' + rs + ')');
}

// 3. IT IS A THRESHOLD -- sweep K; R stays low then climbs (a phase transition, not linear)
{
  let prev = 0, monotone = true, jumped = false;
  for (const K of [0.2, 0.8, 1.6, 2.4, 4, 6]) { const w = build({ K }); run(w, 350); const R = w.fields().sync.R;
    if (R < prev - 0.08) monotone = false; if (R - prev > 0.2) jumped = true; prev = R; }
  ok(monotone && jumped, 'R rises through a critical band as K grows (a genuine synchronization threshold)');
}

// 4. IDENTICAL OSCILLATORS lock even at faint coupling; a WIDE spread of tempos resists locking
{
  const same = build({ K: 1.0, freqSpread: 0 }); run(same, 400);
  ok(same.fields().sync.R > 0.9, 'identical tempos synchronize even at faint coupling (R ' + same.fields().sync.R + ')');
  const wide = build({ K: 1.0, freqSpread: 4.0 }); run(wide, 400);
  ok(wide.fields().sync.R < 0.6, 'a wide spread of natural tempos resists the SAME coupling (R ' + wide.fields().sync.R + ')');
}

// 5. the readout states: a strong crowd reads LOCKED, a weak one INCOHERENT
{
  const strong = build({ K: 6 }); run(strong, 400);
  ok(strong.fields().sync.R > 0.9, 'a strongly-coupled crowd reads LOCKED (R>0.9)');
  const weak = build({ K: 0.2 }); run(weak, 400);
  ok(weak.fields().sync.R < 0.25, 'a weakly-coupled crowd reads INCOHERENT (R<0.25)');
}

// 6. CHIMERA -- on a ring with a phase lag near pi/2, order and chaos COEXIST (a coherent domain beside an incoherent one)
{
  const w = build({ N: 256, mode: 'ring', neighbours: 40, phaseLag: 1.45, freqSpread: 0, K: 6 }); run(w, 500);
  const f = w.fields().sync;
  ok(f.localRmax > 0.8 && f.localRmin < 0.5, 'a CHIMERA forms: a synchronized domain (localR ' + f.localRmax + ') beside an incoherent one (localR ' + f.localRmin + ') -- from identical oscillators');
}

console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);