Hi, your work and games really inspire me. I’m digging around with VNC flies, and I ended up making my own viewer and engine for a spike neural network. You select edges, run them, and see the signal traces. I’m also studying how a brain cell understands where to grow from its genome.
PROMETHEUS
> “DNA is not firmware, but a generator that writes firmware.
> Half hardwired (innate), half through “run and let it learn.”
Here is the **reference genome of Drosophila melanogaster** (Release 6, RefSeq
GCF_000001215.4) and the bridge from it to our fly connectome: gene →
neurotransmitter → connection symbol.
Files
| file | what is this |
|— |——————————————————|
| `drosophila_genome.fna.gz` | full DNA, 144 MB A/C/G/T (44 MB compressed) |
| `drosophila_genes.gff.gz` | annotation of 17,537 genes |
| `dna_nt_map.py` | extracts NT genes + raw DNA, compiles a table |
| `dna_nt_map.csv` | result: gene → NT → connectit class → sign |
Why is this?
All day in the connectome, we’ve been fixing the **connection sign** (excitation/inhibition) – and it’s not from the
EM scan, it’s from the **DNA**. Each neuron makes its own neurotransmitter based on a gene,
and these genes are:
| gene | neurotransmitter | sign (in flies) | role |
|——-|——————–|—————-|—————–|
| ChAT | acetylcholine | **+ excit.** | ACh synthesis |
| VGlut | glutamate | **− inhibitor.| glutamate transporter |
| Gad1 | gaba | *− inhibitor.*| GABA synthesis |
| VGAT | gaba | **− inhibitor.** | GABA transporter |
| ple | dopamine | + | tyrosine hydroxylase |
| DAT | dopamine | + | DA transporter |
| Tdc2 / Tbh | octopamine | + | octopamine synthesis |
| SerT | serotonin | + | 5HT transporter |
Plus morphogens (dpp/wg/hh) and axon guidance (fra/comm)—the “developmental script”
by which the brain assembles itself.
## Key Idea
“`
4 letters → protein → neurotransmitter → connection symbol (in .taas nt_types)
“`
**There is no synapse strength in the genome.** There is `dpp` (where to grow), `fra` (where to extend
the axon), `Gad1` (what symbol)—but no “synapse #3 184 223 = 0.7”. Therefore, we learn the weights (linker, R-STDP), and read the symbol from DNA.
You probably developed a cool gene generator in the new game phantasia life. I saw how llamas walk and I suspect you made a cool spike or burst engine. Their walking is amazing in its continuity. I would like to talk to you or work with my TAAS spike engine.
and the coolest thing is the integration with game engines and WASM
taas— Zero-Cost SIMD Neural Dynamics
High-performance neural simulation engine with SIMD-first architecture and type-safe model construction.
Features
Zero-cost abstractions SIMD operations compile to single CPU instructions
Type-safe builders No magic parameter indices, named configuration
Pre-configured patterns Izhikevich RS/FS/IB/CH, LIF, AdEx, HH, and more
Batch processing Process 8 neurons per instruction (AVX2)
Flexible architecture From single neurons to large-scale networks
Builder API
“`rust
use pillars::prelude::*;
// Create 1000 Regular Spiking neurons
let mut pop = IzhikevichBuilder::new(1000)
.dt(0.5)
.pattern(IzhPattern::RegularSpiking)
.build();
// Simulate
let input = vec![10.0; 1000];
let mut spikes = vec![0i32; 1000];
pop.step(&input, &mut spikes);
“`
### Available Builders
#### Izhikevich Neurons
“`rust
// Pre-configured patterns
let pop = IzhikevichBuilder::new(1000)
.pattern(IzhPattern::RegularSpiking) // RS, FS, IB, CH, LTS, TC, RZ
.dt(0.5)
.v_thresh(30.0)
.build();
// Custom parameters
let pop = IzhikevichBuilder::new(1000)
.a(0.02)
.b(0.2)
.c(-65.0)
.d(8.0)
.build();
“`
#### LIF Neurons
“`rust
let pop = LIFBuilder::new(2000)
.dt(0.1)
.tau_m(10.0) // Membrane time constant (ms)
.v_thresh(-54.0) // Spike threshold (mV)
.v_rest(-70.0) // Resting potential (mV)
.v_reset(-70.0) // Reset potential (mV)
.tau_ref(2.0) // Refractory period (ms)
.build();
“`
#### AdEx Neurons
“`rust
let pop = AdExBuilder::new(1000)
.dt(0.05)
.tau_m(5.0) // Membrane time constant (ms)
.tau_w(100.0) // Adaptation time constant (ms)
.delta_t(2.0) // Exponential slope factor (mV)
.a(0.5) // Subthreshold adaptation (nS)
.b(7.0) // Spike-triggered adaptation (nA)
.build();
“`
### Manual Construction (Advanced)
“`rust
use pillars::prelude::*;
let mut pop = NeuralPopulation::<IzhikevichModel>::new(1000);
pop.fill_param(0, 0.5); // dt
pop.fill_param(1, 0.02); // a
pop.fill_param(2, 0.2); // b
// … (not recommended for new code)
“`
## Performance
– **8x throughput**: Process 8 neurons per SIMD instruction
– **FMA optimization**: Fused multiply-add for critical paths
– **Cache-friendly**: Column-major SoA layout
– **Zero allocation**: Pre-allocated aligned buffers
### Benchmark Results
“`
LIF (1M neurons): ~2.5 ms/step (AVX2)
Izhikevich (1M): ~3.8 ms/step (AVX2)
AdEx (1M): ~4.2 ms/step (AVX2)
Hodgkin-Huxley (1M): ~12 ms/step (AVX2)
“`
## Architecture
“`
User Code
↓
api/Builder ← Type-safe construction
↓
api/NeuralPopulation ← Safe wrapper
↓
zoo/Models ← Biological models (LIF, HH, STDP)
↓
ops/Primitives ← ohmic, vtrap, sigmoid
↓
core/SimdLane ← Generic SIMD abstraction
↓
Hardware (AVX2/NEON)
“Hi John! I’ve been deep in the ‘digital laboratory’ lately. After our talks, I realized that simple neural models just don’t cut it for true life-like behavior.I’ve developed a new model in Rust called HAAP (Hyperpolarization-Activated Adaptive Pulse-Bursting). It implements M-currents and HCN channels to get that authentic spike-frequency adaptation and rebound bursts. It feels much closer to the ‘biological spirit’ of Creatures than anything I’ve tried before.My adventure in the Yakutian tundra still haunts my work—seeing those mammoth remains made me realize how much ‘history’ is stored in biological systems. Trying to translate that into code for ‘Who Am I?’ is a challenge, but a rewarding one. Hope all is well!”
//!
//! Uses `#[derive(NeuronModel)]` + `ComputeNeuron` for typed API.
//!
//! # Principles
//! – **Ohm’s Law**: I = g·(V − E_rev), subtracted from I_inj
//! – **Forward Euler**: no gate_integrate, no hidden abstractions
//! – **Zero divisions**: all tau stored as inv_tau at init
//!
//! Equations:
//! “`text
//! I_leak = g_L·(V − E_L)
//! I_M = g_M·u·(V − E_K)
//! I_h = g_h·h·(V − E_h)
//! C_m·dV/dt = I_syn − I_leak − I_M − I_h
//! du/dt = (u_∞(V) − u) · inv_tau_u (Forward Euler)
//! dh/dt = (h_∞(V) − h) · inv_tau_h (Forward Euler)
//! Spike: V ≥ V_thresh → V = V_reset, u += delta_u
//! “`
//!
//! # Gate steady states (standard Boltzmann — `k` is the slope factor, a divisor)
//! “`text
//! u_∞(V) = 1 / (1 + exp(−(V − V_half_u) / k_u)) M-current (opens on depolarization)
//! h_∞(V) = 1 / (1 + exp(+(V − V_half_h) / k_h)) HCN (opens on hyperpolarization)
//! “`
//! This is the biologically correct convention, matching the model’s name and
//! purpose: `h` gives hyperpolarization-activated rebound, `u` gives
//! depolarization-activated spike-frequency adaptation. Because `k` divides,
//! the exponent argument stays small for physiological `V` (|arg| ≲ 40) and
//! cannot overflow.
#![allow(clippy::module_inception)] // deliberate: `pub mod haap` mirrors the file module
use pillars_macros::dual_neuron;
// ═══════════════════════════════════════════════════════════════
// ═══════════════════════════════════════════════════════════════
/// Hyperpolarization-activated adaptive bursting neuron.
///
/// # States (3)
/// – `v`: membrane potential (mV)
/// – `u`: M-current activation (0..1)
/// – `h`: h-current activation (0..1)
///
/// # Params (17)
/// – `dt`, `inv_cm`, `g_l`, `e_l`, `g_m`, `e_k`, `g_h`, `e_h`,
/// `inv_tau_u`, `inv_tau_h`, `v_half_u`, `k_u`, `v_half_h`,
/// `k_h`, `v_thresh`, `v_reset`, `delta_u`
#[dual_neuron]
pub mod haap {
use crate::core::compute::{ComputeNeuron, StepContext};
use crate::core::lane::SimdLane;
use pillars_macros::NeuronModel;
#[derive(NeuronModel)]
pub struct HAAPModel {
#[state]
pub v: f32,
#[state]
pub u: f32,
#[state]
pub h: f32,
#[param]
pub dt: f32,
#[param]
pub inv_cm: f32,
#[param]
pub g_l: f32,
#[param]
pub e_l: f32,
#[param]
pub g_m: f32,
#[param]
pub e_k: f32,
#[param]
pub g_h: f32,
#[param]
pub e_h: f32,
#[param]
pub inv_tau_u: f32,
#[param]
pub inv_tau_h: f32,
#[param]
pub v_half_u: f32,
#[param]
pub k_u: f32,
#[param]
pub v_half_h: f32,
#[param]
pub k_h: f32,
#[param]
pub v_thresh: f32,
#[param]
pub v_reset: f32,
#[param]
pub delta_u: f32,
}
impl ComputeNeuron for HAAPModel {
type State<S: SimdLane> = HAAPState<S>;
type Params<S: SimdLane> = HAAPParams<S>;
#[inline(always)]
fn step<S: SimdLane>(
state: &mut HAAPState<S>,
params: &HAAPParams<S>,
ctx: &mut StepContext<S>,
) {
let one = S::one();
let dt = ctx.dt_simd();
// ─── Ohmic currents: I = g·(V − E), subtracted from I_inj ──
let i_leak = params.g_l * (state.v – params.e_l);
let i_m = params.g_m * state.u * (state.v – params.e_k);
let i_h = params.g_h * state.h * (state.v – params.e_h);
let i_total = i_leak + i_m + i_h;
let dv = (ctx.i_syn – i_total) * params.inv_cm;
let v_new = state.v + dv * dt;
// ─── Gate kinetics (standard Boltzmann: `k` = slope factor, a divisor) ──
// u_∞ = 1/(1+exp(−(V − V_half_u)/k_u)) M-current opens on depolarization
// h_∞ = 1/(1+exp(+(V − V_half_h)/k_h)) HCN opens on hyperpolarization
//
// With `k` dividing, |arg| stays ≲ 40 for physiological V, so exp
// cannot overflow — the earlier “steepness” rewrite (k as a
// multiplier) produced ±300 arguments, which was the real source of
// the overflow it then “fixed” with clamping. The ±50 clamp below
// is a pure safety net for pathological V (far outside biophysics).
let u_arg = (-(v_new – params.v_half_u) / params.k_u)
.max(S::splat(-50.0))
.min(S::splat(50.0));
let u_inf = one / (one + u_arg.exp());
let u_new = (state.u + dt * (u_inf – state.u) * params.inv_tau_u).clamp01();
let h_arg = ((v_new – params.v_half_h) / params.k_h)
.max(S::splat(-50.0))
.min(S::splat(50.0));
let h_inf = one / (one + h_arg.exp());
let h_new = (state.h + dt * (h_inf – state.h) * params.inv_tau_h).clamp01();
// ─── Spike detection & reset ───────────────────────────────
let spike = v_new.cmp_ge(params.v_thresh);
ctx.emit_spike(spike);
state.v = spike.cond().then(params.v_reset).else_(v_new);
let u_bump = (u_new + params.delta_u).clamp01();
state.u = spike.cond().then(u_bump).else_(u_new);
state.h = h_new;
}
}
}
// Re-export so existing paths keep working:
// `pillars::zoo::neurons::haap::HAAPModel` etc.
pub use haap::*;
