Skip to content

Commit

Permalink
Support callback for coordinate generation
Browse files Browse the repository at this point in the history
  • Loading branch information
frthjf committed Dec 21, 2023
1 parent a152abe commit c6421ed
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 5 deletions.
46 changes: 42 additions & 4 deletions docs/guide/microcircuit-mea.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
Working with custom microcircuit-MEA simulations
Simulating Multielectrode Arrays
================================================

.. note::
This How-To uses the :doc:`imperative interface <../tutorial/imperative-interface>`.
A common system of interest in neural simulation are Multielectrode Arrays (MEA) which integrate multiple microelectrodes to obtain or deliver neural signals to the culture.

To construct MEAs in simulation, you can position artificial STIM(umlus) cells that emulate delivering electrodes and/or position LFP readouts to emulate obtaining electrodes.

A full code example can be found in the `MiV-Simulator-Cases repository <https://github.com/GazzolaLab/MiV-Simulator-Cases/blob/main/3-interface-api/microcircuit-mea.py>`__.
Positioning 'electrode' STIM cells
----------------------------------

Given the x,y,z coordinates of your MEA electrodes, you can insert them into the culture via a callback.

.. code:: python
# define callable that returns MEA coordinates
def callable(n, extents): # -> (n, 3)
...
return xyz_coordinate_array
"cell_distributions": {
"STIM": { # generate STIM(ulus) cells
"@callable": 64 # @ to invoke callable during generation
},
...
}
"layer_extents": {
"@callable": [ # extents definition
[0.0, 0.0, 0.0],
[200.0, 200.0, 150.0],
],
...
}
Positioning 'electrode' LFPs
----------------------------

To emulate the readout feature of the electrodes, you can leverage the ``miv_simulator.lfp.LFP`` class.


.. literalinclude:: ../MiV-Simulator-Cases/3-interface-api/interface/experiment/rc.py
:language: python
:linenos:
:lines: 5,73-88
3 changes: 2 additions & 1 deletion docs/tutorial/imperative-interface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ chained as follows:
:linenos:
:lines: 14-112

The ``graph.files()`` method in the last line returns the filepaths of the generated H5 files that are required to launch a simulation.
You are free to modify or entirely replace any step in the pipeline. Notably, machinable will automatically keep track of configuration changes to determine if generation steps need to be re-executed. This means that file geneneration is cached and managed behind the scences.
You can access the resulting filepaths of the generated H5 files via ``graph.files()``.

Launching the simulation
------------------------
Expand Down
27 changes: 27 additions & 0 deletions src/miv_simulator/simulator/generate_network_architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import random
import sys
from collections import defaultdict
import importlib

import h5py
import numpy as np
Expand Down Expand Up @@ -341,6 +342,26 @@ def generate_network_architecture(
if count <= 0:
continue

if layer.startswith("@"):
# generate via callback
module_path, _, obj_name = layer[1:].rpartition(".")
if module_path == "__main__" or module_path == "":
module = sys.modules["__main__"]
else:
module = importlib.import_module(module_path)
callback = getattr(module, obj_name)

nodes = callback(count, layer_extents[layer])

if not len(nodes) == count:
logger.error(
f"Generator {layer} produced mismatch between actual count {len(nodes)} and configured count {count}"
)

xyz_coords_lst.append(nodes.reshape(-1, 3))

continue

alpha = layer_alpha_shapes[layer]

vert = alpha.points
Expand Down Expand Up @@ -583,6 +604,12 @@ def generate_network_architecture(
for i in range(delta):
for layer, count in pop_layers.items():
if count > 0:
if layer.startswith("@"):
logger.warning(
f"Generator {layer} did not return the specified number of coordinates"
)
continue

min_extent = layer_extents[layer][0]
max_extent = layer_extents[layer][1]
coord_u = np.random.uniform(
Expand Down

0 comments on commit c6421ed

Please sign in to comment.