15-883: Advice for MATLAB Programmers
Computational Models of Neural Systems
Over the years I've found that many students in 15-883 learned
programming without learning good programming habits. This causes
problems when they tackle more complex assignments like the
15-883 modeling project,
because the lack of good habits leads to disorganized code that no
one can understand or debug.
This document introduces some basic rules that everyone should
follow when coding their modeling project. Ignore this advice
at your peril.
1. Decompose your program into simple parts.
Don't try to cram everything into one function. Write a function
just for setting up your currents. Write another function just
for setting up your pyramidal cells. Write another function for
calculating spike statistics. And so on.
2. Every function goes in its own file.
In MATLAB, every function should go in a separate file whose name
matches the function name. This allows functions to be called
from the MATLAB Command Window so they can be debugged.
Also, do not use MATLAB Live files (.mlx files) for this
assignment, because they are not human readable outside of MATLAB.
Only use regular .m files, which are text files.
3. Use global variables for constants and major data structures.
Constants such as the number of cells and number of time
steps in the simulation should be defined as global variables so
they can be referenced anywhere in your code. Do not pass them as
arguments. Similarly, major data structures such as the
collection of cells you're simulating should be globals so they
can be accessed and updated from wherever necessary.
The declare_globals script suggested in the project instructions
is a convenient way to ensure that every function has access to
all the global variables it needs, and relieves you of the need to
repeat all your global declarations in every function. Note that
declare_globals must be a script, not a function.
4. Use appropriate data structures.
MATLAB provides structure objects with named fields. If you're
not familiar with this type of data structure, take a few minutes
to learn about it. Each current is represented as a structure
with fields such as tau_rise, tau_fall, G, etc. Each pyramidal
cell is represented by a structure with fields such as V (for the
membrane voltage), last_spike_time, and state. Since we have
multiple pyramidal cells, they will live in an array of structs.
If you are tempted to just stick everything in a bunch of separate
arrays named tau_rise, tau_fall, etc., you are not following good
programming practice, and Santa will surely put you on his
"naughty" list.
5. Follow function specifications exactly.
If you are told to write a function updateGamma(p,t), then your
function should take exactly those arguments and nothing else.
Any other variables your function needs to reference should be
declared as globals.
6. Never put constants in your code.
A constant like 500 should never appear in your code. Assign it
to a global variable such as num_time_steps and then use that
variable in your code.
There are only two exceptions to this rule. First, it's okay to
write a constant when you're assigning it to a variable, e.g.,
"num_time_steps = 500;" is fine. Second, it's okay to use common
constants such as 0 to initialize a sum, or 1 as the starting
index of a for loop or the numerator of a fraction. But it's not
okay to use 1 in other than these very common contexts. For
example, if you're using 1 as a capacitance value, assign it to a
variable called Capacitance and use that instead.
7. Use meaningful variable names.
Keep in mind that other people must be able to read your code. If you
want to keep track of a cell's last spike time, don't use a cryptic
name like "lt" or "lstspk". Use "last_spike_time" or "lastSpikeTime".
Avoid generic names like "answer", "result", or "x". Use a name that
accurately describes what you're computing.
8. Do not needlessly duplicate code. Use abstraction instead.
A cell's membrane voltage is the result of multiple currents. Do
not write a separate expression to calculate each current; that
bloats the code and makes it harder to spot errors. Instead, use
a for loop to iterate over all the currents and calculate them one
at a time. That way the code will only contain a single call to
your conductance calculation function, inside the for loop. This
is why the currents should be described in a struct array instead
of using a separate variable to hold each current.
9. Clean up your messes.
While developing your program you may end up with old functions
you no longer use, old code that is commented out, and print
statements added for debugging. Get rid of all this clutter
before submitting your code.
|