Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

20
Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon

Transcript of Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Page 1: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Introducing LEMS-LiteAn intermediate format for code-generation

Robert Cannon

Page 2: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Executing LEMS models via LEMS-Lite

Familiar old problem– Published descriptions of models in neuroscience are almost

always inadequate for reproducing results.

– You can't do much science if you can't analyze, extend, reuse or build upon other people's work.

Familiar old solution– Declarative model descriptions using basic design principles

form software engineering: separate logic (equations) from data (parameter values); avoid duplication; think about your design and refactor as needed.

– Implemented in LEMS/NeuroML. Related efforts include NineML, SpineML, SBML although these focus more on machine-readability, less on human writeability.

Neuroscience-specific definitionsPhiysics, Geometry

Page 3: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

What next?

How do you execute models?– Map NeuroML/LEMS models to existing tools, such as

Neuron.

– New simulators designed for the LEMS data model. jLEMS, pyLEMS

– Generate code for general purpose compilers.

– Generate code for custom hardware.

Page 4: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Why generate code rather than using Neuron, Moose, Genesis, Brian, PSICS etc?

Exploit novel hardware including FPGAs and GPUs

Work with more diverse models– Neuron already involves code generation, Moose relies on custom

extensions

Efficiency and memory footprint– Eg integerization of models (Mike Hull)

Why not?– Models are like programs. The obvious thing is to compile them, not

just run them on a language simulator.

– The alternative view is that models are like data, to be fed into specialist programs. It all depends on the completeness and diversity of your model specifications.

Reaction against nature of existing tools that is perceived as hindering investigation of novel types of model.

Page 5: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

LEMS

Embeds knowledge of:

Physics – dimensionalityGeometry – containment,

adjacency, trees, 1-D skeleton of 3-D tree

Source code for custom

hardware

Source code for general purpose

hardware

?

Page 6: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

LEMS

Embeds knowledge of:

Physics – dimensionalityGeometry – containment,

adjacency, trees, 1-D skeleton of 3-D tree

Source code for general purpose

hardware

X

Single-step code generation embeds a lot of knowledge – physics, geometry, and numerics.

We've swapped a monolithic simulator for a monolithic code generator.

Page 7: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

LEMS

Physics – dimensionalityGeometry – containment,

adjacency, trees, 1-D skeleton of 3-D tree

Source code for general purpose

hardware

Flat LEMS model Still expressed in LEMSComparable to NineML

LEMS-Lite

Components, Arrays, Update rules, Connections

No geometryNo ODEs

No neuroscience terms Code for custom hardware

Stage 1: Remove physics and Geometry. Keep ODEs

Stage 2:Combine with declarative representation of numericsODEs → update rules

Numerical Integration SchemesEuler, RK4, Crank Nicolson etc

PhysicsMathematics

Computing

Page 8: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

<LEMSLite> <DiscreteUpdateComponent name="lif_neuron">

<Interface> <Parameter name="bias"/> <Parameter name="gain"/> <Parameter name="constInput"/> <InputEventPort name="spike-in"> <Parameter name="weight"/> </InputEventPort> <OutputEventPort name="spike-out"/>

<Constant name="one_over_rc_float" value="0.0488281"/> <Constant name="ptsc_scale_float" value="0.154279"/>

<OutputVariable name="v"/> </Interface>

<State> <StateVariable name="v"/> <StateVariable name="inp"/> <StateVariable name="ref"/> </State>

<Step> <Var name="total" value="(gain * (inp + constInput)) + bias"/> <Var name="dv" value="(total-v) * one_over_rc_float"/> <Update variable="v" value="v + dv"/> <Update variable="inp" value="inp * (1. - ptsc_scale_float)"/> <Output variable="v" value="v"/> </Step>

<OnEvent port="spike-in" > <Update variable="inp" value="inp + weight * one_over rc"/> </OnEvent>

<OnCondition if="v .gt. 1.0"> <Update variable="v" value="0"/> <Update variable="ref" value="2"/> <Emit port="spike-out"/> </OnCondition> <OnCondition if="ref .gt. 0"> <Update variable="v" value="0"/> <Update variable="ref" value="ref-1"/> </OnCondition> </DiscreteUpdateComponent>

<DataSources> <File name="mh_conv_level0" id="f_params_pop0" format="csv" shape="(5,3000)"/> <Array name="pop0_bias"> <FileSource file="f_params_pop0" column="1"/> </Array> </DataSources>

<ComponentArray name="level0" component="lif_neuron" size="3000"> <Let parameter="constInput" array="pop0_constInput"/> <Let parameter="bias" array="pop0_bias"/> <Let parameter="gain" array="pop0_gain"/>

<Initialize stateVariable="inp" array="pop0_inp" /> </ComponentArray>

<EventConnections name="pop0_to_pop1" from="level0" to="level1"> <EventSource port="spike-out"/> <EventTarget port="spike-in"/>

<SourceTargetConnector> <FromArrayConnector pre="conn01_pre" post="conn01_post"/> </SourceTargetConnector>

<ConnectionProperties> <Property name="weight" array="conn01_weight"/> <Delay value="0"/> </ConnectionProperties>

<EventArguments> <Arg name="weight" value="connection.weight"/> </EventArguments> </EventConnections>

<Simulation name="handwriting_simulation" dt="1.0e-3" endTime="0.02"> <OutputFiles> <File id="f_out0_csv" name="f_out1.csv" format="csv"></File> </OutputFiles>

<Recording startTime="0" endTime="1" interval="0.1"> <VariableRecording file="f_out0_csv" componentArray="level0" indices="1,2,3" variable="v"/> </Recording> </Simulation>

</LEMSLite>

Page 9: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Summary: LEMS-Lite

Acts as a fixed point between the LEMS specification and code generators

LEMS specification can be revised and extended without affecting downstream implementations provided we maintain the mappings to LEMS-Lite

lower-level description that nmodl, NineML etc

– Describes the post-discretization version of the model

– No ODEs, no neuroscience terminology

– Reduces uncertainty about what is to be computed or how equations are to be solved

Page 10: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

HiveMind: Providing an efficient programming model for extreme-scale real-time neural network simulation

Steven Marsh

Supervised by: Dr. Simon Moore

Computer Laboratory Computer Architecture Group

Page 11: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

From abstract descriptions to working simulators

LEMS

NeuroML

LEMS-LiteGenerator

LEMS-Lite HiveMind

Single Threaded

CParallel C BlueVec OpenCL SpiNNaker

Page 12: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Architecture of the generated system

Component

Component Updater

EventEmitter

Population

Component List

Population Updater

Connection Mapping

Connection List

Event DelayQueue

EventReceiver

EventHandler

Page 13: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Example architecture

LIF Population AComponent List

LIF Component

Connections B to AConnection List

Connections C to AConnection List

Event Handler

IaF Population BComponent List

IaF Component

Connections A to BConnection List

Event Handler

HH Population CComponent List

HH Component

Connections B to CConnection List

Event Handler

Page 14: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Challenges of code generation

• Ensuring efficient memory access patterns

• Reducing dynamic memory requirements

• Architecting in an easy-to-parallelize way

• Translation of equations strings to C equivalent

• Taking a high-level declarative model into an efficient low-level implementation

Page 15: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

LIF Component behaviour (LEMS Lite)

<DiscreteUpdateComponent name='lif_neuron'>

<Interface>

<Parameter name="bias"/>

<Parameter name="gain"/>

<Parameter name="constInput"/>

<InputEventPort name="spike-in">

<Parameter name="weight"/>

</InputEventPort>

<OutputEventPort name="spike-out"/>

<Constant name="one_over_rc_float" value="0.15429"/>

<Constant name="dt" value="0.1E-3"/>

<OutputVariable name="v"/>

</Interface>

<State>

<StateVariable name="v"/>

<StateVariable name="inp"/>

<StateVariable name="ref"/>

</State>

<OnEvent port='spike-in' >

<update variable="inp" value="inp + weight * one_over_rc_float"/>

</OnEvent>

<Step>

<var name="total" value="(gain * (inp + constInput)) + bias"/>

<var name="dv" value="(total-v) * one_over_rc_float"/>

<update variable="v" value="v + dv*dt"/>

<update variable="inp" value="inp * (1-one_over_rc_float)"/>

<condition_check/>

<output variable="v" value="v"/>

</Step>

<OnCondition if="v .gt. 1.0">

<update variable="v" value="0"/>

<update variable="ref" value="2"/>

<emit port="spike-out"/>

</OnCondition>

<OnCondition if="ref .gt. 0">

<update variable="v" value="0"/>

<update variable="ref" value="ref-1"/>

</OnCondition>

</DiscreteUpdateComponent>

Page 16: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

LIF Component behaviour (Generated C)

lif_neuron_OUTPUT_EVENT updateComponent_lif_neuron(lif_neuron* component,double *output_v) {

// StateVariables

double v = component->v;

double inp = component->inp;

double ref = component->ref;

// Parameters

double bias = component->bias;

double gain = component->gain;

double constInput = component->constInput;

// Variables

double total = (gain * (inp + constInput)) + bias;

double dv = (total - v) * one_over_rc_float;

// Updates

component->v = v + dv * dt;

component->inp = inp * (1 - one_over_rc_float);

// Conditions

lif_neuron_OUTPUT_EVENT STATUS = lif_neuron_NONE;

if (component->v > 1.0) {

STATUS |= lif_neuron_spike_out;

component->v = 0;

component->ref = 2;

}

if (component->ref > 0) {

component->v = 0;

component->ref = ref - 1;

}

// Outputs

if (output_v != NULL) {

*output_v = v;

}

return STATUS;

}

void spike_in_lif_neuron(lif_neuron* component, double weight) {

component->inp = component->inp + weight * one_over_rc_float;

}

Page 17: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

ComponentArray declaration (LEMS-Lite)

<Datasources>

<Array name='pop0_bias’> <FileSource file='f_params_pop0' column='1'/> </Array>

<Array name='pop0_gain’> <FileSource file='f_params_pop0' column='2'/> </Array>

<Array name='pop0_constInput'> <FileSource file='f_params_pop0' column='3'/> </Array>

<Array name='pop0_inp'> <FileSource file='f_params_pop0' column='4'/> </Array>

<Datasources/>

<ComponentArray name="level0" component="lif_neuron" size="3000">

<let parameter="constInput" array="pop0_constInput"/>

<let parameter="bias" array="pop0_bias"/>

<let parameter="gain" array="pop0_gain"/>

<initialise state_variable="inp" array="pop0_inp" />

</ComponentArray>

Page 18: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

ComponentArray declaration (Generated C)

level0_array level0 =

{

.size=3000,

.components={

{.constInput=-3.84473, .bias=10.31933, .inp=-0.00134, .gain=8.7792},

{.constInput=-3.84473, .bias=5.94921, .inp=-0.00134, .gain=1.37988},

{.constInput=-3.84473, .bias=0.63964, .inp=-0.00134, .gain=10.0195},

{.constInput=-3.76855, .bias=10.31933, .inp=-25.93707, .gain=8.7792},

{.constInput=-3.76855, .bias=5.94921, .inp=-25.93707, .gain=1.37988},

{.constInput=-3.76855, .bias=0.63964, .inp=-25.93707, .gain=10.0195},

{.constInput=-19.9043, .bias=10.31933, .inp=7.22445, .gain=8.7792},

… }

}

Page 19: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

ComponentArray declaration (Generated C)

void update_level0_array() {

lif_neuron_OUTPUT_EVENT SPIKE_STATUS[3000];

double output_v[3000];

int index = 0;

for(index = 0; index < 3000; index++) {

SPIKE_STATUS[index] = updateComponent_lif_neuron(&level0.components[index], &output_v[index]);

}

recordVariable("level0", "v", output_v, 3000);

for (index = 0; index < 3000; index++) {

if (SPIKE_STATUS[index] & lif_neuron_spike_out) {

pop0_to_pop1_spike_in(index);

}

}

}

Page 20: Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.

Benefits of HiveMind code generation

• High-level declarative descriptions

• Can be easily generated from existing tools

• Allows neuroscientists to focus on neuroscience

• Generates C: cross-platform and mature tool-chains

• Avoids programming for esoteric systems

• Allows static declaration of memory

• Minimal yet efficient simulators

• Performance comparable to hand-optimized C code