From 0b0abed33c6808fa60819df990eeb54f635b2fd1 Mon Sep 17 00:00:00 2001 From: "Robert A. McDougal" Date: Wed, 23 Mar 2022 16:30:51 -0400 Subject: [PATCH 1/3] NetStim random guide; also fix to toctree --- docs/guide/fig/netstim_independence.png | Bin 0 -> 17717 bytes docs/guide/index.rst | 1 + docs/guide/randomness.rst | 407 ++++++++++++++++++++++++ docs/python/modelspec/programmatic.rst | 1 + 4 files changed, 409 insertions(+) create mode 100644 docs/guide/fig/netstim_independence.png create mode 100644 docs/guide/randomness.rst diff --git a/docs/guide/fig/netstim_independence.png b/docs/guide/fig/netstim_independence.png new file mode 100644 index 0000000000000000000000000000000000000000..47702ce348126c8da6b0578b7e11a68b681ff4b7 GIT binary patch literal 17717 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 literal 0 HcmV?d00001 diff --git a/docs/guide/index.rst b/docs/guide/index.rst index 3b980850bc..7916cbfafc 100644 --- a/docs/guide/index.rst +++ b/docs/guide/index.rst @@ -12,4 +12,5 @@ Guides cellbuilder import3d optimization + randomness faq \ No newline at end of file diff --git a/docs/guide/randomness.rst b/docs/guide/randomness.rst new file mode 100644 index 0000000000..76cdcfc1de --- /dev/null +++ b/docs/guide/randomness.rst @@ -0,0 +1,407 @@ +Randomness in NEURON models +=========================== +There are many potential applications of randomness in modeling. These fall into two broad categories: + +Randomization of the model specification + Purpose: to emulate natural biological variation of parameters such as number of cells, connectivity between cells, anatomical and/or biophysical properties of cells and their connections. + +Randomization of simulation execution + Purpose: to emulate stochastic phenomena such as single channel gating, transmitter release, afferent spike trains, variation of natural stimuli, extrinsic noise sources. + +Recurring issues in the specification of models of cells and networks that involve randomness include the following: + +* How to create model specification code that employs randomization in a way that (1) avoids undesired correlations between parameters, and (2) produces a model cell or network that has the same architecture and biophysical properties, and generates the same simulation results regardless of whether it is run on serial or parallel hardware, the number of processors that it uses, and how the elements of the model are distributed over the processors. +* How to generate spike streams or other signals that fluctuate in ways that are statistically independent of each other. + +These papers present models implemented with NEURON that provide some examples of randomization in model specification code: + +* + The network with random connectivity in + + Carnevale NT, Hines ML. (2008). Translating network models to parallel hardware in NEURON. *Journal of neuroscience methods*, 169(2), 425. `doi:10.1016/j.jneumeth.2007.09.010 `_ + + Preprint available from http://www.neuron.yale.edu/neuron/nrnpubs/, source code available via accession number 96444 from ModelDB http://modeldb.yale.edu/96444 + +* + The network models in + + Brette, R., Rudolph, M., Carnevale, T., Hines, M., Beeman, D., Bower, J. M., ... Destexhe, A. (2007). Simulation of networks of spiking neurons: a review of tools and strategies. *Journal of computational neuroscience*, 23(3), 349-398. `doi:10.1007/s10827-007-0038-6 `_ + + Preprint available from http://www.neuron.yale.edu/neuron/nrnpubs/, source code available via accession number 83319 from ModelDB http://modeldb.yale.edu/83319 + +Interested users are encouraged to read these papers, and download and analyze the related source code, in order to better understand how to apply randomization in the construction and simulation of models. + +How to generate independent random spike streams +------------------------------------------------ + +There are several ways to generate random spike streams. One is to use a :class:`NetStim` with its random parameter set to a value in the range 0 < random <= 1 (be sure to read the programmer's reference documentation of the :class:`NetStim` class to find out what this does). + +But what if you need more than one spike stream? Can you just add another NetStim and grind out spike times? + +Not if you want your NetStims' streams to be independent of each other. Read on to find out why, and what to do about it. + +The problem +~~~~~~~~~~~ + +Consider this example: a 20 ms simulation of two NetStims, each of which has the following parameters + + .. table:: + :widths: auto + + ======== ===== + interval 10 ms + number 10 + start 1 ms + noise 1 + ======== ===== + +produces events at these times + + .. table:: + :widths: auto + + ====== ==== + time cell + ====== ==== + 1.347 1 + 6.015 0 + 10.123 0 + 12.628 1 + 13.663 1 + 14.913 0 + ====== ==== + +But if cell 1's start time is 6 ms, the results are + + .. table:: + :widths: auto + + ====== ==== + time cell + ====== ==== + 6.015 0 + 6.347 1 + 10.456 1 + 15.245 1 + 16.281 1 + 17.295 0 + ====== ==== + +which demonstrates that the two event streams are not independent of each other. + +This is because the two NetStims are drawing values from the same random number generator. Change any parameter that affects one--interval, start time, number, or noise--and you'll also affect the other, whether you want to or not. + +And usually you don't want to, because at the very least such side effects introduce confounds that can interfere with interpretation of experimental manipulations. Suppose you have a model cell that receives multiple afferent input streams, and you want to see what happens if you delay the onset of one of the afferent spike trains, or if the mean firing frequency of an inhibitory afferent changes. Well, you're out of luck, because any perturbation to one afferent train is going to perturb all afferent trains. + +But if you don't change the NetStims' parameters, everything will be OK, right? + +Right, until you decide to parallelize your model by using MPI to distribute it over multiple processors. At that point you're going to discover that randomization of model setup and randomization of simulation execution will make model parameters and/or simulation results depend on the number of processors and how your model's cells are distributed over the processors. This is most undesirable, because **an essential test of the parallel implementation of a model is the demonstration that it produces the same results as the serial implementation**. If the parallel and serial implementations produce different results, then something is broken, and the parallel implementation cannot be a reliable surrogate for the serial implementation. + +Fortunately, a particular strength of NEURON is that it enables you to parallelize models in such a way that the parallel code produces the same results as the serial code does, regardless of whether the parallel code is being executed on serial or parallel hardware, or the number of processors that are available on the parallel machine, or how the model is distributed over the parallel machine's processors. + +So what's the solution to our current problem? How can we keep our NetStims from using a shared random number generator? Read on to discover the answer. + +Source code that demonstrates the problem +######################################### + +Save the following to a file called :file:`initn.py`, then use python to execute it. Note the organization of the program, in particular the extensive use of procedures, and how it performs the following tasks in sequence: + +* load NEURON library objects we need +* declares important constants (model parameters and simulation parameters) +* creates the model itself (just a collection of cells that spike at random times) +* specifies instrumentation (in this case, recording of spike times) +* specifies simulation control +* executes one or more simulations with various model parameters + +.. code:: + python + + ''' + initn.py + demonstrates that multiple NetStims draw from a shared distribution + ''' + + # + # load libraries + # + + from neuron import h + from neuron.units import ms, mV + h.load_file("stdrun.hoc") + + # + # constants/simulation parameters + # + + NSNUM = 2 # how many NetStims to create + + ISI = 10 * ms # default NetStim parameters + NUM = 10 + START = 1 * ms + NOISE = True # equivalent to passing 1; fractional values + # are also supported + + TSTOP = 20 * ms # length of simulation + + # + # model setup + # + + # create netstims + ns_list = [h.NetStim() for _ in range(NSNUM)] + + # set netstim parameters + for ns in ns_list: + ns.interval = ISI + ns.number = NUM + ns.start = START + ns.noise = NOISE + + # + # instrumentation (record NetStim behavior) + # + + stim_t = h.Vector() + stim_id = h.Vector() + for ns in ns_list: + nc = h.NetCon(ns, None) + nc.record(stim_t, stim_id) + + # + # simulation control + # + + def my_run(): + # set random seed + for ns in ns_list: + ns.seed(1) + h.finitialize(-65 * mV) + h.continuerun(TSTOP) + print(" time cell") + for t, id_ in zip(stim_t, stim_id): + print(f"{t:7.3f} \t{id_}") + + print(""" + =============================================================== + Control: both have interval 10, number 10, start 1, noise True + =============================================================== + """) + my_run() + + print(""" + =============================================================== + Control part 2: Showing we get the same results each time + =============================================================== + """) + my_run() + + print(""" + =============================================================== + Test: NetStim 1 starts at 6 ms + =============================================================== + """) + ns_list[1].start = 6 * ms + my_run() + +The solution +~~~~~~~~~~~~ + +The solution to these problems is to give each NetStim its own random number generator. + +"How?" you might very well ask. + +By creating an instance of the Random class for every instance of the :class:`NetStim` class, and using the :meth:`NetStim.noiseFromRandom` to associate the two. You can probably figure out how to do this on your own if I just point you to a good example, like :file:`NEURON/common/netstim.hoc` in the source code for entry 83319 from ModelDB https://modeldb.yale.edu/83319 + +But for the impatient, here's a quick example. Promise to check out the ModelDB entry after you understand this one. + +Outline of the solution: +######################## + +In essense the solution is this simple: + +.. code:: + none + + Repeat + create a new NetStim instance + set the NetStim to use a unique random stream + Until enough NetStims have been created + Run a simulation and show results + +But of course the actual implementation is somewhat more complex, because there are many details to attend to. These include + +* Specifying the basic properties of the :class:`NetStim` (interval, number, start, noise). +* Specifying a distinct random stream for each NetStim via :meth:`NetStim.noiseFromRandom123`. (For analogous reasons, you will likely want to use :meth:`Random.Random123` with distinct streams for each cell -- independent of any NetStim or other streams -- when choosing cell properties randomly.) +* Instrumentation and simulation control. As in the previous example, instrumentation will consist of using the NetCon class's ``record()`` method to capture spike times and cell ids to a pair of Vectors. Also as before, simulation control will use a custom ``my_run()`` function that pulls together the standard run system's :func:`continuerun` with a bit more code that prints out the spike times and cell ids, but this time there will also be a graph that shows a raster plot of the spikes generated by the NetStims. + +The ``Random123`` generator is an implementation of a (pseudo)random number generator described in: + + Salmon JK, Moraes MA, Dror RO, Shaw DE (2011). Parallel random numbers: as easy as 1, 2, 3. In *Proceedings of 2011 international conference for high performance computing, networking, storage and analysis* (pp. 1-12). `doi:10.1145/2063384.2063405 `_ + + + +Source code that demonstrates the solution: +########################################### + +This file shows how to take advantage of :meth:`NetStim.noiseFromRandom123` in your own programs, so that your :class:`NetStim` objects can generate independent random streams produced by the ``Random123`` generator. For the most part, this file is quite similar to :file:`initn.py`, which demonstrated one of the problems that may occur when NetStims share a common random number generator. + +.. code:: + python + + ''' + independent_netstims.py + demonstrates writing NetStims to use independent random streams + ''' + + # + # load libraries + # + + from neuron import h + from neuron.units import ms, mV + import matplotlib.pyplot as plt + h.load_file("stdrun.hoc") + + # + # constants/simulation parameters + # + + NSNUM = 2 # how many NetStims to create + + ISI = 10 * ms # default NetStim parameters + NUM = 10 + START = 1 * ms + NOISE = True # equivalent to passing 1; fractional values + # are also supported + + TSTOP = 20 * ms # length of simulation + + # + # model setup + # + + # create netstims + ns_list = [h.NetStim() for _ in range(NSNUM)] + + all_random_streams = [] + + # set netstim parameters + for i, ns in enumerate(ns_list): + ns.interval = ISI + ns.number = NUM + ns.start = START + ns.noise = NOISE + # specify the (i, 0, 0)th random stream + ns.noiseFromRandom123(i, 0, 0) + + + # + # instrumentation (record NetStim behavior) + # + + stim_t = h.Vector() + stim_id = h.Vector() + for ns in ns_list: + nc = h.NetCon(ns, None) + nc.record(stim_t, stim_id) + + # + # simulation control + # + + def my_run(): + # set random seed + for ns in ns_list: + ns.seed(1) + h.finitialize(-65 * mV) + h.continuerun(TSTOP) + + print(" time cell") + for t, id_ in zip(stim_t, stim_id): + print(f"{t:7.3f} \t{id_}") + + # show raster + for i in range(len(ns_list)): + plt.vlines([t for t, id_ in zip(stim_t, stim_id) if id_ == i], + i - 0.4, i + 0.4) + plt.yticks(range(len(ns_list))) + plt.xlim(0, TSTOP) + + plt.figure(figsize=(6, 6)) + print(""" + =============================================================== + Control: both have interval 10, number 10, start 1, noise True + =============================================================== + """) + plt.subplot(3, 1, 1) + my_run() + plt.ylabel("Control 1") + + print(""" + =============================================================== + Control part 2: Showing we get the same results each time + =============================================================== + """) + plt.subplot(3, 1, 2) + my_run() + plt.ylabel("Control 2") + + print(""" + =============================================================== + Test: NetStim 1 starts at 6 ms + =============================================================== + """) + ns_list[1].start = 6 * ms + plt.subplot(3, 1, 3) + my_run() + plt.ylabel("Test") + plt.xlabel("t (ms)") + plt.show() + +Running the above code displays: + +.. code:: + none + + =============================================================== + Control: both have interval 10, number 10, start 1, noise True + =============================================================== + + time cell + 7.603 1.0 + 8.226 1.0 + 10.187 0.0 + 11.459 0.0 + 14.528 0.0 + 19.546 0.0 + 19.827 0.0 + + =============================================================== + Control part 2: Showing we get the same results each time + =============================================================== + + time cell + 7.603 1.0 + 8.226 1.0 + 10.187 0.0 + 11.459 0.0 + 14.528 0.0 + 19.546 0.0 + 19.827 0.0 + + =============================================================== + Test: NetStim 1 starts at 6 ms + =============================================================== + + time cell + 10.187 0.0 + 11.459 0.0 + 12.603 1.0 + 13.226 1.0 + 14.528 0.0 + 19.546 0.0 + 19.827 0.0 + +.. image:: fig/netstim_independence.png + +In particular, we note that cell 0 fires at exactly the same times in all cases, and cell 1s spike times are shifted forward in the test case. \ No newline at end of file diff --git a/docs/python/modelspec/programmatic.rst b/docs/python/modelspec/programmatic.rst index 4475f9cf87..a55df6fa3f 100755 --- a/docs/python/modelspec/programmatic.rst +++ b/docs/python/modelspec/programmatic.rst @@ -11,5 +11,6 @@ Programmatic Model Specification programmatic/network.rst programmatic/electrod.rst programmatic/mechtype.rst + programmatic/ste.rst programmatic/obsoletestimuli.rst From e4107bf7499384ff0adcd18d3c4c073da9929587 Mon Sep 17 00:00:00 2001 From: "Robert A. McDougal" Date: Wed, 23 Mar 2022 16:36:52 -0400 Subject: [PATCH 2/3] adjusted white space in text tables --- docs/guide/randomness.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guide/randomness.rst b/docs/guide/randomness.rst index 76cdcfc1de..9e1e832507 100644 --- a/docs/guide/randomness.rst +++ b/docs/guide/randomness.rst @@ -316,7 +316,7 @@ This file shows how to take advantage of :meth:`NetStim.noiseFromRandom123` in y h.finitialize(-65 * mV) h.continuerun(TSTOP) - print(" time cell") + print(" time cell") for t, id_ in zip(stim_t, stim_id): print(f"{t:7.3f} \t{id_}") @@ -367,7 +367,7 @@ Running the above code displays: Control: both have interval 10, number 10, start 1, noise True =============================================================== - time cell + time cell 7.603 1.0 8.226 1.0 10.187 0.0 @@ -380,7 +380,7 @@ Running the above code displays: Control part 2: Showing we get the same results each time =============================================================== - time cell + time cell 7.603 1.0 8.226 1.0 10.187 0.0 @@ -393,7 +393,7 @@ Running the above code displays: Test: NetStim 1 starts at 6 ms =============================================================== - time cell + time cell 10.187 0.0 11.459 0.0 12.603 1.0 From 0570de98945011568ecb6dc64411faf9f8bb5280 Mon Sep 17 00:00:00 2001 From: "Robert A. McDougal" Date: Wed, 23 Mar 2022 16:42:39 -0400 Subject: [PATCH 3/3] Removed ModelDB reference for something predating Random123 --- docs/guide/randomness.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guide/randomness.rst b/docs/guide/randomness.rst index 9e1e832507..4017d77fd4 100644 --- a/docs/guide/randomness.rst +++ b/docs/guide/randomness.rst @@ -206,18 +206,18 @@ Save the following to a file called :file:`initn.py`, then use python to execute The solution ~~~~~~~~~~~~ -The solution to these problems is to give each NetStim its own random number generator. +The solution to these problems is to give each NetStim its own random number stream. "How?" you might very well ask. -By creating an instance of the Random class for every instance of the :class:`NetStim` class, and using the :meth:`NetStim.noiseFromRandom` to associate the two. You can probably figure out how to do this on your own if I just point you to a good example, like :file:`NEURON/common/netstim.hoc` in the source code for entry 83319 from ModelDB https://modeldb.yale.edu/83319 +By using :meth:`NetStim.noiseFromRandom123` to specify a unique random stream for each NetStim; each stream is independent and selected as a combination of three integer values. -But for the impatient, here's a quick example. Promise to check out the ModelDB entry after you understand this one. +Here's a quick example. Outline of the solution: ######################## -In essense the solution is this simple: +In essence the solution is this simple: .. code:: none