brain-lab / brain_lab /static /brain-activity-models.js
Chungulus's picture
Deploy Brain Lab CPU classroom app with verified saved research playback
4155f6b verified
Raw History Blame Contribute Delete
2.45 kB
/** Small, deterministic teaching models. No recorded neural or participant data. */
export class NeuronModel {
constructor(){this.reset()}
reset(){this.t=0;this.v=-65;this.refractory=0;this.spikes=[];this.count=0;this.trace=[{t:0,v:-65,spike:false}];}
pulse(amount=8,{threshold=-50,refractory=4}={}){
if(this.refractory>0)return false;
this.v+=amount;if(this.v>=threshold){this.count++;this.spikes.push(this.t);this.v=-65;this.refractory=refractory;this.trace.push({t:this.t,v:20,spike:true});}
return true;
}
step(duration,{excitation=18,inhibition=0,threshold=-50,tau=20,refractory=4,pattern='steady'}={}){
if(!Number.isFinite(duration)||duration<0||duration>10000)throw Error('Choose a simulation interval between 0 and 10,000 ms.');
if(!(tau>0)||refractory<0||threshold<=-65)throw Error('Invalid neuron settings.');
const end=this.t+duration;
while(this.t<end-1e-8){const dt=Math.min(.5,end-this.t);let spike=false;
if(this.refractory>0){this.refractory=Math.max(0,this.refractory-dt);this.v=-65;}
else {const input=(pattern==='steady'||this.t%100<40?excitation:0)-inhibition,target=-65+input;
this.v=target+(this.v-target)*Math.exp(-dt/tau);
if(this.v>=threshold){spike=true;this.count++;this.spikes.push(this.t+dt);this.v=-65;this.refractory=refractory;}
}
this.t+=dt;this.trace.push({t:this.t,v:spike?20:this.v,spike});
}
this.trace=this.trace.filter(row=>row.t>=this.t-1000);this.spikes=this.spikes.filter(t=>t>=this.t-1000);
return this;
}
rate(){const window=Math.min(500,this.t);return window>0?this.spikes.filter(t=>t>this.t-window).length*1000/window:0;}
}
export class AssociationModel {
constructor(){this.reset()}
reset(){this.weights={A:0,B:0};this.history=[];}
trial(cues=['A'],outcome=1,rate=.2){
if(!Array.isArray(cues)||!cues.length||new Set(cues).size!==cues.length||cues.some(c=>!['A','B'].includes(c)))throw Error('Choose cue A, B, or A + B.');
if(![0,1].includes(outcome)||!(rate>0&&rate<=.5))throw Error('Invalid learning settings.');
if(this.history.length>=5000)throw Error('This run has reached 5,000 trials. Reset learning to start another.');
const prediction=cues.reduce((sum,cue)=>sum+this.weights[cue],0),error=outcome-prediction;
for(const cue of cues)this.weights[cue]+=rate*error;
const row={trial:this.history.length+1,cues:cues.join('+'),outcome,prediction,error,A:this.weights.A,B:this.weights.B,rate};this.history.push(row);return row;
}
}