200 likes | 331 Views
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.
E N D
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 definitions Phiysics, 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 – dimensionality Geometry – containment, adjacency, trees, 1-D skeleton of 3-D tree Source code for general purpose hardware ? Source code for custom hardware
LEMS Embeds knowledge of: Physics – dimensionality Geometry – containment, adjacency, trees, 1-D skeleton of 3-D tree Single-step code generation embeds a lot of knowledge – physics, geometry, and numerics. We've swapped a monolithic simulator for a monolithic code generator. X Source code for general purpose hardware
Physics Mathematics Stage 1: Remove physics and Geometry. Keep ODEs LEMS Physics – dimensionality Geometry – containment, adjacency, trees, 1-D skeleton of 3-D tree Flat LEMS model Still expressed in LEMS Comparable to NineML Stage 2: Combine with declarative representation of numerics ODEs → update rules Numerical Integration Schemes Euler, RK4, Crank Nicolson etc LEMS-Lite Components, Arrays, Update rules, Connections No geometry No ODEs No neuroscience terms Source code for general purpose hardware Code for custom hardware 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 LEMS-Lite Generator HiveMind LEMS-Lite NeuroML Single Threaded C Parallel C BlueVec OpenCL SpiNNaker
Architecture of the generated system Component Population Connection Mapping Component Updater Component List Connection List Event Emitter Population Updater Event Delay Queue Event Receiver Event Handler
Example architecture Connections B to A LIF Population A Connection List Component List Event Handler LIF Component Connections C to A Connection List Connections A to B IaF Population B Connection List Component List Event Handler IaF Component Connections B to C HH Population C Connection List Component List Event Handler HH Component
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_EVENTupdateComponent_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"/> <initialisestate_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_EVENTSPIKE_STATUS[3000]; double output_v[3000]; intindex = 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