Skip to content

Latest commit

 

History

134 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🧠 Brain

Minimal neuroevolution library. A Genome is a list of synapse genes (sourceLayer, sourceIndex, sinkLayer, sinkIndex, weight); a Brain builds the network from it, prunes paths that never reach an output, and feeds inputs through it.

Brain.feed() fires synapses sequentially in gene order (not topologically) — this is by design: gene order is part of the phenotype and evolution can exploit it. Every other execution backend in this library reproduces that exact behavior.

Install

pnpm add github:c0sm1cdus7/brain

The package compiles itself on install (postinstall: tsc), so consumers need no build step.

Quick start

import { Genome, Brain } from "brain";

const genome = Genome.create(300, {
    inputLayerLength: 32,
    hiddenLayers: 1,
    outputLayerLength: 2
});

const brain = new Brain(genome);
const output = brain.feed(input); // number[] of length <= outputLayerLength

const offspring = Genome.crossover(parentA, parentB, mutationRate);

Batch execution: BrainPack (CPU)

BrainPack flattens many brains into contiguous typed arrays and replays the exact Brain.feed() operations over Float64Array — results are bit-identical to Brain.feed() (spec-enforced) and faster, since it skips the object graph.

import { BrainPack } from "brain";

const pack = BrainPack.fromGenomes(genomes);
const outputs = pack.feedAll(inputs); // one input vector per agent
const one = pack.feed(agentIndex, input);

GPU execution: GpuBrainPack (WebGPU)

One GPU thread per agent; each thread walks its synapse chain in exact gene order. Backed by Dawn via the optional webgpu package — no browser involved: it targets Metal on macOS, Vulkan on Linux and D3D12 on Windows, and runs headless (a display server is not required; on Linux servers the vendor GPU driver with its Vulkan ICD is enough).

pnpm add webgpu   # optional peer dependency, only where you want GPU execution
import { BrainPack, GpuRuntime } from "brain";

const runtime = await GpuRuntime.create(); // null if webgpu isn't installed or no GPU exists
const feeder = runtime ? runtime.createPack(BrainPack.fromGenomes(genomes)) : BrainPack.fromGenomes(genomes);

const outputs = await feeder.feedAll(inputs);

feeder.destroy?.();  // per generation (frees GPU buffers)
runtime?.destroy();  // when done entirely
console.log(runtime?.description); // e.g. "apple metal-3 apple-m1-pro"

Precision: GPUs expose no float64, so the GPU path computes in float32 — same operations, same order, ~1e-6 output drift vs the CPU float64 path (spec-enforced tolerance, and verified to not flip ±0.5 threshold decisions away from the threshold). Results are deterministic run-to-run on the same machine; different GPU vendors may differ in the last ULP.

Plugging into a simulation

Anything that feeds populations can accept a BatchFeeder (implemented by both packs). The bundled grid-world Simulation takes a feederFactory per generation:

const simulation = new Simulation(mapSize, {
    ...options,
    feederFactory: (genomes) => runtime.createPack(BrainPack.fromGenomes(genomes))
});
await simulation.run(steps);

Scripts

pnpm test    # vitest: bit-exactness, GPU parity, evolution specs (GPU specs skip gracefully without a GPU)
pnpm start   # demo: Brain vs BrainPack vs GpuBrainPack timings + parity on this machine

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages