architecture/network/evaluation-pack

Deterministic evaluation-pack contracts (Layer 2 — Worker Transport Normalization).

An evaluation pack is the transport-neutral initial state for one evaluation episode. It bundles deterministic typed arrays with the seed and schema version that produced them, so a worker can recreate the exact same starting point on the same runtime. The boundary exists because benchmarks such as multi-agent, competitive, and cooperative tasks all need to ship initial state across a worker boundary, but each benchmark should not invent its own pack, transfer, and versioning rules.

This module owns the generic contract:

The pack is intentionally transport-neutral. It does not know about domain frames, opponent snapshots, or simulation physics; the benchmark's Layer 3 wrapper injects that state after the pack arrives. This split lets Layer 2 stay reusable while each benchmark keeps its own frame format and lifecycle.

Determinism here is bounded to the same runtime. The pack is filled with a self-contained xorshift32 PRNG, so same seed plus same inputs yields the same arrays on the same Node or browser build. Cross-runtime byte identity is not promised because typed-array layout, transfer semantics, and floating-point reduction order can differ between environments.

flowchart TD
  classDef base fill:#08131f,stroke:#1ea7ff,color:#dff6ff,stroke-width:1px;
  classDef accent fill:#0f2233,stroke:#ffd166,color:#fff4cc,stroke-width:1.5px;

  Core[Layer 1 — NGE core<br/>collective evaluation]:::base --> Pack[Layer 2 — generic pack<br/>seed + agentCount + schemaVersion]:::accent
  Pack --> Transfer[postMessage + transfer list<br/>zero-copy worker handoff]:::base
  Transfer --> Wrap[Layer 3 — benchmark wrapper<br/>populateEvaluationFrame, etc.]:::base
  Wrap --> Frame[benchmark frame<br/>EvaluationFrame]:::accent

For background on the PRNG family used to fill the arrays, see Marsaglia, G. (2003), "Xorshift RNGs," Journal of Statistical Software, 8(14), 1–6, https://www.jstatsoft.org/article/view/v008i14, and Wikipedia contributors, Pseudorandom number generator. For background on the worker boundary where the zero-copy transfer list is consumed, see Wikipedia contributors, Web Workers, and MDN Web Docs, Transferable objects.

Example: create a generic pack and resolve its zero-copy transfer list for a worker.

const pack = createDeterministicEvaluationPack(42, {
  agentCount: 4,
  schemaVersion: 'eval-pack-v1',
});
const transferList = resolveTransferList(pack);
worker.postMessage({ type: 'eval', pack }, transferList);

Example: assert a schema version before reading typed-array fields.

assertSchemaVersion(receivedPack, 'eval-pack-v1');
// now safe to read pack.arrays

Practical reading order:

  1. Start here for the generic pack contract, the reproducibility tuple, and the layer map.
  2. Read createDeterministicEvaluationPack for seed-to-array semantics.
  3. Read resolveTransferList for zero-copy transfer-list resolution.
  4. Read assertSchemaVersion for forward-compatibility rejection.

architecture/network/evaluation-pack/network.evaluation-pack.ts

assertSchemaVersion

assertSchemaVersion(
  pack: { schemaVersion: unknown; },
  expectedVersion: string,
): void

Asserts that the schemaVersion field of a pack matches the expected version string. Consumers must call this before reading any typed-array field so that a version mismatch is caught at the boundary rather than silently misinterpreting the packed bytes.

This is the generic version of the domain-specific schema-version guards used by individual benchmarks — the expected version is passed as a parameter so any benchmark can use the same boundary guard. Schema-version sentinels are a common forward-compatibility technique; see Wikipedia contributors, Forward compatibility.

Parameters:

Example:

assertSchemaVersion(receivedPack, 'eval-pack-v1');
// now safe to read typed-array fields

createDeterministicEvaluationPack

createDeterministicEvaluationPack(
  seed: number,
  inputs: EvaluationPackInputs,
): DeterministicEvaluationPack

Constructs a deterministic evaluation pack from a seed and transport-neutral inputs. Identical (seed, inputs) → identical pack on the same runtime (same Node or browser build).

The pack's typed arrays are filled by a self-contained xorshift32 PRNG seeded from seed. The PRNG algorithm matches the same family used in src/neat/rng/core/ but is duplicated here to avoid a cross-layer dependency — Layer 2 must not import NEAT core internals. For background on xorshift32, see Marsaglia, G. (2003), "Xorshift RNGs," Journal of Statistical Software, 8(14), 1–6, https://www.jstatsoft.org/article/view/v008i14.

Generic reproducibility tuple: (seed, agentCount, schemaVersion). Same tuple → identical pack on the same runtime. Cross-runtime byte identity is not promised.

Parameters:

Returns: A DeterministicEvaluationPack whose typed arrays are deterministic functions of (seed, inputs).

Example:

const pack = createDeterministicEvaluationPack(42, {
  agentCount: 4,
  schemaVersion: 'eval-pack-v1',
});
// pack.arrays are byte-identical on every call with the same (seed, inputs)

DeterministicEvaluationPack

Transport-neutral deterministic evaluation pack.

A pack is the transport-neutral deterministic initial state for the generic evaluation-pack arrays. Benchmark-specific episode state such as opponentSnapshot, trackId, or featureFlags is injected by the Layer 3 wrapper after the pack arrives, not by this generic type.

Given the same seed and the same EvaluationPackInputs, the pack's typed arrays are byte-identical within a single runtime (same Node or browser build). Cross-runtime byte identity is not promised.

The arrays field holds every typed array that participates in zero-copy transfer; resolveTransferList walks this collection to build the postMessage transfer list.

EvaluationPackInputs

Transport-neutral inputs consumed by createDeterministicEvaluationPack.

Captures the reproducibility tuple components that are NOT the seed: agentCount (determines typed-array sizes) and schemaVersion (forward- compatibility sentinel). Benchmark-specific inputs (opponent snapshots, simulation physics, etc.) are injected by the benchmark's own wrapper, not by this generic type.

Generic reproducibility tuple: (seed, agentCount, schemaVersion). Same tuple → identical pack on the same runtime.

resolveTransferList

resolveTransferList(
  pack: DeterministicEvaluationPack,
): ArrayBuffer[]

Collects every distinct ArrayBuffer backing a typed-array field in pack into a transfer list for zero-copy postMessage transfer.

Rules:

The transfer list follows the HTML structured-clone transferables contract consumed by Web Workers. For background, see MDN Web Docs, Transferable objects, and Wikipedia contributors, Web Workers.

Parameters:

Returns: Ordered list of ArrayBuffer references for postMessage transfer.

Example:

const pack = createDeterministicEvaluationPack(42, inputs);
const transferList = resolveTransferList(pack);
worker.postMessage({ type: 'eval', pack }, transferList);
Generated from source JSDoc • GitHub