Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.
-
Upload
gloria-lawrence -
Category
Documents
-
view
214 -
download
0
Transcript of Introducing LEMS-Lite An intermediate format for code-generation Robert Cannon.
Introducing LEMS-LiteAn 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
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.
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.
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
?
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.
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
<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>
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
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
From abstract descriptions to working simulators
LEMS
NeuroML
LEMS-LiteGenerator
LEMS-Lite HiveMind
Single Threaded
CParallel C BlueVec OpenCL SpiNNaker
Architecture of the generated system
Component
Component Updater
EventEmitter
Population
Component List
Population Updater
Connection Mapping
Connection List
Event DelayQueue
EventReceiver
EventHandler
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
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
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>
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;
}
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>
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},
… }
}
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);
}
}
}
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