"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "27bfbb77-ec4e-4f9d-bb59-fd5b6889df5c",
+ "metadata": {},
+ "source": [
+ "Before BMTK starts a simulation, it will go ahead and call on NEST to create neurons for all cells using attributes like **model_type**, **model_template**, and **dynamics_params**. BMTK will initialize the creation of these cells for you and for the vast majority of simulations the user does not have to do anythin special other than specify the models and parameters in the SONATA file. \n",
+ "\n",
+ "However, on rare occassions you may find yourself requiring more grainular control of how PointNet initializes and creates the neurons before the simulation begins. For example, you may want to find or alter cell parameters at the start of a simulation. Some models may require a non-standard way of initializing them. Or you may need to attach Parrot or Relay neurons to a subset of your cells. \n",
+ "\n",
+ "When BMTK initializes cells for simualation it will normally call the function `loadNESTModel` function in [`bmtk.simulation.pointnet.default_setters.cell_models`](). This is a simple function that calls that NEST `Create` function to initialize a batch of neurons that share the same model-name and properites.\n",
+ "\n",
+ "```python\n",
+ "def loadNESTModel(cell, template_name, dynamics_params):\n",
+ " return nest.Create(template_name, cell.n_nodes, dynamics_params)\n",
+ "```\n",
+ "\n",
+ "If you need to override this function, BMTK provides a special [python decorator](https://peps.python.org/pep-0318/) called `@cell_model`. To use the decorator you can add the following to your *run_pointnet.py* (or in a separate python file imported before you run the simulation) as such\n",
+ "\n",
+ "```python\n",
+ "from bmtk.simulator.pointnet import cell_model\n",
+ "\n",
+ "@cell_model(directive='nest', model_type='point_neuron')\n",
+ "def loadNESTModel(cell, template_name, dynamics_params):\n",
+ " nest_nodes = ... # create and initialize cells\n",
+ " return nest_nodes\n",
+ "```\n",
+ "To explain what is happening in this function\n",
+ "* The `@cell_model` decorator is placed right before our user defined function in order to tell bmtk that this is a special function that will be used in the creation of \"nest\" cells\n",
+ " * The parameters tell BMTK that this function will be called for every group of cells with **model_template** of format `nest:...` and have **model_type** of point_neuron\n",
+ "* There are three parameters the BMTK will pass into this function\n",
+ " * `cell` is copy of cell object and can be used to access all of an cell(s) properities (can be accessed like a dictionary)\n",
+ " * `template` will be the name of the model (eg. izhikevich, glif_psc, hh_cond, etc)\n",
+ " * `dynamics_params` will be a dictionary containing the contents of the json or hdf5 **dynamics_params**.\n",
+ "* Inside the function we should create our cell(s), usually using the NEST [Create](https://nest-simulator.readthedocs.io/en/v3.3/ref_material/pynest_apis.html#module-nest.lib.hl_api_nodes) function.\n",
+ "* You must return the results of the Create function, usually of list of ids, back to bmtk.\n",
+ "\n",
+ "During the initialization process BMTK will call this custom function. It tries to do so in batches, often initializing cells with the same models and signitures in batches.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f1e646fd-c529-407c-a8c6-bc8e533641a7",
+ "metadata": {},
+ "source": [
+ "\n",
+ "⚠️ WARNING: Overwriting loadNESTModel can be dangerous\n",
+ "
\n",
+ "\n",
+ "If you're network has many different models then overwritting `loadNESTModel` to deal with only a subset of the cells can have unintended side-effects. However bmtk lets you create specific load methods for different models\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d12fcb57-8c57-4fb8-86f1-6c8a431e44c9",
+ "metadata": {},
+ "source": [
+ "#### Overriding Izhikeich model\n",
+ "\n",
+ "For example, let's say we want to modify and adjust parameters on-the-fly (eg. we don't want to have to rebuild the network every time) but only for the `izhikevich` models. Specially we want to add jitter to the \"d\" parameter which won't exists for other model types. To do so we add a function `loadIzhikevich` and in the `@cell_model` parameters make sure that it only applies to `nest:Izhikevich` models, so that all other model types gets created in the default manner.\n",
+ "\n",
+ "We add the following function"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "605848aa-1c59-4929-a68f-73641e1dfec9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from bmtk.simulator.pointnet import cell_model\n",
+ "from bmtk.simulator.pointnet.io_tools import io\n",
+ "import nest\n",
+ "\n",
+ "@cell_model(directive='nest:izhikevich', model_type='point_neuron')\n",
+ "def loadIzhikevich(cell, template_name, dynamics_params):\n",
+ " nodes = nest.Create(template_name, cell.n_nodes, dynamics_params)\n",
+ " \n",
+ " d_orig = nest.GetStatus(nodes, 'd')\n",
+ " jitter = cell['jitter']\n",
+ " d_noisy = d_orig*jitter\n",
+ " nest.SetStatus(nodes, {'d': d_noisy})\n",
+ " io.log_info(f'Modifying the parameters of {cell.n_nodes} {template_name} neurons.')\n",
+ " return nodes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2238dbbe-25fa-4143-808c-2b29c838f5a4",
+ "metadata": {},
+ "source": [
+ "* First we create the cells as normal using the `Create` method. Then we use the `nest.GetStatus` and `nest.SetStatus` to update the \"d\" parameters for each cell. It is sometimes possible to alter the way `Create()` method is called, but in general it is best to use [`GetStatus`](https://nest-simulator.readthedocs.io/en/v3.3/ref_material/pynest_apis.html#module-nest.lib.hl_api_info) to alter parameters\n",
+ "* The network has a jitter parameter which we can multiple by the default \"d\" value and is stored in the hdf5 file. We call `cell[jitter]` to get this value as an array, and can use `cell` to fetch other cell attributes.\n",
+ "* As an optional measure we add some logging. This is a good sanity check to make sure our funnction is being called as it should.\n",
+ "\n",
+ "After we added our custom function we can load PointNet in the normal manner:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "0acdb9d5-cd8a-441b-a4aa-5da5f879cdb0",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,907 [INFO] Created log file\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Created log file\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,930 [INFO] Batch processing nodes for net/0.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Batch processing nodes for net/0.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,938 [INFO] Batch processing nodes for virt_exc/0.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Batch processing nodes for virt_exc/0.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,958 [INFO] Setting up output directory\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Setting up output directory\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,959 [INFO] Building cells.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Building cells.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,960 [INFO] Modifying the parameters of 10 izhikevich neurons.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Modifying the parameters of 10 izhikevich neurons.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,961 [INFO] Building recurrent connections\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Building recurrent connections\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,963 [INFO] Network created.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Network created.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,963 [INFO] Build virtual cell stimulations for thalamus_spikes\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Build virtual cell stimulations for thalamus_spikes\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:51,971 [INFO] Starting Simulation\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Starting Simulation\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:52,297 [INFO] Simulation finished, finalizing results.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Simulation finished, finalizing results.\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-10-21 15:11:52,316 [INFO] Done.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "INFO:Done.\n"
+ ]
+ }
+ ],
+ "source": [
+ "from bmtk.simulator import pointnet\n",
+ "\n",
+ "configure = pointnet.Config.from_json('config.iz_updated.json')\n",
+ "configure.build_env()\n",
+ "\n",
+ "network = pointnet.PointNetwork.from_config(configure)\n",
+ "sim = pointnet.PointSimulator.from_config(configure, network)\n",
+ "sim.run()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d1fb1efd-fe46-4b56-890b-c691db36aeab",
+ "metadata": {},
+ "source": [
+ "## 4. Example: Switching between synaptic (and electrical) models