From 809f563dc89d93898d512343dcbc3c4b4cbf55d7 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 13 Sep 2019 23:29:20 -0400 Subject: [PATCH 01/88] ENH: try making HNN GUI with ipywidgets --- hnn_widget.ipynb | 471 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 hnn_widget.ipynb diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb new file mode 100644 index 000000000..1773efdbb --- /dev/null +++ b/hnn_widget.ipynb @@ -0,0 +1,471 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"Itonic_A_L2Basket\": 0.0,\n", + " \"Itonic_A_L2Pyr_soma\": 0.0,\n", + " \"Itonic_A_L5Basket\": 0.0,\n", + " \"Itonic_A_L5Pyr_soma\": 0.0,\n", + " \"Itonic_T_L2Basket\": -1.0,\n", + " \"Itonic_T_L2Pyr_soma\": -1.0,\n", + " \"Itonic_T_L5Basket\": -1.0,\n", + " \"Itonic_T_L5Pyr_soma\": -1.0,\n", + " \"Itonic_t0_L2Basket\": 0.0,\n", + " \"Itonic_t0_L2Pyr_soma\": 0.0,\n", + " \"Itonic_t0_L5Basket\": 0.0,\n", + " \"Itonic_t0_L5Pyr_soma\": 0.0,\n", + " \"L2Basket_Gauss_A_weight\": 0.0,\n", + " \"L2Basket_Gauss_mu\": 2000.0,\n", + " \"L2Basket_Gauss_sigma\": 3.6,\n", + " \"L2Basket_Pois_A_weight_ampa\": 0.0,\n", + " \"L2Basket_Pois_A_weight_nmda\": 0.0,\n", + " \"L2Basket_Pois_lamtha\": 0.0,\n", + " \"L2Pyr_Gauss_A_weight\": 0.0,\n", + " \"L2Pyr_Gauss_mu\": 2000.0,\n", + " \"L2Pyr_Gauss_sigma\": 3.6,\n", + " \"L2Pyr_Pois_A_weight_ampa\": 0.0,\n", + " \"L2Pyr_Pois_A_weight_nmda\": 0.0,\n", + " \"L2Pyr_Pois_lamtha\": 0.0,\n", + " \"L2Pyr_ampa_e\": 0.0,\n", + " \"L2Pyr_ampa_tau1\": 0.5,\n", + " \"L2Pyr_ampa_tau2\": 5.0,\n", + " \"L2Pyr_apical1_L\": 306.0,\n", + " \"L2Pyr_apical1_diam\": 4.08,\n", + " \"L2Pyr_apicaloblique_L\": 340.0,\n", + " \"L2Pyr_apicaloblique_diam\": 3.91,\n", + " \"L2Pyr_apicaltrunk_L\": 59.5,\n", + " \"L2Pyr_apicaltrunk_diam\": 4.25,\n", + " \"L2Pyr_apicaltuft_L\": 238.0,\n", + " \"L2Pyr_apicaltuft_diam\": 3.4,\n", + " \"L2Pyr_basal1_L\": 85.0,\n", + " \"L2Pyr_basal1_diam\": 4.25,\n", + " \"L2Pyr_basal2_L\": 255.0,\n", + " \"L2Pyr_basal2_diam\": 2.72,\n", + " \"L2Pyr_basal3_L\": 255.0,\n", + " \"L2Pyr_basal3_diam\": 2.72,\n", + " \"L2Pyr_dend_Ra\": 200.0,\n", + " \"L2Pyr_dend_cm\": 0.6195,\n", + " \"L2Pyr_dend_el_hh2\": -65.0,\n", + " \"L2Pyr_dend_gbar_km\": 250.0,\n", + " \"L2Pyr_dend_gkbar_hh2\": 0.01,\n", + " \"L2Pyr_dend_gl_hh2\": 4.26e-05,\n", + " \"L2Pyr_dend_gnabar_hh2\": 0.15,\n", + " \"L2Pyr_gabaa_e\": -80.0,\n", + " \"L2Pyr_gabaa_tau1\": 0.5,\n", + " \"L2Pyr_gabaa_tau2\": 5.0,\n", + " \"L2Pyr_gabab_e\": -80.0,\n", + " \"L2Pyr_gabab_tau1\": 1.0,\n", + " \"L2Pyr_gabab_tau2\": 20.0,\n", + " \"L2Pyr_nmda_e\": 0.0,\n", + " \"L2Pyr_nmda_tau1\": 1.0,\n", + " \"L2Pyr_nmda_tau2\": 20.0,\n", + " \"L2Pyr_soma_L\": 22.1,\n", + " \"L2Pyr_soma_Ra\": 200.0,\n", + " \"L2Pyr_soma_cm\": 0.6195,\n", + " \"L2Pyr_soma_diam\": 23.4,\n", + " \"L2Pyr_soma_el_hh2\": -65.0,\n", + " \"L2Pyr_soma_gbar_km\": 250.0,\n", + " \"L2Pyr_soma_gkbar_hh2\": 0.01,\n", + " \"L2Pyr_soma_gl_hh2\": 4.26e-05,\n", + " \"L2Pyr_soma_gnabar_hh2\": 0.18,\n", + " \"L5Basket_Gauss_A_weight\": 0.0,\n", + " \"L5Basket_Gauss_mu\": 2000.0,\n", + " \"L5Basket_Gauss_sigma\": 2.0,\n", + " \"L5Basket_Pois_A_weight_ampa\": 0.0,\n", + " \"L5Basket_Pois_A_weight_nmda\": 0.0,\n", + " \"L5Basket_Pois_lamtha\": 0.0,\n", + " \"L5Pyr_Gauss_A_weight\": 0.0,\n", + " \"L5Pyr_Gauss_mu\": 2000.0,\n", + " \"L5Pyr_Gauss_sigma\": 4.8,\n", + " \"L5Pyr_Pois_A_weight_ampa\": 0.0,\n", + " \"L5Pyr_Pois_A_weight_nmda\": 0.0,\n", + " \"L5Pyr_Pois_lamtha\": 0.0,\n", + " \"L5Pyr_ampa_e\": 0.0,\n", + " \"L5Pyr_ampa_tau1\": 0.5,\n", + " \"L5Pyr_ampa_tau2\": 5.0,\n", + " \"L5Pyr_apical1_L\": 680.0,\n", + " \"L5Pyr_apical1_diam\": 7.48,\n", + " \"L5Pyr_apical2_L\": 680.0,\n", + " \"L5Pyr_apical2_diam\": 4.93,\n", + " \"L5Pyr_apicaloblique_L\": 255.0,\n", + " \"L5Pyr_apicaloblique_diam\": 5.1,\n", + " \"L5Pyr_apicaltrunk_L\": 102.0,\n", + " \"L5Pyr_apicaltrunk_diam\": 10.2,\n", + " \"L5Pyr_apicaltuft_L\": 425.0,\n", + " \"L5Pyr_apicaltuft_diam\": 3.4,\n", + " \"L5Pyr_basal1_L\": 85.0,\n", + " \"L5Pyr_basal1_diam\": 6.8,\n", + " \"L5Pyr_basal2_L\": 255.0,\n", + " \"L5Pyr_basal2_diam\": 8.5,\n", + " \"L5Pyr_basal3_L\": 255.0,\n", + " \"L5Pyr_basal3_diam\": 8.5,\n", + " \"L5Pyr_dend_Ra\": 200.0,\n", + " \"L5Pyr_dend_cm\": 0.85,\n", + " \"L5Pyr_dend_el_hh2\": -71.0,\n", + " \"L5Pyr_dend_gbar_ar\": 1e-06,\n", + " \"L5Pyr_dend_gbar_ca\": 60.0,\n", + " \"L5Pyr_dend_gbar_cat\": 0.0002,\n", + " \"L5Pyr_dend_gbar_kca\": 0.0002,\n", + " \"L5Pyr_dend_gbar_km\": 200.0,\n", + " \"L5Pyr_dend_gkbar_hh2\": 0.01,\n", + " \"L5Pyr_dend_gl_hh2\": 4.26e-05,\n", + " \"L5Pyr_dend_gnabar_hh2\": 0.14,\n", + " \"L5Pyr_dend_taur_cad\": 20.0,\n", + " \"L5Pyr_gabaa_e\": -80.0,\n", + " \"L5Pyr_gabaa_tau1\": 0.5,\n", + " \"L5Pyr_gabaa_tau2\": 5.0,\n", + " \"L5Pyr_gabab_e\": -80.0,\n", + " \"L5Pyr_gabab_tau1\": 1.0,\n", + " \"L5Pyr_gabab_tau2\": 20.0,\n", + " \"L5Pyr_nmda_e\": 0.0,\n", + " \"L5Pyr_nmda_tau1\": 1.0,\n", + " \"L5Pyr_nmda_tau2\": 20.0,\n", + " \"L5Pyr_soma_L\": 39.0,\n", + " \"L5Pyr_soma_Ra\": 200.0,\n", + " \"L5Pyr_soma_cm\": 0.85,\n", + " \"L5Pyr_soma_diam\": 28.9,\n", + " \"L5Pyr_soma_el_hh2\": -65.0,\n", + " \"L5Pyr_soma_gbar_ar\": 1e-06,\n", + " \"L5Pyr_soma_gbar_ca\": 60.0,\n", + " \"L5Pyr_soma_gbar_cat\": 0.0002,\n", + " \"L5Pyr_soma_gbar_kca\": 0.0002,\n", + " \"L5Pyr_soma_gbar_km\": 200.0,\n", + " \"L5Pyr_soma_gkbar_hh2\": 0.01,\n", + " \"L5Pyr_soma_gl_hh2\": 4.26e-05,\n", + " \"L5Pyr_soma_gnabar_hh2\": 0.16,\n", + " \"L5Pyr_soma_taur_cad\": 20.0,\n", + " \"N_pyr_x\": 10,\n", + " \"N_pyr_y\": 10,\n", + " \"N_trials\": 1,\n", + " \"T_pois\": -1,\n", + " \"celsius\": 37.0,\n", + " \"dipole_scalefctr\": 3000,\n", + " \"dipole_smooth_win\": 30,\n", + " \"distribution_dist\": \"normal\",\n", + " \"distribution_prox\": \"normal\",\n", + " \"dt\": 0.025,\n", + " \"dt_evprox0_evdist\": -1,\n", + " \"dt_evprox0_evprox1\": -1,\n", + " \"events_per_cycle_dist\": 2,\n", + " \"events_per_cycle_prox\": 2,\n", + " \"f_input_dist\": 10.0,\n", + " \"f_input_prox\": 10.0,\n", + " \"f_max_spec\": 100,\n", + " \"f_stdev_dist\": 20.0,\n", + " \"f_stdev_prox\": 20.0,\n", + " \"gbar_L2Basket_L2Basket\": 0.02,\n", + " \"gbar_L2Basket_L2Pyr_gabaa\": 0.05,\n", + " \"gbar_L2Basket_L2Pyr_gabab\": 0.05,\n", + " \"gbar_L2Basket_L5Pyr\": 0.001,\n", + " \"gbar_L2Pyr_L2Basket\": 0.0005,\n", + " \"gbar_L2Pyr_L2Pyr_ampa\": 0.0005,\n", + " \"gbar_L2Pyr_L2Pyr_nmda\": 0.0005,\n", + " \"gbar_L2Pyr_L5Basket\": 0.00025,\n", + " \"gbar_L2Pyr_L5Pyr\": 0.00025,\n", + " \"gbar_L5Basket_L5Basket\": 0.02,\n", + " \"gbar_L5Basket_L5Pyr_gabaa\": 0.025,\n", + " \"gbar_L5Basket_L5Pyr_gabab\": 0.025,\n", + " \"gbar_L5Pyr_L5Basket\": 0.0005,\n", + " \"gbar_L5Pyr_L5Pyr_ampa\": 0.0005,\n", + " \"gbar_L5Pyr_L5Pyr_nmda\": 0.0005,\n", + " \"gbar_evdist_1_L2Basket_ampa\": 0.006562,\n", + " \"gbar_evdist_1_L2Basket_nmda\": 0.019482,\n", + " \"gbar_evdist_1_L2Pyr_ampa\": 7e-06,\n", + " \"gbar_evdist_1_L2Pyr_nmda\": 0.004317,\n", + " \"gbar_evdist_1_L5Pyr_ampa\": 0.1423,\n", + " \"gbar_evdist_1_L5Pyr_nmda\": 0.080074,\n", + " \"gbar_evprox_1_L2Basket_ampa\": 0.08831,\n", + " \"gbar_evprox_1_L2Basket_nmda\": 0.0,\n", + " \"gbar_evprox_1_L2Pyr_ampa\": 0.01525,\n", + " \"gbar_evprox_1_L2Pyr_nmda\": 0.0,\n", + " \"gbar_evprox_1_L5Basket_ampa\": 0.19934,\n", + " \"gbar_evprox_1_L5Basket_nmda\": 0.0,\n", + " \"gbar_evprox_1_L5Pyr_ampa\": 0.00865,\n", + " \"gbar_evprox_1_L5Pyr_nmda\": 0.0,\n", + " \"gbar_evprox_2_L2Basket_ampa\": 3e-06,\n", + " \"gbar_evprox_2_L2Basket_nmda\": 0.0,\n", + " \"gbar_evprox_2_L2Pyr_ampa\": 1.43884,\n", + " \"gbar_evprox_2_L2Pyr_nmda\": 0.0,\n", + " \"gbar_evprox_2_L5Basket_ampa\": 0.008958,\n", + " \"gbar_evprox_2_L5Basket_nmda\": 0.0,\n", + " \"gbar_evprox_2_L5Pyr_ampa\": 0.684013,\n", + " \"gbar_evprox_2_L5Pyr_nmda\": 0.0,\n", + " \"inc_evinput\": 0.0,\n", + " \"input_dist_A_delay_L2\": 5.0,\n", + " \"input_dist_A_delay_L5\": 5.0,\n", + " \"input_dist_A_weight_L2Basket_ampa\": 0.0,\n", + " \"input_dist_A_weight_L2Basket_nmda\": 0.0,\n", + " \"input_dist_A_weight_L2Pyr_ampa\": 0.0,\n", + " \"input_dist_A_weight_L2Pyr_nmda\": 0.0,\n", + " \"input_dist_A_weight_L5Pyr_ampa\": 0.0,\n", + " \"input_dist_A_weight_L5Pyr_nmda\": 0.0,\n", + " \"input_prox_A_delay_L2\": 0.1,\n", + " \"input_prox_A_delay_L5\": 1.0,\n", + " \"input_prox_A_weight_L2Basket_ampa\": 0.0,\n", + " \"input_prox_A_weight_L2Basket_nmda\": 0.0,\n", + " \"input_prox_A_weight_L2Pyr_ampa\": 0.0,\n", + " \"input_prox_A_weight_L2Pyr_nmda\": 0.0,\n", + " \"input_prox_A_weight_L5Basket_ampa\": 0.0,\n", + " \"input_prox_A_weight_L5Basket_nmda\": 0.0,\n", + " \"input_prox_A_weight_L5Pyr_ampa\": 0.0,\n", + " \"input_prox_A_weight_L5Pyr_nmda\": 0.0,\n", + " \"numspikes_evdist_1\": 1,\n", + " \"numspikes_evprox_1\": 1,\n", + " \"numspikes_evprox_2\": 1,\n", + " \"prng_seedcore_evdist_1\": 4,\n", + " \"prng_seedcore_evprox_1\": 4,\n", + " \"prng_seedcore_evprox_2\": 4,\n", + " \"prng_seedcore_extgauss\": 4,\n", + " \"prng_seedcore_extpois\": 4,\n", + " \"prng_seedcore_input_dist\": 4,\n", + " \"prng_seedcore_input_prox\": 4,\n", + " \"repeats_dist\": 10,\n", + " \"repeats_prox\": 10,\n", + " \"save_dpl\": 0,\n", + " \"save_figs\": 0,\n", + " \"save_spec_data\": 0,\n", + " \"save_vsoma\": 0,\n", + " \"sigma_t_evdist_1\": 3.85,\n", + " \"sigma_t_evprox_1\": 2.47,\n", + " \"sigma_t_evprox_2\": 8.33,\n", + " \"sim_prefix\": \"default\",\n", + " \"sync_evinput\": false,\n", + " \"t0_input_dist\": 1000,\n", + " \"t0_input_prox\": 1000.0,\n", + " \"t0_input_stdev_dist\": 0.0,\n", + " \"t0_input_stdev_prox\": 0.0,\n", + " \"t0_pois\": 0.0,\n", + " \"t_evdist_1\": 63.53,\n", + " \"t_evprox_1\": 26.61,\n", + " \"t_evprox_2\": 137.12,\n", + " \"threshold\": 0.0,\n", + " \"tstop\": 170,\n", + " \"tstop_input_dist\": 1001,\n", + " \"tstop_input_prox\": 1001\n", + "}\n", + "{\n", + " \"L2Pyr_soma_L\": 22.1,\n", + " \"L2Pyr_soma_Ra\": 200.0,\n", + " \"L2Pyr_soma_cm\": 0.6195,\n", + " \"L2Pyr_soma_diam\": 23.4,\n", + " \"L2Pyr_soma_el_hh2\": -65.0,\n", + " \"L2Pyr_soma_gbar_km\": 250.0,\n", + " \"L2Pyr_soma_gkbar_hh2\": 0.01,\n", + " \"L2Pyr_soma_gl_hh2\": 4.26e-05,\n", + " \"L2Pyr_soma_gnabar_hh2\": 0.18\n", + "}\n" + ] + } + ], + "source": [ + "import os.path as op\n", + "\n", + "###############################################################################\n", + "# Let us import hnn_core\n", + "\n", + "import hnn_core\n", + "from hnn_core import simulate_dipole, Params, Network\n", + "\n", + "hnn_core_root = op.join(op.dirname(hnn_core.__file__), '..')\n", + "\n", + "###############################################################################\n", + "# Then we read the parameters file\n", + "params_fname = op.join(hnn_core_root, 'param', 'default.json')\n", + "params = Params(params_fname)\n", + "print(params)\n", + "\n", + "###############################################################################\n", + "# This is a lot of parameters! We can also filter the\n", + "# parameters using unix-style wildcard characters\n", + "print(params['L2Pyr_soma*'])\n", + "\n", + "###############################################################################\n", + "# Now let's simulate the dipole\n", + "# You can simulate multiple trials in parallel by using n_jobs > 1\n", + "# net = Network(params)\n", + "# dpls = simulate_dipole(net, n_jobs=1, n_trials=2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import AppLayout, Button, Layout" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import Output\n", + "import matplotlib.pyplot as plt\n", + "\n", + "log_out = Output(layout={'border': '1px solid black'})\n", + "plot_out = Output(layout={'border': '1px solid black'})\n", + "\n", + "def on_button_clicked(b, variables):\n", + " with log_out:\n", + " variables['net'] = Network(params)\n", + " dpls = simulate_dipole(variables['net'], n_jobs=1, n_trials=1)\n", + " with plot_out:\n", + " dpls[0].plot()\n", + "\n", + " \n", + "def update_params(**updates):\n", + " params.update(dict(**updates))\n", + " return params\n", + "\n", + "def update_plot(plot_type):\n", + " plot_out.clear_output()\n", + "\n", + " if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'):\n", + " return\n", + " \n", + " with plot_out:\n", + " if plot_type['new'] == 'spikes':\n", + " variables['net'].plot_spikes()" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import Button, HBox, VBox" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "def create_expanded_button(description, button_style):\n", + " return Button(description=description, button_style=button_style, layout=Layout(height='10', width='auto'))\n", + "\n", + "header_button = create_expanded_button('Human Neocortical Neurosolver', 'success')\n", + "run_button = create_expanded_button('Run', 'success')" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [], + "source": [ + "def _get_min(v):\n", + " if v < 0:\n", + " return v * 10\n", + " else:\n", + " return v * 0.1\n", + "\n", + "def _get_max(v):\n", + " if v > 0:\n", + " return v * 10\n", + " else:\n", + " return v * 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import FloatSlider, Dropdown, interactive_output, interactive\n", + "from functools import partial\n", + "\n", + "variables = dict(net=None)\n", + "run_button.on_click(partial(on_button_clicked, variables=variables))\n", + "\n", + "sliders = [FloatSlider(\n", + " value=params[d], min=_get_min(params[d]), max=_get_max(params[d]), step=0.1,\n", + " description=d,\n", + " disabled=False, continuous_update=False, orientation='horizontal',\n", + " readout=True, readout_format='.2f') for d in params['L2Pyr_soma*'].keys()]\n", + "\n", + "dropdown = Dropdown(\n", + " options=['input histogram', 'dipole current', 'spikes'],\n", + " value='dipole current',\n", + " description='Plot:',\n", + " disabled=False,\n", + ")\n", + "\n", + "interactive_output(update_params, {s.description: s for s in sliders})\n", + "interactive(update_plot, plot_type='dipole current')\n", + "dropdown.observe(update_plot, 'value')\n", + "\n", + "left_box = VBox(sliders)\n", + "footer = HBox([run_button, dropdown])" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "af70085aca9f4fcfb28757a8d17c9910", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "AppLayout(children=(Button(button_style='success', description='Human Neocortical Neurosolver', layout=Layout(…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "AppLayout(header=header_button,\n", + " left_sidebar=left_box,\n", + " center=log_out,\n", + " right_sidebar=plot_out,\n", + " footer=footer,\n", + " pane_widths=[1, 1, 1])" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 2305c41ccea242b481282426ade6cd6e3dcd26cb Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 14 Sep 2019 00:00:07 -0400 Subject: [PATCH 02/88] A bit of cleanup --- hnn_widget.ipynb | 351 ++++++----------------------------------------- 1 file changed, 39 insertions(+), 312 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 1773efdbb..534b18bf3 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -2,301 +2,24 @@ "cells": [ { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"Itonic_A_L2Basket\": 0.0,\n", - " \"Itonic_A_L2Pyr_soma\": 0.0,\n", - " \"Itonic_A_L5Basket\": 0.0,\n", - " \"Itonic_A_L5Pyr_soma\": 0.0,\n", - " \"Itonic_T_L2Basket\": -1.0,\n", - " \"Itonic_T_L2Pyr_soma\": -1.0,\n", - " \"Itonic_T_L5Basket\": -1.0,\n", - " \"Itonic_T_L5Pyr_soma\": -1.0,\n", - " \"Itonic_t0_L2Basket\": 0.0,\n", - " \"Itonic_t0_L2Pyr_soma\": 0.0,\n", - " \"Itonic_t0_L5Basket\": 0.0,\n", - " \"Itonic_t0_L5Pyr_soma\": 0.0,\n", - " \"L2Basket_Gauss_A_weight\": 0.0,\n", - " \"L2Basket_Gauss_mu\": 2000.0,\n", - " \"L2Basket_Gauss_sigma\": 3.6,\n", - " \"L2Basket_Pois_A_weight_ampa\": 0.0,\n", - " \"L2Basket_Pois_A_weight_nmda\": 0.0,\n", - " \"L2Basket_Pois_lamtha\": 0.0,\n", - " \"L2Pyr_Gauss_A_weight\": 0.0,\n", - " \"L2Pyr_Gauss_mu\": 2000.0,\n", - " \"L2Pyr_Gauss_sigma\": 3.6,\n", - " \"L2Pyr_Pois_A_weight_ampa\": 0.0,\n", - " \"L2Pyr_Pois_A_weight_nmda\": 0.0,\n", - " \"L2Pyr_Pois_lamtha\": 0.0,\n", - " \"L2Pyr_ampa_e\": 0.0,\n", - " \"L2Pyr_ampa_tau1\": 0.5,\n", - " \"L2Pyr_ampa_tau2\": 5.0,\n", - " \"L2Pyr_apical1_L\": 306.0,\n", - " \"L2Pyr_apical1_diam\": 4.08,\n", - " \"L2Pyr_apicaloblique_L\": 340.0,\n", - " \"L2Pyr_apicaloblique_diam\": 3.91,\n", - " \"L2Pyr_apicaltrunk_L\": 59.5,\n", - " \"L2Pyr_apicaltrunk_diam\": 4.25,\n", - " \"L2Pyr_apicaltuft_L\": 238.0,\n", - " \"L2Pyr_apicaltuft_diam\": 3.4,\n", - " \"L2Pyr_basal1_L\": 85.0,\n", - " \"L2Pyr_basal1_diam\": 4.25,\n", - " \"L2Pyr_basal2_L\": 255.0,\n", - " \"L2Pyr_basal2_diam\": 2.72,\n", - " \"L2Pyr_basal3_L\": 255.0,\n", - " \"L2Pyr_basal3_diam\": 2.72,\n", - " \"L2Pyr_dend_Ra\": 200.0,\n", - " \"L2Pyr_dend_cm\": 0.6195,\n", - " \"L2Pyr_dend_el_hh2\": -65.0,\n", - " \"L2Pyr_dend_gbar_km\": 250.0,\n", - " \"L2Pyr_dend_gkbar_hh2\": 0.01,\n", - " \"L2Pyr_dend_gl_hh2\": 4.26e-05,\n", - " \"L2Pyr_dend_gnabar_hh2\": 0.15,\n", - " \"L2Pyr_gabaa_e\": -80.0,\n", - " \"L2Pyr_gabaa_tau1\": 0.5,\n", - " \"L2Pyr_gabaa_tau2\": 5.0,\n", - " \"L2Pyr_gabab_e\": -80.0,\n", - " \"L2Pyr_gabab_tau1\": 1.0,\n", - " \"L2Pyr_gabab_tau2\": 20.0,\n", - " \"L2Pyr_nmda_e\": 0.0,\n", - " \"L2Pyr_nmda_tau1\": 1.0,\n", - " \"L2Pyr_nmda_tau2\": 20.0,\n", - " \"L2Pyr_soma_L\": 22.1,\n", - " \"L2Pyr_soma_Ra\": 200.0,\n", - " \"L2Pyr_soma_cm\": 0.6195,\n", - " \"L2Pyr_soma_diam\": 23.4,\n", - " \"L2Pyr_soma_el_hh2\": -65.0,\n", - " \"L2Pyr_soma_gbar_km\": 250.0,\n", - " \"L2Pyr_soma_gkbar_hh2\": 0.01,\n", - " \"L2Pyr_soma_gl_hh2\": 4.26e-05,\n", - " \"L2Pyr_soma_gnabar_hh2\": 0.18,\n", - " \"L5Basket_Gauss_A_weight\": 0.0,\n", - " \"L5Basket_Gauss_mu\": 2000.0,\n", - " \"L5Basket_Gauss_sigma\": 2.0,\n", - " \"L5Basket_Pois_A_weight_ampa\": 0.0,\n", - " \"L5Basket_Pois_A_weight_nmda\": 0.0,\n", - " \"L5Basket_Pois_lamtha\": 0.0,\n", - " \"L5Pyr_Gauss_A_weight\": 0.0,\n", - " \"L5Pyr_Gauss_mu\": 2000.0,\n", - " \"L5Pyr_Gauss_sigma\": 4.8,\n", - " \"L5Pyr_Pois_A_weight_ampa\": 0.0,\n", - " \"L5Pyr_Pois_A_weight_nmda\": 0.0,\n", - " \"L5Pyr_Pois_lamtha\": 0.0,\n", - " \"L5Pyr_ampa_e\": 0.0,\n", - " \"L5Pyr_ampa_tau1\": 0.5,\n", - " \"L5Pyr_ampa_tau2\": 5.0,\n", - " \"L5Pyr_apical1_L\": 680.0,\n", - " \"L5Pyr_apical1_diam\": 7.48,\n", - " \"L5Pyr_apical2_L\": 680.0,\n", - " \"L5Pyr_apical2_diam\": 4.93,\n", - " \"L5Pyr_apicaloblique_L\": 255.0,\n", - " \"L5Pyr_apicaloblique_diam\": 5.1,\n", - " \"L5Pyr_apicaltrunk_L\": 102.0,\n", - " \"L5Pyr_apicaltrunk_diam\": 10.2,\n", - " \"L5Pyr_apicaltuft_L\": 425.0,\n", - " \"L5Pyr_apicaltuft_diam\": 3.4,\n", - " \"L5Pyr_basal1_L\": 85.0,\n", - " \"L5Pyr_basal1_diam\": 6.8,\n", - " \"L5Pyr_basal2_L\": 255.0,\n", - " \"L5Pyr_basal2_diam\": 8.5,\n", - " \"L5Pyr_basal3_L\": 255.0,\n", - " \"L5Pyr_basal3_diam\": 8.5,\n", - " \"L5Pyr_dend_Ra\": 200.0,\n", - " \"L5Pyr_dend_cm\": 0.85,\n", - " \"L5Pyr_dend_el_hh2\": -71.0,\n", - " \"L5Pyr_dend_gbar_ar\": 1e-06,\n", - " \"L5Pyr_dend_gbar_ca\": 60.0,\n", - " \"L5Pyr_dend_gbar_cat\": 0.0002,\n", - " \"L5Pyr_dend_gbar_kca\": 0.0002,\n", - " \"L5Pyr_dend_gbar_km\": 200.0,\n", - " \"L5Pyr_dend_gkbar_hh2\": 0.01,\n", - " \"L5Pyr_dend_gl_hh2\": 4.26e-05,\n", - " \"L5Pyr_dend_gnabar_hh2\": 0.14,\n", - " \"L5Pyr_dend_taur_cad\": 20.0,\n", - " \"L5Pyr_gabaa_e\": -80.0,\n", - " \"L5Pyr_gabaa_tau1\": 0.5,\n", - " \"L5Pyr_gabaa_tau2\": 5.0,\n", - " \"L5Pyr_gabab_e\": -80.0,\n", - " \"L5Pyr_gabab_tau1\": 1.0,\n", - " \"L5Pyr_gabab_tau2\": 20.0,\n", - " \"L5Pyr_nmda_e\": 0.0,\n", - " \"L5Pyr_nmda_tau1\": 1.0,\n", - " \"L5Pyr_nmda_tau2\": 20.0,\n", - " \"L5Pyr_soma_L\": 39.0,\n", - " \"L5Pyr_soma_Ra\": 200.0,\n", - " \"L5Pyr_soma_cm\": 0.85,\n", - " \"L5Pyr_soma_diam\": 28.9,\n", - " \"L5Pyr_soma_el_hh2\": -65.0,\n", - " \"L5Pyr_soma_gbar_ar\": 1e-06,\n", - " \"L5Pyr_soma_gbar_ca\": 60.0,\n", - " \"L5Pyr_soma_gbar_cat\": 0.0002,\n", - " \"L5Pyr_soma_gbar_kca\": 0.0002,\n", - " \"L5Pyr_soma_gbar_km\": 200.0,\n", - " \"L5Pyr_soma_gkbar_hh2\": 0.01,\n", - " \"L5Pyr_soma_gl_hh2\": 4.26e-05,\n", - " \"L5Pyr_soma_gnabar_hh2\": 0.16,\n", - " \"L5Pyr_soma_taur_cad\": 20.0,\n", - " \"N_pyr_x\": 10,\n", - " \"N_pyr_y\": 10,\n", - " \"N_trials\": 1,\n", - " \"T_pois\": -1,\n", - " \"celsius\": 37.0,\n", - " \"dipole_scalefctr\": 3000,\n", - " \"dipole_smooth_win\": 30,\n", - " \"distribution_dist\": \"normal\",\n", - " \"distribution_prox\": \"normal\",\n", - " \"dt\": 0.025,\n", - " \"dt_evprox0_evdist\": -1,\n", - " \"dt_evprox0_evprox1\": -1,\n", - " \"events_per_cycle_dist\": 2,\n", - " \"events_per_cycle_prox\": 2,\n", - " \"f_input_dist\": 10.0,\n", - " \"f_input_prox\": 10.0,\n", - " \"f_max_spec\": 100,\n", - " \"f_stdev_dist\": 20.0,\n", - " \"f_stdev_prox\": 20.0,\n", - " \"gbar_L2Basket_L2Basket\": 0.02,\n", - " \"gbar_L2Basket_L2Pyr_gabaa\": 0.05,\n", - " \"gbar_L2Basket_L2Pyr_gabab\": 0.05,\n", - " \"gbar_L2Basket_L5Pyr\": 0.001,\n", - " \"gbar_L2Pyr_L2Basket\": 0.0005,\n", - " \"gbar_L2Pyr_L2Pyr_ampa\": 0.0005,\n", - " \"gbar_L2Pyr_L2Pyr_nmda\": 0.0005,\n", - " \"gbar_L2Pyr_L5Basket\": 0.00025,\n", - " \"gbar_L2Pyr_L5Pyr\": 0.00025,\n", - " \"gbar_L5Basket_L5Basket\": 0.02,\n", - " \"gbar_L5Basket_L5Pyr_gabaa\": 0.025,\n", - " \"gbar_L5Basket_L5Pyr_gabab\": 0.025,\n", - " \"gbar_L5Pyr_L5Basket\": 0.0005,\n", - " \"gbar_L5Pyr_L5Pyr_ampa\": 0.0005,\n", - " \"gbar_L5Pyr_L5Pyr_nmda\": 0.0005,\n", - " \"gbar_evdist_1_L2Basket_ampa\": 0.006562,\n", - " \"gbar_evdist_1_L2Basket_nmda\": 0.019482,\n", - " \"gbar_evdist_1_L2Pyr_ampa\": 7e-06,\n", - " \"gbar_evdist_1_L2Pyr_nmda\": 0.004317,\n", - " \"gbar_evdist_1_L5Pyr_ampa\": 0.1423,\n", - " \"gbar_evdist_1_L5Pyr_nmda\": 0.080074,\n", - " \"gbar_evprox_1_L2Basket_ampa\": 0.08831,\n", - " \"gbar_evprox_1_L2Basket_nmda\": 0.0,\n", - " \"gbar_evprox_1_L2Pyr_ampa\": 0.01525,\n", - " \"gbar_evprox_1_L2Pyr_nmda\": 0.0,\n", - " \"gbar_evprox_1_L5Basket_ampa\": 0.19934,\n", - " \"gbar_evprox_1_L5Basket_nmda\": 0.0,\n", - " \"gbar_evprox_1_L5Pyr_ampa\": 0.00865,\n", - " \"gbar_evprox_1_L5Pyr_nmda\": 0.0,\n", - " \"gbar_evprox_2_L2Basket_ampa\": 3e-06,\n", - " \"gbar_evprox_2_L2Basket_nmda\": 0.0,\n", - " \"gbar_evprox_2_L2Pyr_ampa\": 1.43884,\n", - " \"gbar_evprox_2_L2Pyr_nmda\": 0.0,\n", - " \"gbar_evprox_2_L5Basket_ampa\": 0.008958,\n", - " \"gbar_evprox_2_L5Basket_nmda\": 0.0,\n", - " \"gbar_evprox_2_L5Pyr_ampa\": 0.684013,\n", - " \"gbar_evprox_2_L5Pyr_nmda\": 0.0,\n", - " \"inc_evinput\": 0.0,\n", - " \"input_dist_A_delay_L2\": 5.0,\n", - " \"input_dist_A_delay_L5\": 5.0,\n", - " \"input_dist_A_weight_L2Basket_ampa\": 0.0,\n", - " \"input_dist_A_weight_L2Basket_nmda\": 0.0,\n", - " \"input_dist_A_weight_L2Pyr_ampa\": 0.0,\n", - " \"input_dist_A_weight_L2Pyr_nmda\": 0.0,\n", - " \"input_dist_A_weight_L5Pyr_ampa\": 0.0,\n", - " \"input_dist_A_weight_L5Pyr_nmda\": 0.0,\n", - " \"input_prox_A_delay_L2\": 0.1,\n", - " \"input_prox_A_delay_L5\": 1.0,\n", - " \"input_prox_A_weight_L2Basket_ampa\": 0.0,\n", - " \"input_prox_A_weight_L2Basket_nmda\": 0.0,\n", - " \"input_prox_A_weight_L2Pyr_ampa\": 0.0,\n", - " \"input_prox_A_weight_L2Pyr_nmda\": 0.0,\n", - " \"input_prox_A_weight_L5Basket_ampa\": 0.0,\n", - " \"input_prox_A_weight_L5Basket_nmda\": 0.0,\n", - " \"input_prox_A_weight_L5Pyr_ampa\": 0.0,\n", - " \"input_prox_A_weight_L5Pyr_nmda\": 0.0,\n", - " \"numspikes_evdist_1\": 1,\n", - " \"numspikes_evprox_1\": 1,\n", - " \"numspikes_evprox_2\": 1,\n", - " \"prng_seedcore_evdist_1\": 4,\n", - " \"prng_seedcore_evprox_1\": 4,\n", - " \"prng_seedcore_evprox_2\": 4,\n", - " \"prng_seedcore_extgauss\": 4,\n", - " \"prng_seedcore_extpois\": 4,\n", - " \"prng_seedcore_input_dist\": 4,\n", - " \"prng_seedcore_input_prox\": 4,\n", - " \"repeats_dist\": 10,\n", - " \"repeats_prox\": 10,\n", - " \"save_dpl\": 0,\n", - " \"save_figs\": 0,\n", - " \"save_spec_data\": 0,\n", - " \"save_vsoma\": 0,\n", - " \"sigma_t_evdist_1\": 3.85,\n", - " \"sigma_t_evprox_1\": 2.47,\n", - " \"sigma_t_evprox_2\": 8.33,\n", - " \"sim_prefix\": \"default\",\n", - " \"sync_evinput\": false,\n", - " \"t0_input_dist\": 1000,\n", - " \"t0_input_prox\": 1000.0,\n", - " \"t0_input_stdev_dist\": 0.0,\n", - " \"t0_input_stdev_prox\": 0.0,\n", - " \"t0_pois\": 0.0,\n", - " \"t_evdist_1\": 63.53,\n", - " \"t_evprox_1\": 26.61,\n", - " \"t_evprox_2\": 137.12,\n", - " \"threshold\": 0.0,\n", - " \"tstop\": 170,\n", - " \"tstop_input_dist\": 1001,\n", - " \"tstop_input_prox\": 1001\n", - "}\n", - "{\n", - " \"L2Pyr_soma_L\": 22.1,\n", - " \"L2Pyr_soma_Ra\": 200.0,\n", - " \"L2Pyr_soma_cm\": 0.6195,\n", - " \"L2Pyr_soma_diam\": 23.4,\n", - " \"L2Pyr_soma_el_hh2\": -65.0,\n", - " \"L2Pyr_soma_gbar_km\": 250.0,\n", - " \"L2Pyr_soma_gkbar_hh2\": 0.01,\n", - " \"L2Pyr_soma_gl_hh2\": 4.26e-05,\n", - " \"L2Pyr_soma_gnabar_hh2\": 0.18\n", - "}\n" - ] - } - ], + "outputs": [], "source": [ "import os.path as op\n", "\n", - "###############################################################################\n", - "# Let us import hnn_core\n", - "\n", "import hnn_core\n", "from hnn_core import simulate_dipole, Params, Network\n", "\n", "hnn_core_root = op.join(op.dirname(hnn_core.__file__), '..')\n", "\n", - "###############################################################################\n", - "# Then we read the parameters file\n", "params_fname = op.join(hnn_core_root, 'param', 'default.json')\n", - "params = Params(params_fname)\n", - "print(params)\n", - "\n", - "###############################################################################\n", - "# This is a lot of parameters! We can also filter the\n", - "# parameters using unix-style wildcard characters\n", - "print(params['L2Pyr_soma*'])\n", - "\n", - "###############################################################################\n", - "# Now let's simulate the dipole\n", - "# You can simulate multiple trials in parallel by using n_jobs > 1\n", - "# net = Network(params)\n", - "# dpls = simulate_dipole(net, n_jobs=1, n_trials=2)\n" + "params = Params(params_fname)" ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -305,22 +28,22 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from ipywidgets import Output\n", "import matplotlib.pyplot as plt\n", "\n", - "log_out = Output(layout={'border': '1px solid black'})\n", - "plot_out = Output(layout={'border': '1px solid black'})\n", + "log_out = Output(layout={'border': '1px solid gray'})\n", + "plot_out = Output(layout={'border': '1px solid gray'})\n", "\n", "def on_button_clicked(b, variables):\n", " with log_out:\n", " variables['net'] = Network(params)\n", - " dpls = simulate_dipole(variables['net'], n_jobs=1, n_trials=1)\n", + " variables['dpls'] = simulate_dipole(variables['net'], n_jobs=1, n_trials=1)\n", " with plot_out:\n", - " dpls[0].plot()\n", + " variables['dpls'][0].plot()\n", "\n", " \n", "def update_params(**updates):\n", @@ -335,12 +58,28 @@ " \n", " with plot_out:\n", " if plot_type['new'] == 'spikes':\n", - " variables['net'].plot_spikes()" + " variables['net'].plot_spikes()\n", + " elif plot_type['new'] == 'dipole current':\n", + " variables['dpls'][0].plot()\n", + " elif plot_type['new'] == 'input histogram':\n", + " variables['net'].plot_input()\n", + " \n", + "def _get_min(v):\n", + " if v < 0:\n", + " return v * 10\n", + " else:\n", + " return v * 0.1\n", + "\n", + "def _get_max(v):\n", + " if v > 0:\n", + " return v * 10\n", + " else:\n", + " return v * 0.1" ] }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -349,7 +88,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -362,33 +101,14 @@ }, { "cell_type": "code", - "execution_count": 86, - "metadata": {}, - "outputs": [], - "source": [ - "def _get_min(v):\n", - " if v < 0:\n", - " return v * 10\n", - " else:\n", - " return v * 0.1\n", - "\n", - "def _get_max(v):\n", - " if v > 0:\n", - " return v * 10\n", - " else:\n", - " return v * 0.1" - ] - }, - { - "cell_type": "code", - "execution_count": 87, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "from ipywidgets import FloatSlider, Dropdown, interactive_output, interactive\n", "from functools import partial\n", "\n", - "variables = dict(net=None)\n", + "variables = dict(net=None, dpl=None)\n", "run_button.on_click(partial(on_button_clicked, variables=variables))\n", "\n", "sliders = [FloatSlider(\n", @@ -414,7 +134,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 7, "metadata": { "slideshow": { "slide_type": "slide" @@ -424,7 +144,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "af70085aca9f4fcfb28757a8d17c9910", + "model_id": "ab3becc054164716b387fd9d912e81be", "version_major": 2, "version_minor": 0 }, @@ -444,6 +164,13 @@ " footer=footer,\n", " pane_widths=[1, 1, 1])" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From dd6796bd5b5680c9a0dab3cea5e379bb660e96a1 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 13 Apr 2021 15:49:26 -0400 Subject: [PATCH 03/88] Update for new API --- hnn_widget.ipynb | 161 ++++++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 71 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 534b18bf3..cada03a39 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -9,12 +9,12 @@ "import os.path as op\n", "\n", "import hnn_core\n", - "from hnn_core import simulate_dipole, Params, Network\n", + "from hnn_core import simulate_dipole, read_params, Network\n", "\n", - "hnn_core_root = op.join(op.dirname(hnn_core.__file__), '..')\n", + "hnn_core_root = op.join(op.dirname(hnn_core.__file__))\n", "\n", "params_fname = op.join(hnn_core_root, 'param', 'default.json')\n", - "params = Params(params_fname)" + "params = read_params(params_fname)" ] }, { @@ -32,38 +32,42 @@ "metadata": {}, "outputs": [], "source": [ - "from ipywidgets import Output\n", - "import matplotlib.pyplot as plt\n", - "\n", - "log_out = Output(layout={'border': '1px solid gray'})\n", - "plot_out = Output(layout={'border': '1px solid gray'})\n", - "\n", - "def on_button_clicked(b, variables):\n", - " with log_out:\n", - " variables['net'] = Network(params)\n", - " variables['dpls'] = simulate_dipole(variables['net'], n_jobs=1, n_trials=1)\n", - " with plot_out:\n", - " variables['dpls'][0].plot()\n", + "# HNN Banner\n", + "def create_expanded_button(description, button_style):\n", + " return Button(description=description, button_style=button_style, layout=Layout(height='10', width='auto'))\n", "\n", - " \n", + "header_button = create_expanded_button('Human Neocortical Neurosolver', 'success')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "285629137ceb476d982830e46eac5eee", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Sliders to change Params\n", + "from ipywidgets import FloatSlider, Dropdown, interactive_output, interactive\n", + "from functools import partial\n", + " \n", "def update_params(**updates):\n", " params.update(dict(**updates))\n", " return params\n", - "\n", - "def update_plot(plot_type):\n", - " plot_out.clear_output()\n", - "\n", - " if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'):\n", - " return\n", " \n", - " with plot_out:\n", - " if plot_type['new'] == 'spikes':\n", - " variables['net'].plot_spikes()\n", - " elif plot_type['new'] == 'dipole current':\n", - " variables['dpls'][0].plot()\n", - " elif plot_type['new'] == 'input histogram':\n", - " variables['net'].plot_input()\n", - " \n", "def _get_min(v):\n", " if v < 0:\n", " return v * 10\n", @@ -74,16 +78,18 @@ " if v > 0:\n", " return v * 10\n", " else:\n", - " return v * 0.1" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import Button, HBox, VBox" + " return v * 0.1\n", + "\n", + " \n", + "variables = dict(net=None, dpl=None)\n", + "\n", + "sliders = [FloatSlider(\n", + " value=params[d], min=_get_min(params[d]), max=_get_max(params[d]), step=0.1,\n", + " description=d,\n", + " disabled=False, continuous_update=False, orientation='horizontal',\n", + " readout=True, readout_format='.2f') for d in params['L2Pyr_soma*'].keys()]\n", + "\n", + "interactive_output(update_params, {s.description: s for s in sliders})" ] }, { @@ -92,30 +98,24 @@ "metadata": {}, "outputs": [], "source": [ - "def create_expanded_button(description, button_style):\n", - " return Button(description=description, button_style=button_style, layout=Layout(height='10', width='auto'))\n", + "from ipywidgets import HBox, VBox\n", "\n", - "header_button = create_expanded_button('Human Neocortical Neurosolver', 'success')\n", - "run_button = create_expanded_button('Run', 'success')" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import FloatSlider, Dropdown, interactive_output, interactive\n", - "from functools import partial\n", + "# Dropdown menu to switch between plots\n", "\n", - "variables = dict(net=None, dpl=None)\n", - "run_button.on_click(partial(on_button_clicked, variables=variables))\n", + "def update_plot(plot_type):\n", + " plot_out.clear_output()\n", + "\n", + " if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'):\n", + " return\n", + " \n", + " with plot_out:\n", + " if plot_type['new'] == 'spikes':\n", + " variables['net'].cell_response.plot_spikes_raster()\n", + " elif plot_type['new'] == 'dipole current':\n", + " variables['dpls'][0].plot()\n", + " elif plot_type['new'] == 'input histogram':\n", + " variables['net'].cell_response.plot_spikes_hist()\n", "\n", - "sliders = [FloatSlider(\n", - " value=params[d], min=_get_min(params[d]), max=_get_max(params[d]), step=0.1,\n", - " description=d,\n", - " disabled=False, continuous_update=False, orientation='horizontal',\n", - " readout=True, readout_format='.2f') for d in params['L2Pyr_soma*'].keys()]\n", "\n", "dropdown = Dropdown(\n", " options=['input histogram', 'dipole current', 'spikes'],\n", @@ -124,11 +124,36 @@ " disabled=False,\n", ")\n", "\n", - "interactive_output(update_params, {s.description: s for s in sliders})\n", "interactive(update_plot, plot_type='dipole current')\n", "dropdown.observe(update_plot, 'value')\n", "\n", - "left_box = VBox(sliders)\n", + "left_box = VBox(sliders)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Run button\n", + "from ipywidgets import Output\n", + "\n", + "log_out = Output(layout={'border': '1px solid gray'})\n", + "plot_out = Output(layout={'border': '1px solid gray'})\n", + "\n", + "\n", + "def on_button_clicked(b, variables):\n", + " \"\"\"Run the simulation and plot outputs.\"\"\"\n", + " with log_out:\n", + " variables['net'] = Network(params, add_drives_from_params=True)\n", + " variables['dpls'] = simulate_dipole(variables['net'], n_trials=1)\n", + " with plot_out:\n", + " variables['dpls'][0].plot()\n", + "\n", + "run_button = create_expanded_button('Run', 'success')\n", + "run_button.on_click(partial(on_button_clicked, variables=variables))\n", + "\n", "footer = HBox([run_button, dropdown])" ] }, @@ -144,7 +169,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ab3becc054164716b387fd9d912e81be", + "model_id": "7166a4f54a1a4f4abaebc2e2c4c42031", "version_major": 2, "version_minor": 0 }, @@ -157,6 +182,7 @@ } ], "source": [ + "# Final layout of the app\n", "AppLayout(header=header_button,\n", " left_sidebar=left_box,\n", " center=log_out,\n", @@ -164,13 +190,6 @@ " footer=footer,\n", " pane_widths=[1, 1, 1])" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 771fc723dd0bd0b5f0d043b66e9abb91a1408fe9 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 14 Apr 2021 18:01:07 -0400 Subject: [PATCH 04/88] Add accordions and tabs --- hnn_widget.ipynb | 84 +++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index cada03a39..105c3e056 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 50, "metadata": {}, "outputs": [], "source": [ @@ -19,16 +19,16 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ - "from ipywidgets import AppLayout, Button, Layout" + "from ipywidgets import AppLayout, Button" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 52, "metadata": {}, "outputs": [], "source": [ @@ -41,27 +41,24 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 105, "metadata": {}, "outputs": [ { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "285629137ceb476d982830e46eac5eee", - "version_major": 2, - "version_minor": 0 - }, "text/plain": [ - "Output()" + "[None, None]" ] }, + "execution_count": 105, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ "# Sliders to change Params\n", - "from ipywidgets import FloatSlider, Dropdown, interactive_output, interactive\n", + "from ipywidgets import (FloatSlider, Dropdown, Layout, Accordion, Tab, VBox,\n", + " interactive_output, interactive)\n", "from functools import partial\n", " \n", "def update_params(**updates):\n", @@ -83,18 +80,49 @@ " \n", "variables = dict(net=None, dpl=None)\n", "\n", - "sliders = [FloatSlider(\n", - " value=params[d], min=_get_min(params[d]), max=_get_max(params[d]), step=0.1,\n", - " description=d,\n", - " disabled=False, continuous_update=False, orientation='horizontal',\n", - " readout=True, readout_format='.2f') for d in params['L2Pyr_soma*'].keys()]\n", - "\n", - "interactive_output(update_params, {s.description: s for s in sliders})" + "def _get_sliders(params, param_keys):\n", + " \"\"\"Get sliders\"\"\"\n", + " style = {'description_width': 'initial'}\n", + " sliders = list()\n", + " for d in param_keys:\n", + " min_val = _get_min(params[d])\n", + " max_val = _get_max(params[d])\n", + " step = (max_val - min_val) / 10.\n", + " slider = FloatSlider(\n", + " value=params[d], min=min_val, max=max_val, step=step,\n", + " description=d,\n", + " disabled=False, continuous_update=False, orientation='horizontal',\n", + " readout=True, readout_format='.2e',\n", + " style=style)\n", + " sliders.append(slider)\n", + " \n", + " interactive_output(update_params, {s.description: s for s in sliders})\n", + " return sliders\n", + "\n", + "sliders = [_get_sliders(params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda',\n", + " 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']),\n", + " _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa',\n", + " 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa',\n", + " 'gbar_L5Basket_L5Pyr_gabab']),\n", + " _get_sliders(params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']),\n", + " _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])]\n", + "\n", + "# accordians\n", + "boxes = [VBox(slider) for slider in sliders]\n", + "titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas']\n", + "accordian = Accordion(children=boxes)\n", + "[accordian.set_title(idx, title) for idx, title in enumerate(titles)]\n", + "\n", + "# Tabs\n", + "left_tab = Tab()\n", + "left_tab.children = [accordian, accordian]\n", + "titles = ['Cell connectivity', 'Drives']\n", + "[left_tab.set_title(idx, title) for idx, title in enumerate(titles)] # XXX: why doesn't this work?" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 106, "metadata": {}, "outputs": [], "source": [ @@ -125,14 +153,12 @@ ")\n", "\n", "interactive(update_plot, plot_type='dipole current')\n", - "dropdown.observe(update_plot, 'value')\n", - "\n", - "left_box = VBox(sliders)" + "dropdown.observe(update_plot, 'value')" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 107, "metadata": {}, "outputs": [], "source": [ @@ -159,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "slide" @@ -169,7 +195,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "7166a4f54a1a4f4abaebc2e2c4c42031", + "model_id": "2666cdedf14b4876adc9c6cadb3cc3f0", "version_major": 2, "version_minor": 0 }, @@ -184,7 +210,7 @@ "source": [ "# Final layout of the app\n", "AppLayout(header=header_button,\n", - " left_sidebar=left_box,\n", + " left_sidebar=left_tab,\n", " center=log_out,\n", " right_sidebar=plot_out,\n", " footer=footer,\n", @@ -209,7 +235,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.8.6" } }, "nbformat": 4, From 51a0f031e1283de81ac784222c7993eb39b02406 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 14 Apr 2021 21:04:19 -0400 Subject: [PATCH 05/88] Add drives interactively --- hnn_widget.ipynb | 108 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 26 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 105c3e056..b1a18b726 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 50, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -19,16 +19,16 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "from ipywidgets import AppLayout, Button" + "from ipywidgets import AppLayout, Button, Layout" ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -41,22 +41,11 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 4, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[None, None]" - ] - }, - "execution_count": 105, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "# Sliders to change Params\n", + "# Sliders to change local-connectivity Params\n", "from ipywidgets import (FloatSlider, Dropdown, Layout, Accordion, Tab, VBox,\n", " interactive_output, interactive)\n", "from functools import partial\n", @@ -111,18 +100,85 @@ "boxes = [VBox(slider) for slider in sliders]\n", "titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas']\n", "accordian = Accordion(children=boxes)\n", - "[accordian.set_title(idx, title) for idx, title in enumerate(titles)]\n", + "for idx, title in enumerate(titles):\n", + " accordian.set_title(idx, title)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Dropdown for different drives\n", "\n", - "# Tabs\n", + "from ipywidgets import FloatText, interactive, Dropdown, interact, Output\n", + "from IPython.display import display\n", + "\n", + "drives_dropdown = Dropdown(\n", + " options=['Evoked', 'Poisson', 'Rhythmic'],\n", + " value='Evoked',\n", + " description='Drive:',\n", + " disabled=False,\n", + ")\n", + "\n", + "drives_out = Output()\n", + "drive_boxes = list()\n", + "drive_titles = list()\n", + "\n", + "def add_drive_options(drive_type):\n", + "\n", + " drives_out.clear_output()\n", + " with drives_out:\n", + " if drive_type['new'] == 'Rhythmic':\n", + " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False),\n", + " FloatText(value=7.5, description='Start time dev:', disabled=False),\n", + " FloatText(value=7.5, description='Stop time:', disabled=False),\n", + " FloatText(value=7.5, description='Burst rate:', disabled=False),\n", + " FloatText(value=7.5, description='Burst std dev:', disabled=False),\n", + " FloatText(value=7.5, description='Spike ISI:', disabled=False)])\n", + " elif drive_type['new'] == 'Poisson':\n", + " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False),\n", + " FloatText(value=8.5, description='Stop time:', disabled=False),\n", + " FloatText(value=8.5, description='Rate constant:', disabled=False)])\n", + " elif drive_type['new'] == 'Evoked':\n", + " drive = VBox([FloatText(value=7.5, description='Mean time:', disabled=False),\n", + " FloatText(value=8.5, description='Std dev time:', disabled=False),\n", + " FloatText(value=8.5, description='Number of spikes:', disabled=False)])\n", + "\n", + " drive_title = drive_type['new'] + str(len(drive_boxes))\n", + " drive_titles.append(drive_title)\n", + " drive_boxes.append(drive)\n", + "\n", + " accordion = Accordion(children=drive_boxes,\n", + " selected_index=len(drive_boxes) - 1)\n", + " for idx, this_title in enumerate(drive_titles):\n", + " accordion.set_title(idx, this_title)\n", + " display(accordion)\n", + "\n", + "# XXX: should be simpler to use Stacked class starting from IPywidgets > 8.0\n", + "interactive(add_drive_options, drive_type='Evoked')\n", + "drives_dropdown.observe(add_drive_options, 'value')\n", + "drives_options = VBox([drives_dropdown, drives_out])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Tabs for left pane\n", "left_tab = Tab()\n", - "left_tab.children = [accordian, accordian]\n", + "left_tab.children = [accordian, drives_options]\n", "titles = ['Cell connectivity', 'Drives']\n", - "[left_tab.set_title(idx, title) for idx, title in enumerate(titles)] # XXX: why doesn't this work?" + "for idx, title in enumerate(titles):\n", + " left_tab.set_title(idx, title)" ] }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -158,7 +214,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -185,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 9, "metadata": { "slideshow": { "slide_type": "slide" @@ -195,7 +251,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "2666cdedf14b4876adc9c6cadb3cc3f0", + "model_id": "c8e4ec18b9f64e4b989294acd6be1cf6", "version_major": 2, "version_minor": 0 }, From 021951e5bc2b0a5051f95e9ed5bba260ced3177f Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 14 Apr 2021 22:57:21 -0400 Subject: [PATCH 06/88] Less jitter --- hnn_widget.ipynb | 50 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index b1a18b726..1a9eba3fd 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -14,7 +14,10 @@ "hnn_core_root = op.join(op.dirname(hnn_core.__file__))\n", "\n", "params_fname = op.join(hnn_core_root, 'param', 'default.json')\n", - "params = read_params(params_fname)" + "params = read_params(params_fname)\n", + "\n", + "variables = dict(net=None, dpl=None)\n", + "variables['net'] = Network(params, add_drives_from_params=True)" ] }, { @@ -66,8 +69,6 @@ " else:\n", " return v * 0.1\n", "\n", - " \n", - "variables = dict(net=None, dpl=None)\n", "\n", "def _get_sliders(params, param_keys):\n", " \"\"\"Get sliders\"\"\"\n", @@ -106,7 +107,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -115,11 +116,14 @@ "from ipywidgets import FloatText, interactive, Dropdown, interact, Output\n", "from IPython.display import display\n", "\n", + "layout = Layout(width='200px', height='auto')\n", + "\n", "drives_dropdown = Dropdown(\n", " options=['Evoked', 'Poisson', 'Rhythmic'],\n", " value='Evoked',\n", " description='Drive:',\n", " disabled=False,\n", + " layout=layout\n", ")\n", "\n", "drives_out = Output()\n", @@ -131,20 +135,20 @@ " drives_out.clear_output()\n", " with drives_out:\n", " if drive_type['new'] == 'Rhythmic':\n", - " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False),\n", - " FloatText(value=7.5, description='Start time dev:', disabled=False),\n", - " FloatText(value=7.5, description='Stop time:', disabled=False),\n", - " FloatText(value=7.5, description='Burst rate:', disabled=False),\n", - " FloatText(value=7.5, description='Burst std dev:', disabled=False),\n", - " FloatText(value=7.5, description='Spike ISI:', disabled=False)])\n", + " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False, layout=layout),\n", + " FloatText(value=7.5, description='Start time dev:', disabled=False, layout=layout),\n", + " FloatText(value=7.5, description='Stop time:', disabled=False, layout=layout),\n", + " FloatText(value=7.5, description='Burst rate:', disabled=False, layout=layout),\n", + " FloatText(value=7.5, description='Burst std dev:', disabled=False, layout=layout),\n", + " FloatText(value=7.5, description='Spike ISI:', disabled=False, layout=layout)])\n", " elif drive_type['new'] == 'Poisson':\n", - " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False),\n", - " FloatText(value=8.5, description='Stop time:', disabled=False),\n", - " FloatText(value=8.5, description='Rate constant:', disabled=False)])\n", + " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False, layout=layout),\n", + " FloatText(value=8.5, description='Stop time:', disabled=False, layout=layout),\n", + " FloatText(value=8.5, description='Rate constant:', disabled=False, layout=layout)])\n", " elif drive_type['new'] == 'Evoked':\n", - " drive = VBox([FloatText(value=7.5, description='Mean time:', disabled=False),\n", - " FloatText(value=8.5, description='Std dev time:', disabled=False),\n", - " FloatText(value=8.5, description='Number of spikes:', disabled=False)])\n", + " drive = VBox([FloatText(value=7.5, description='Mean time:', disabled=False, layout=layout),\n", + " FloatText(value=8.5, description='Std dev time:', disabled=False, layout=layout),\n", + " FloatText(value=8.5, description='Number of spikes:', disabled=False, layout=layout)])\n", "\n", " drive_title = drive_type['new'] + str(len(drive_boxes))\n", " drive_titles.append(drive_title)\n", @@ -164,7 +168,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -178,7 +182,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -214,7 +218,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -228,7 +232,6 @@ "def on_button_clicked(b, variables):\n", " \"\"\"Run the simulation and plot outputs.\"\"\"\n", " with log_out:\n", - " variables['net'] = Network(params, add_drives_from_params=True)\n", " variables['dpls'] = simulate_dipole(variables['net'], n_trials=1)\n", " with plot_out:\n", " variables['dpls'][0].plot()\n", @@ -241,7 +244,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "slide" @@ -251,7 +254,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c8e4ec18b9f64e4b989294acd6be1cf6", + "model_id": "baaaacd0bec74e4c80591445290ee878", "version_major": 2, "version_minor": 0 }, @@ -270,7 +273,8 @@ " center=log_out,\n", " right_sidebar=plot_out,\n", " footer=footer,\n", - " pane_widths=[1, 1, 1])" + " pane_widths=['380px', 1, 1],\n", + " pane_heights=[1, '500px', 1])" ] } ], From a5d6b5b7dccbb91bb9e1968333362777af39ed28 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 15 Apr 2021 02:12:54 -0400 Subject: [PATCH 07/88] WIP: adding drives to net --- hnn_widget.ipynb | 89 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 1a9eba3fd..f885dccf4 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -16,7 +16,7 @@ "params_fname = op.join(hnn_core_root, 'param', 'default.json')\n", "params = read_params(params_fname)\n", "\n", - "variables = dict(net=None, dpl=None)\n", + "variables = dict(net=None, dpls=None)\n", "variables['net'] = Network(params, add_drives_from_params=True)" ] }, @@ -107,13 +107,13 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# Dropdown for different drives\n", "\n", - "from ipywidgets import FloatText, interactive, Dropdown, interact, Output\n", + "from ipywidgets import FloatText, interactive, Dropdown, interact, Output, RadioButtons\n", "from IPython.display import display\n", "\n", "layout = Layout(width='200px', height='auto')\n", @@ -134,23 +134,61 @@ "\n", " drives_out.clear_output()\n", " with drives_out:\n", + " drive_title = drive_type['new'] + str(len(drive_boxes))\n", + "\n", " if drive_type['new'] == 'Rhythmic':\n", - " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False, layout=layout),\n", - " FloatText(value=7.5, description='Start time dev:', disabled=False, layout=layout),\n", - " FloatText(value=7.5, description='Stop time:', disabled=False, layout=layout),\n", - " FloatText(value=7.5, description='Burst rate:', disabled=False, layout=layout),\n", - " FloatText(value=7.5, description='Burst std dev:', disabled=False, layout=layout),\n", - " FloatText(value=7.5, description='Spike ISI:', disabled=False, layout=layout)])\n", - " elif drive_type['new'] == 'Poisson':\n", - " drive = VBox([FloatText(value=7.5, description='Start time:', disabled=False, layout=layout),\n", - " FloatText(value=8.5, description='Stop time:', disabled=False, layout=layout),\n", - " FloatText(value=8.5, description='Rate constant:', disabled=False, layout=layout)])\n", - " elif drive_type['new'] == 'Evoked':\n", - " drive = VBox([FloatText(value=7.5, description='Mean time:', disabled=False, layout=layout),\n", - " FloatText(value=8.5, description='Std dev time:', disabled=False, layout=layout),\n", - " FloatText(value=8.5, description='Number of spikes:', disabled=False, layout=layout)])\n", + " tstart = FloatText(value=7.5, description='Start time:', layout=layout)\n", + " tstart_std = FloatText(value=7.5, description='Start time dev:', layout=layout)\n", + " tstop = FloatText(value=7.5, description='Stop time:', layout=layout)\n", + " burst_rate = FloatText(value=7.5, description='Burst rate:', layout=layout)\n", + " burst_std = FloatText(value=7.5, description='Burst std dev:', layout=layout)\n", + " location = RadioButtons(options=['proximal', 'distal'])\n", + "\n", + " drive = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, location])\n", + " variables['net'].add_bursty_drive(name=drive_title,\n", + " tstart=tstart.value,\n", + " tstart_std=tstart_std.value,\n", + " burst_rate=burst_rate.value,\n", + " burst_std=burst_std.value,\n", + " location=location.value)\n", + " elif drive_type['new'] == 'Poisson': \n", + " tstart = FloatText(value=7.5, description='Start time:', layout=layout)\n", + " tstop = FloatText(value=8.5, description='Stop time:', layout=layout)\n", + " rate_constant = FloatText(value=8.5, description='Rate constant:', layout=layout)\n", + " location = RadioButtons(options=['proximal', 'distal'])\n", + " weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout)\n", + " weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout)\n", + " \n", + " drive = VBox([tstart, tstop, rate_constant, location,\n", + " weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", + " variables['net'].add_poisson_drive(name=drive_title,\n", + " tstart=tstart.value, \n", + " tstop=tstop.value,\n", + " rate_constant=rate_constant.value, \n", + " location=location.value, \n", + " weights_ampa=dict(L5_pyramidal=weights_ampa_L5Pyr.value), \n", + " weights_nmda=dict(L5_pyramidal=weights_nmda_L5Pyr.value), \n", + " space_constant=100.0\n", + " )\n", + " elif drive_type['new'] == 'Evoked': \n", + " mu = FloatText(value=7.5, description='Mean time:', layout=layout)\n", + " sigma = FloatText(value=8.5, description='Std dev time:', layout=layout)\n", + " numspikes = FloatText(value=8.5, description='Number of spikes:', layout=layout)\n", + " location = RadioButtons(options=['proximal', 'distal'])\n", + " weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout)\n", + " weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout)\n", + "\n", + " drive = VBox([mu, sigma, numspikes, location, weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", + " variables['net'].add_evoked_drive(name=drive_title,\n", + " mu=mu.value, \n", + " sigma=sigma.value, \n", + " numspikes=numspikes.value, \n", + " sync_within_trial=False, \n", + " location=location.value, \n", + " weights_ampa=dict(L5_pyramidal=weights_ampa_L5Pyr.value), \n", + " weights_nmda=dict(L5_pyramidal=weights_nmda_L5Pyr.value), \n", + " space_constant=3.0)\n", "\n", - " drive_title = drive_type['new'] + str(len(drive_boxes))\n", " drive_titles.append(drive_title)\n", " drive_boxes.append(drive)\n", "\n", @@ -168,7 +206,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -182,7 +220,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -218,7 +256,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -232,19 +270,22 @@ "def on_button_clicked(b, variables):\n", " \"\"\"Run the simulation and plot outputs.\"\"\"\n", " with log_out:\n", + " # XXX: loop over drives and add them here\n", " variables['dpls'] = simulate_dipole(variables['net'], n_trials=1)\n", " with plot_out:\n", " variables['dpls'][0].plot()\n", "\n", "run_button = create_expanded_button('Run', 'success')\n", + "load_button = create_expanded_button('Load parameters', 'success')\n", + "\n", "run_button.on_click(partial(on_button_clicked, variables=variables))\n", "\n", - "footer = HBox([run_button, dropdown])" + "footer = HBox([run_button, load_button, dropdown])" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 9, "metadata": { "slideshow": { "slide_type": "slide" @@ -254,7 +295,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "baaaacd0bec74e4c80591445290ee878", + "model_id": "7c9e9b80108748b4b4ec346e90db1ee7", "version_major": 2, "version_minor": 0 }, From a54679aae51c6c9ad9e445b729434a59ce593ced Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 15 Apr 2021 19:49:28 -0400 Subject: [PATCH 08/88] ENH: add drives before simulation --- hnn_widget.ipynb | 148 ++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 67 deletions(-) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index f885dccf4..8dbc2b8c5 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -17,7 +17,7 @@ "params = read_params(params_fname)\n", "\n", "variables = dict(net=None, dpls=None)\n", - "variables['net'] = Network(params, add_drives_from_params=True)" + "variables['net'] = Network(params, add_drives_from_params=False)" ] }, { @@ -25,17 +25,10 @@ "execution_count": 2, "metadata": {}, "outputs": [], - "source": [ - "from ipywidgets import AppLayout, Button, Layout" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], "source": [ "# HNN Banner\n", + "from ipywidgets import Button, Layout\n", + "\n", "def create_expanded_button(description, button_style):\n", " return Button(description=description, button_style=button_style, layout=Layout(height='10', width='auto'))\n", "\n", @@ -44,7 +37,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -107,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -127,10 +120,11 @@ ")\n", "\n", "drives_out = Output()\n", - "drive_boxes = list()\n", + "drive_widgets = list()\n", "drive_titles = list()\n", + "drive_boxes = list()\n", "\n", - "def add_drive_options(drive_type):\n", + "def add_drive_widget(drive_type):\n", "\n", " drives_out.clear_output()\n", " with drives_out:\n", @@ -144,13 +138,14 @@ " burst_std = FloatText(value=7.5, description='Burst std dev:', layout=layout)\n", " location = RadioButtons(options=['proximal', 'distal'])\n", "\n", - " drive = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, location])\n", - " variables['net'].add_bursty_drive(name=drive_title,\n", - " tstart=tstart.value,\n", - " tstart_std=tstart_std.value,\n", - " burst_rate=burst_rate.value,\n", - " burst_std=burst_std.value,\n", - " location=location.value)\n", + " drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, location])\n", + " drive = dict(type='Rhythmic', name=drive_title,\n", + " tstart=tstart,\n", + " tstart_std=tstart_std,\n", + " burst_rate=burst_rate,\n", + " burst_std=burst_std,\n", + " location=location\n", + " )\n", " elif drive_type['new'] == 'Poisson': \n", " tstart = FloatText(value=7.5, description='Start time:', layout=layout)\n", " tstop = FloatText(value=8.5, description='Stop time:', layout=layout)\n", @@ -159,17 +154,16 @@ " weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout)\n", " weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout)\n", " \n", - " drive = VBox([tstart, tstop, rate_constant, location,\n", - " weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", - " variables['net'].add_poisson_drive(name=drive_title,\n", - " tstart=tstart.value, \n", - " tstop=tstop.value,\n", - " rate_constant=rate_constant.value, \n", - " location=location.value, \n", - " weights_ampa=dict(L5_pyramidal=weights_ampa_L5Pyr.value), \n", - " weights_nmda=dict(L5_pyramidal=weights_nmda_L5Pyr.value), \n", - " space_constant=100.0\n", - " )\n", + " drive_box = VBox([tstart, tstop, rate_constant, location,\n", + " weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", + " drive = dict(type='Poisson', name=drive_title,\n", + " tstart=tstart, \n", + " tstop=tstop,\n", + " rate_constant=rate_constant, \n", + " location=location, \n", + " weights_ampa_L5Pyr=weights_ampa_L5Pyr, \n", + " weights_nmda_L5Pyr=weights_nmda_L5Pyr\n", + " )\n", " elif drive_type['new'] == 'Evoked': \n", " mu = FloatText(value=7.5, description='Mean time:', layout=layout)\n", " sigma = FloatText(value=8.5, description='Std dev time:', layout=layout)\n", @@ -178,19 +172,20 @@ " weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout)\n", " weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout)\n", "\n", - " drive = VBox([mu, sigma, numspikes, location, weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", - " variables['net'].add_evoked_drive(name=drive_title,\n", - " mu=mu.value, \n", - " sigma=sigma.value, \n", - " numspikes=numspikes.value, \n", - " sync_within_trial=False, \n", - " location=location.value, \n", - " weights_ampa=dict(L5_pyramidal=weights_ampa_L5Pyr.value), \n", - " weights_nmda=dict(L5_pyramidal=weights_nmda_L5Pyr.value), \n", - " space_constant=3.0)\n", + " drive_box = VBox([mu, sigma, numspikes, location, weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", + " drive = dict(type='Evoked', name=drive_title,\n", + " mu=mu,\n", + " sigma=sigma, \n", + " numspikes=numspikes, \n", + " sync_within_trial=False, \n", + " location=location, \n", + " weights_ampa_L5Pyr=weights_ampa_L5Pyr, \n", + " weights_nmda_L5Pyr=weights_nmda_L5Pyr, \n", + " space_constant=3.0)\n", "\n", " drive_titles.append(drive_title)\n", - " drive_boxes.append(drive)\n", + " drive_boxes.append(drive_box)\n", + " drive_widgets.append(drive)\n", "\n", " accordion = Accordion(children=drive_boxes,\n", " selected_index=len(drive_boxes) - 1)\n", @@ -199,14 +194,14 @@ " display(accordion)\n", "\n", "# XXX: should be simpler to use Stacked class starting from IPywidgets > 8.0\n", - "interactive(add_drive_options, drive_type='Evoked')\n", - "drives_dropdown.observe(add_drive_options, 'value')\n", + "interactive(add_drive_widget, drive_type='Evoked')\n", + "drives_dropdown.observe(add_drive_widget, 'value')\n", "drives_options = VBox([drives_dropdown, drives_out])" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -220,7 +215,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -256,7 +251,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -270,7 +265,39 @@ "def on_button_clicked(b, variables):\n", " \"\"\"Run the simulation and plot outputs.\"\"\"\n", " with log_out:\n", - " # XXX: loop over drives and add them here\n", + " for drive in drive_widgets:\n", + " if drive['type'] == 'Poisson':\n", + " variables['net'].add_poisson_drive(\n", + " name=drive['name'],\n", + " tstart=drive['tstart'].value, \n", + " tstop=drive['tstop'].value,\n", + " rate_constant=dict(L5_pyramidal=drive['rate_constant'].value), \n", + " location=drive['location'].value,\n", + " weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), \n", + " weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), \n", + " space_constant=100.0\n", + " )\n", + " elif drive['type'] == 'Evoked':\n", + " variables['net'].add_evoked_drive(\n", + " name=drive['name'],\n", + " mu=drive['mu'].value,\n", + " sigma=drive['sigma'].value, \n", + " numspikes=drive['numspikes'].value, \n", + " sync_within_trial=False, \n", + " location=drive['location'].value, \n", + " weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), \n", + " weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), \n", + " space_constant=3.0\n", + " )\n", + " elif drive['type'] == 'Rhythmic':\n", + " variables['net'].add_bursty_drive(\n", + " name=drive['name'],\n", + " tstart=drive['tstart'].value,\n", + " tstart_std=drive['tstart_std'].value,\n", + " burst_rate=drive['burst_rate'].value,\n", + " burst_std=drive['burst_std'].value,\n", + " location=drive['location'].value\n", + " )\n", " variables['dpls'] = simulate_dipole(variables['net'], n_trials=1)\n", " with plot_out:\n", " variables['dpls'][0].plot()\n", @@ -285,30 +312,17 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7c9e9b80108748b4b4ec346e90db1ee7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "AppLayout(children=(Button(button_style='success', description='Human Neocortical Neurosolver', layout=Layout(…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Final layout of the app\n", + "from ipywidgets import AppLayout\n", + "\n", "AppLayout(header=header_button,\n", " left_sidebar=left_tab,\n", " center=log_out,\n", @@ -336,7 +350,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.7.4" } }, "nbformat": 4, From dcc99cfd12199ce49cbc9e8f8d536e87ef905423 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 15 Apr 2021 23:57:53 -0400 Subject: [PATCH 09/88] Move code away from jupyter --- hnn_core/gui.py | 283 +++++++++++++++++++++++++++++++++++++++ hnn_widget.ipynb | 341 +++-------------------------------------------- 2 files changed, 301 insertions(+), 323 deletions(-) create mode 100644 hnn_core/gui.py diff --git a/hnn_core/gui.py b/hnn_core/gui.py new file mode 100644 index 000000000..e77bb2263 --- /dev/null +++ b/hnn_core/gui.py @@ -0,0 +1,283 @@ +"""IPywidgets GUI.""" +# Authors: Mainak Jas + +import os.path as op +from functools import partial, update_wrapper + +import hnn_core +from hnn_core import simulate_dipole, read_params, Network + +from IPython.display import display + +from ipywidgets import (FloatSlider, Dropdown, Accordion, Tab, + interactive_output, HBox, VBox, + FloatText, interactive, Dropdown, interact, Output, + RadioButtons, Button) +from ipywidgets import Layout, AppLayout, fixed + + +drives_out = Output() +drive_widgets = list() +drive_titles = list() +drive_boxes = list() + + +def create_expanded_button(description, button_style): + return Button(description=description, button_style=button_style, + layout=Layout(height='10', width='auto')) + + +def update_params(params, **updates): + params.update(dict(**updates)) + return params + + +def _get_min(v): + if v < 0: + return v * 10 + else: + return v * 0.1 + + +def _get_max(v): + if v > 0: + return v * 10 + else: + return v * 0.1 + + +def _get_sliders(params, param_keys): + """Get sliders""" + style = {'description_width': 'initial'} + sliders = list() + for d in param_keys: + min_val = _get_min(params[d]) + max_val = _get_max(params[d]) + step = (max_val - min_val) / 10. + slider = FloatSlider( + value=params[d], min=min_val, max=max_val, step=step, + description=d, + disabled=False, continuous_update=False, orientation='horizontal', + readout=True, readout_format='.2e', + style=style) + sliders.append(slider) + + _update_params = partial(update_params, params) + interactive_output(update_params, {s.description: s for s in sliders}) + return sliders + + +def add_drive_widget(drive_type): + + layout = Layout(width='200px', height='auto') + drives_out.clear_output() + with drives_out: + drive_title = drive_type['new'] + str(len(drive_boxes)) + + if drive_type['new'] == 'Rhythmic': + tstart = FloatText(value=7.5, description='Start time:', layout=layout) + tstart_std = FloatText(value=7.5, description='Start time dev:', layout=layout) + tstop = FloatText(value=7.5, description='Stop time:', layout=layout) + burst_rate = FloatText(value=7.5, description='Burst rate:', layout=layout) + burst_std = FloatText(value=7.5, description='Burst std dev:', layout=layout) + location = RadioButtons(options=['proximal', 'distal']) + + drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, location]) + drive = dict(type='Rhythmic', name=drive_title, + tstart=tstart, + tstart_std=tstart_std, + burst_rate=burst_rate, + burst_std=burst_std, + location=location + ) + elif drive_type['new'] == 'Poisson': + tstart = FloatText(value=7.5, description='Start time:', layout=layout) + tstop = FloatText(value=8.5, description='Stop time:', layout=layout) + rate_constant = FloatText(value=8.5, description='Rate constant:', layout=layout) + location = RadioButtons(options=['proximal', 'distal']) + weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout) + weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout) + + drive_box = VBox([tstart, tstop, rate_constant, location, + weights_ampa_L5Pyr, weights_nmda_L5Pyr]) + drive = dict(type='Poisson', name=drive_title, + tstart=tstart, + tstop=tstop, + rate_constant=rate_constant, + location=location, + weights_ampa_L5Pyr=weights_ampa_L5Pyr, + weights_nmda_L5Pyr=weights_nmda_L5Pyr + ) + elif drive_type['new'] == 'Evoked': + mu = FloatText(value=7.5, description='Mean time:', layout=layout) + sigma = FloatText(value=8.5, description='Std dev time:', layout=layout) + numspikes = FloatText(value=8.5, description='Number of spikes:', layout=layout) + location = RadioButtons(options=['proximal', 'distal']) + weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout) + weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout) + + drive_box = VBox([mu, sigma, numspikes, location, + weights_ampa_L5Pyr, weights_nmda_L5Pyr]) + drive = dict(type='Evoked', name=drive_title, + mu=mu, + sigma=sigma, + numspikes=numspikes, + sync_within_trial=False, + location=location, + weights_ampa_L5Pyr=weights_ampa_L5Pyr, + weights_nmda_L5Pyr=weights_nmda_L5Pyr, + space_constant=3.0) + + drive_titles.append(drive_title) + drive_boxes.append(drive_box) + drive_widgets.append(drive) + + accordion = Accordion(children=drive_boxes, + selected_index=len(drive_boxes) - 1) + for idx, this_title in enumerate(drive_titles): + accordion.set_title(idx, this_title) + display(accordion) + + +def update_plot(variables, plot_out, plot_type): + plot_out.clear_output() + + if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'): + return + + with plot_out: + if plot_type['new'] == 'spikes': + variables['net'].cell_response.plot_spikes_raster() + elif plot_type['new'] == 'dipole current': + variables['dpls'][0].plot() + elif plot_type['new'] == 'input histogram': + variables['net'].cell_response.plot_spikes_hist() + + +def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): + """Run the simulation and plot outputs.""" + with log_out: + for drive in drive_widgets: + if drive['type'] == 'Poisson': + variables['net'].add_poisson_drive( + name=drive['name'], + tstart=drive['tstart'].value, + tstop=drive['tstop'].value, + rate_constant=dict(L5_pyramidal=drive['rate_constant'].value), + location=drive['location'].value, + weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), + weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), + space_constant=100.0 + ) + elif drive['type'] == 'Evoked': + variables['net'].add_evoked_drive( + name=drive['name'], + mu=drive['mu'].value, + sigma=drive['sigma'].value, + numspikes=drive['numspikes'].value, + sync_within_trial=False, + location=drive['location'].value, + weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), + weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), + space_constant=3.0 + ) + elif drive['type'] == 'Rhythmic': + variables['net'].add_bursty_drive( + name=drive['name'], + tstart=drive['tstart'].value, + tstart_std=drive['tstart_std'].value, + burst_rate=drive['burst_rate'].value, + burst_std=drive['burst_std'].value, + location=drive['location'].value + ) + variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) + with plot_out: + variables['dpls'][0].plot() + + +def run_hnn_gui(): + + hnn_core_root = op.join(op.dirname(hnn_core.__file__)) + + params_fname = op.join(hnn_core_root, 'param', 'default.json') + params = read_params(params_fname) + + variables = dict(net=None, dpls=None) + variables['net'] = Network(params, add_drives_from_params=False) + + # Output windows + log_out = Output(layout={'border': '1px solid gray'}) + plot_out = Output(layout={'border': '1px solid gray'}) + + # header_button + header_button = create_expanded_button('Human Neocortical Neurosolver', + 'success') + + # Sliders to change local-connectivity Params + sliders = [_get_sliders(params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', + 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), + _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa', + 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa', + 'gbar_L5Basket_L5Pyr_gabab']), + _get_sliders(params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), + _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] + + # accordians + boxes = [VBox(slider) for slider in sliders] + titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas'] + accordian = Accordion(children=boxes) + for idx, title in enumerate(titles): + accordian.set_title(idx, title) + + # Dropdown for different drives + layout = Layout(width='200px', height='auto') + drives_dropdown = Dropdown( + options=['Evoked', 'Poisson', 'Rhythmic'], + value='Evoked', + description='Drive:', + disabled=False, + layout=layout + ) + + # XXX: should be simpler to use Stacked class starting + # from IPywidgets > 8.0 + interactive(add_drive_widget, drive_type='Evoked') + drives_dropdown.observe(add_drive_widget, 'value') + drives_options = VBox([drives_dropdown, drives_out]) + + # Tabs for left pane + left_tab = Tab() + left_tab.children = [accordian, drives_options] + titles = ['Cell connectivity', 'Drives'] + for idx, title in enumerate(titles): + left_tab.set_title(idx, title) + + # Dropdown menu to switch between plots + dropdown = Dropdown( + options=['input histogram', 'dipole current', 'spikes'], + value='dipole current', + description='Plot:', + disabled=False, + ) + + interactive(update_plot, plot_type='dipole current', + variables=fixed(variables), plot_out=fixed(plot_out)) + dropdown.observe(lambda plot_type: update_plot(variables, plot_out, plot_type), 'value') + + # Run button + run_button = create_expanded_button('Run', 'success') + load_button = create_expanded_button('Load parameters', 'success') + + run_button.on_click(lambda b: on_button_clicked(log_out, plot_out, drive_widgets, variables, b)) + + footer = HBox([run_button, load_button, dropdown]) + + # Final layout of the app + hnn_gui = AppLayout(header=header_button, + left_sidebar=left_tab, + center=log_out, + right_sidebar=plot_out, + footer=footer, + pane_widths=['380px', 1, 1], + pane_heights=[1, '500px', 1]) + return hnn_gui diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 8dbc2b8c5..26383499d 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -4,332 +4,27 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], - "source": [ - "import os.path as op\n", - "\n", - "import hnn_core\n", - "from hnn_core import simulate_dipole, read_params, Network\n", - "\n", - "hnn_core_root = op.join(op.dirname(hnn_core.__file__))\n", - "\n", - "params_fname = op.join(hnn_core_root, 'param', 'default.json')\n", - "params = read_params(params_fname)\n", - "\n", - "variables = dict(net=None, dpls=None)\n", - "variables['net'] = Network(params, add_drives_from_params=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# HNN Banner\n", - "from ipywidgets import Button, Layout\n", - "\n", - "def create_expanded_button(description, button_style):\n", - " return Button(description=description, button_style=button_style, layout=Layout(height='10', width='auto'))\n", - "\n", - "header_button = create_expanded_button('Human Neocortical Neurosolver', 'success')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Sliders to change local-connectivity Params\n", - "from ipywidgets import (FloatSlider, Dropdown, Layout, Accordion, Tab, VBox,\n", - " interactive_output, interactive)\n", - "from functools import partial\n", - " \n", - "def update_params(**updates):\n", - " params.update(dict(**updates))\n", - " return params\n", - " \n", - "def _get_min(v):\n", - " if v < 0:\n", - " return v * 10\n", - " else:\n", - " return v * 0.1\n", - "\n", - "def _get_max(v):\n", - " if v > 0:\n", - " return v * 10\n", - " else:\n", - " return v * 0.1\n", - "\n", - "\n", - "def _get_sliders(params, param_keys):\n", - " \"\"\"Get sliders\"\"\"\n", - " style = {'description_width': 'initial'}\n", - " sliders = list()\n", - " for d in param_keys:\n", - " min_val = _get_min(params[d])\n", - " max_val = _get_max(params[d])\n", - " step = (max_val - min_val) / 10.\n", - " slider = FloatSlider(\n", - " value=params[d], min=min_val, max=max_val, step=step,\n", - " description=d,\n", - " disabled=False, continuous_update=False, orientation='horizontal',\n", - " readout=True, readout_format='.2e',\n", - " style=style)\n", - " sliders.append(slider)\n", - " \n", - " interactive_output(update_params, {s.description: s for s in sliders})\n", - " return sliders\n", - "\n", - "sliders = [_get_sliders(params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda',\n", - " 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']),\n", - " _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa',\n", - " 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa',\n", - " 'gbar_L5Basket_L5Pyr_gabab']),\n", - " _get_sliders(params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']),\n", - " _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])]\n", - "\n", - "# accordians\n", - "boxes = [VBox(slider) for slider in sliders]\n", - "titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas']\n", - "accordian = Accordion(children=boxes)\n", - "for idx, title in enumerate(titles):\n", - " accordian.set_title(idx, title)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# Dropdown for different drives\n", - "\n", - "from ipywidgets import FloatText, interactive, Dropdown, interact, Output, RadioButtons\n", - "from IPython.display import display\n", - "\n", - "layout = Layout(width='200px', height='auto')\n", - "\n", - "drives_dropdown = Dropdown(\n", - " options=['Evoked', 'Poisson', 'Rhythmic'],\n", - " value='Evoked',\n", - " description='Drive:',\n", - " disabled=False,\n", - " layout=layout\n", - ")\n", - "\n", - "drives_out = Output()\n", - "drive_widgets = list()\n", - "drive_titles = list()\n", - "drive_boxes = list()\n", - "\n", - "def add_drive_widget(drive_type):\n", - "\n", - " drives_out.clear_output()\n", - " with drives_out:\n", - " drive_title = drive_type['new'] + str(len(drive_boxes))\n", - "\n", - " if drive_type['new'] == 'Rhythmic':\n", - " tstart = FloatText(value=7.5, description='Start time:', layout=layout)\n", - " tstart_std = FloatText(value=7.5, description='Start time dev:', layout=layout)\n", - " tstop = FloatText(value=7.5, description='Stop time:', layout=layout)\n", - " burst_rate = FloatText(value=7.5, description='Burst rate:', layout=layout)\n", - " burst_std = FloatText(value=7.5, description='Burst std dev:', layout=layout)\n", - " location = RadioButtons(options=['proximal', 'distal'])\n", - "\n", - " drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, location])\n", - " drive = dict(type='Rhythmic', name=drive_title,\n", - " tstart=tstart,\n", - " tstart_std=tstart_std,\n", - " burst_rate=burst_rate,\n", - " burst_std=burst_std,\n", - " location=location\n", - " )\n", - " elif drive_type['new'] == 'Poisson': \n", - " tstart = FloatText(value=7.5, description='Start time:', layout=layout)\n", - " tstop = FloatText(value=8.5, description='Stop time:', layout=layout)\n", - " rate_constant = FloatText(value=8.5, description='Rate constant:', layout=layout)\n", - " location = RadioButtons(options=['proximal', 'distal'])\n", - " weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout)\n", - " weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout)\n", - " \n", - " drive_box = VBox([tstart, tstop, rate_constant, location,\n", - " weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", - " drive = dict(type='Poisson', name=drive_title,\n", - " tstart=tstart, \n", - " tstop=tstop,\n", - " rate_constant=rate_constant, \n", - " location=location, \n", - " weights_ampa_L5Pyr=weights_ampa_L5Pyr, \n", - " weights_nmda_L5Pyr=weights_nmda_L5Pyr\n", - " )\n", - " elif drive_type['new'] == 'Evoked': \n", - " mu = FloatText(value=7.5, description='Mean time:', layout=layout)\n", - " sigma = FloatText(value=8.5, description='Std dev time:', layout=layout)\n", - " numspikes = FloatText(value=8.5, description='Number of spikes:', layout=layout)\n", - " location = RadioButtons(options=['proximal', 'distal'])\n", - " weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout)\n", - " weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout)\n", - "\n", - " drive_box = VBox([mu, sigma, numspikes, location, weights_ampa_L5Pyr, weights_nmda_L5Pyr])\n", - " drive = dict(type='Evoked', name=drive_title,\n", - " mu=mu,\n", - " sigma=sigma, \n", - " numspikes=numspikes, \n", - " sync_within_trial=False, \n", - " location=location, \n", - " weights_ampa_L5Pyr=weights_ampa_L5Pyr, \n", - " weights_nmda_L5Pyr=weights_nmda_L5Pyr, \n", - " space_constant=3.0)\n", - "\n", - " drive_titles.append(drive_title)\n", - " drive_boxes.append(drive_box)\n", - " drive_widgets.append(drive)\n", - "\n", - " accordion = Accordion(children=drive_boxes,\n", - " selected_index=len(drive_boxes) - 1)\n", - " for idx, this_title in enumerate(drive_titles):\n", - " accordion.set_title(idx, this_title)\n", - " display(accordion)\n", - "\n", - "# XXX: should be simpler to use Stacked class starting from IPywidgets > 8.0\n", - "interactive(add_drive_widget, drive_type='Evoked')\n", - "drives_dropdown.observe(add_drive_widget, 'value')\n", - "drives_options = VBox([drives_dropdown, drives_out])" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# Tabs for left pane\n", - "left_tab = Tab()\n", - "left_tab.children = [accordian, drives_options]\n", - "titles = ['Cell connectivity', 'Drives']\n", - "for idx, title in enumerate(titles):\n", - " left_tab.set_title(idx, title)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import HBox, VBox\n", - "\n", - "# Dropdown menu to switch between plots\n", - "\n", - "def update_plot(plot_type):\n", - " plot_out.clear_output()\n", - "\n", - " if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'):\n", - " return\n", - " \n", - " with plot_out:\n", - " if plot_type['new'] == 'spikes':\n", - " variables['net'].cell_response.plot_spikes_raster()\n", - " elif plot_type['new'] == 'dipole current':\n", - " variables['dpls'][0].plot()\n", - " elif plot_type['new'] == 'input histogram':\n", - " variables['net'].cell_response.plot_spikes_hist()\n", - "\n", - "\n", - "dropdown = Dropdown(\n", - " options=['input histogram', 'dipole current', 'spikes'],\n", - " value='dipole current',\n", - " description='Plot:',\n", - " disabled=False,\n", - ")\n", - "\n", - "interactive(update_plot, plot_type='dipole current')\n", - "dropdown.observe(update_plot, 'value')" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Run button\n", - "from ipywidgets import Output\n", - "\n", - "log_out = Output(layout={'border': '1px solid gray'})\n", - "plot_out = Output(layout={'border': '1px solid gray'})\n", - "\n", - "\n", - "def on_button_clicked(b, variables):\n", - " \"\"\"Run the simulation and plot outputs.\"\"\"\n", - " with log_out:\n", - " for drive in drive_widgets:\n", - " if drive['type'] == 'Poisson':\n", - " variables['net'].add_poisson_drive(\n", - " name=drive['name'],\n", - " tstart=drive['tstart'].value, \n", - " tstop=drive['tstop'].value,\n", - " rate_constant=dict(L5_pyramidal=drive['rate_constant'].value), \n", - " location=drive['location'].value,\n", - " weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), \n", - " weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), \n", - " space_constant=100.0\n", - " )\n", - " elif drive['type'] == 'Evoked':\n", - " variables['net'].add_evoked_drive(\n", - " name=drive['name'],\n", - " mu=drive['mu'].value,\n", - " sigma=drive['sigma'].value, \n", - " numspikes=drive['numspikes'].value, \n", - " sync_within_trial=False, \n", - " location=drive['location'].value, \n", - " weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), \n", - " weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), \n", - " space_constant=3.0\n", - " )\n", - " elif drive['type'] == 'Rhythmic':\n", - " variables['net'].add_bursty_drive(\n", - " name=drive['name'],\n", - " tstart=drive['tstart'].value,\n", - " tstart_std=drive['tstart_std'].value,\n", - " burst_rate=drive['burst_rate'].value,\n", - " burst_std=drive['burst_std'].value,\n", - " location=drive['location'].value\n", - " )\n", - " variables['dpls'] = simulate_dipole(variables['net'], n_trials=1)\n", - " with plot_out:\n", - " variables['dpls'][0].plot()\n", - "\n", - "run_button = create_expanded_button('Run', 'success')\n", - "load_button = create_expanded_button('Load parameters', 'success')\n", - "\n", - "run_button.on_click(partial(on_button_clicked, variables=variables))\n", - "\n", - "footer = HBox([run_button, load_button, dropdown])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "slide" + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "69202445fbc44aac92c1521d109ef6bf", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "AppLayout(children=(Button(button_style='success', description='Human Neocortical Neurosolver', layout=Layout(…" + ] + }, + "metadata": {}, + "output_type": "display_data" } - }, - "outputs": [], + ], "source": [ - "# Final layout of the app\n", - "from ipywidgets import AppLayout\n", + "from hnn_core.gui import run_hnn_gui\n", + "from IPython.display import display\n", "\n", - "AppLayout(header=header_button,\n", - " left_sidebar=left_tab,\n", - " center=log_out,\n", - " right_sidebar=plot_out,\n", - " footer=footer,\n", - " pane_widths=['380px', 1, 1],\n", - " pane_heights=[1, '500px', 1])" + "display(run_hnn_gui())" ] } ], From 562a2bb09fbb13c172b8e2054427e78eb632edf2 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 00:05:36 -0400 Subject: [PATCH 10/88] Pep8 --- hnn_core/gui.py | 102 +++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index e77bb2263..4c45e8011 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -68,53 +68,71 @@ def _get_sliders(params, param_keys): def add_drive_widget(drive_type): - + """Add a widget for a new drive.""" layout = Layout(width='200px', height='auto') drives_out.clear_output() with drives_out: drive_title = drive_type['new'] + str(len(drive_boxes)) if drive_type['new'] == 'Rhythmic': - tstart = FloatText(value=7.5, description='Start time:', layout=layout) - tstart_std = FloatText(value=7.5, description='Start time dev:', layout=layout) - tstop = FloatText(value=7.5, description='Stop time:', layout=layout) - burst_rate = FloatText(value=7.5, description='Burst rate:', layout=layout) - burst_std = FloatText(value=7.5, description='Burst std dev:', layout=layout) + tstart = FloatText(value=7.5, description='Start time:', + layout=layout) + tstart_std = FloatText(value=7.5, description='Start time dev:', + layout=layout) + tstop = FloatText(value=7.5, description='Stop time:', + layout=layout) + burst_rate = FloatText(value=7.5, description='Burst rate:', + layout=layout) + burst_std = FloatText(value=7.5, description='Burst std dev:', + layout=layout) location = RadioButtons(options=['proximal', 'distal']) - drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, location]) + drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, + location]) drive = dict(type='Rhythmic', name=drive_title, tstart=tstart, tstart_std=tstart_std, burst_rate=burst_rate, burst_std=burst_std, - location=location - ) - elif drive_type['new'] == 'Poisson': - tstart = FloatText(value=7.5, description='Start time:', layout=layout) - tstop = FloatText(value=8.5, description='Stop time:', layout=layout) - rate_constant = FloatText(value=8.5, description='Rate constant:', layout=layout) + location=location) + elif drive_type['new'] == 'Poisson': + tstart = FloatText(value=7.5, description='Start time:', + layout=layout) + tstop = FloatText(value=8.5, description='Stop time:', + layout=layout) + rate_constant = FloatText(value=8.5, description='Rate constant:', + layout=layout) location = RadioButtons(options=['proximal', 'distal']) - weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout) - weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout) - + weights_ampa_L5Pyr = FloatText(value=8.5, + description='AMPA (L5 Pyr):', + layout=layout) + weights_nmda_L5Pyr = FloatText(value=8.5, + description='NMDA (L5 Pyr):', + layout=layout) + drive_box = VBox([tstart, tstop, rate_constant, location, weights_ampa_L5Pyr, weights_nmda_L5Pyr]) drive = dict(type='Poisson', name=drive_title, - tstart=tstart, + tstart=tstart, tstop=tstop, - rate_constant=rate_constant, - location=location, - weights_ampa_L5Pyr=weights_ampa_L5Pyr, - weights_nmda_L5Pyr=weights_nmda_L5Pyr - ) + rate_constant=rate_constant, + location=location, + weights_ampa_L5Pyr=weights_ampa_L5Pyr, + weights_nmda_L5Pyr=weights_nmda_L5Pyr) elif drive_type['new'] == 'Evoked': - mu = FloatText(value=7.5, description='Mean time:', layout=layout) - sigma = FloatText(value=8.5, description='Std dev time:', layout=layout) - numspikes = FloatText(value=8.5, description='Number of spikes:', layout=layout) + mu = FloatText(value=7.5, description='Mean time:', + layout=layout) + sigma = FloatText(value=8.5, description='Std dev time:', + layout=layout) + numspikes = FloatText(value=8.5, description='Number of spikes:', + layout=layout) location = RadioButtons(options=['proximal', 'distal']) - weights_ampa_L5Pyr = FloatText(value=8.5, description='AMPA (L5 Pyr):', layout=layout) - weights_nmda_L5Pyr = FloatText(value=8.5, description='NMDA (L5 Pyr):', layout=layout) + weights_ampa_L5Pyr = FloatText(value=8.5, + description='AMPA (L5 Pyr):', + layout=layout) + weights_nmda_L5Pyr = FloatText(value=8.5, + description='NMDA (L5 Pyr):', + layout=layout) drive_box = VBox([mu, sigma, numspikes, location, weights_ampa_L5Pyr, weights_nmda_L5Pyr]) @@ -214,13 +232,17 @@ def run_hnn_gui(): 'success') # Sliders to change local-connectivity Params - sliders = [_get_sliders(params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', - 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), - _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa', - 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa', - 'gbar_L5Basket_L5Pyr_gabab']), - _get_sliders(params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), - _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] + sliders = [_get_sliders(params, + ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', + 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), + _get_sliders(params, + ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', + 'gbar_L5Pyr_L5Pyr_ampa', 'gbar_L5Pyr_L5Pyr_nmda', + 'gbar_L5Basket_L5Pyr_gabaa', 'gbar_L5Basket_L5Pyr_gabab']), + _get_sliders(params, + ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), + _get_sliders(params, + ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] # accordians boxes = [VBox(slider) for slider in sliders] @@ -262,13 +284,21 @@ def run_hnn_gui(): interactive(update_plot, plot_type='dipole current', variables=fixed(variables), plot_out=fixed(plot_out)) - dropdown.observe(lambda plot_type: update_plot(variables, plot_out, plot_type), 'value') + + def _update_plot(plot_type): + return update_plot(variables, plot_out, plot_type) + + dropdown.observe(_update_plot, 'value') # Run button run_button = create_expanded_button('Run', 'success') load_button = create_expanded_button('Load parameters', 'success') - run_button.on_click(lambda b: on_button_clicked(log_out, plot_out, drive_widgets, variables, b)) + def _on_button_clicked(b): + return on_button_clicked(log_out, plot_out, drive_widgets, variables, + b) + + run_button.on_click(_on_button_clicked) footer = HBox([run_button, load_button, dropdown]) From 99ae33d1b4100ac6034219accc4c59c64e6529f5 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 00:26:53 -0400 Subject: [PATCH 11/88] MAINT: Flat is better than nested --- hnn_core/gui.py | 104 +++++++++++++++++++++++------------------------ hnn_widget.ipynb | 4 +- 2 files changed, 52 insertions(+), 56 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 4c45e8011..a5b14e425 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -9,12 +9,11 @@ from IPython.display import display -from ipywidgets import (FloatSlider, Dropdown, Accordion, Tab, - interactive_output, HBox, VBox, - FloatText, interactive, Dropdown, interact, Output, - RadioButtons, Button) -from ipywidgets import Layout, AppLayout, fixed - +from ipywidgets import (FloatSlider, Dropdown, Button, RadioButtons, + fixed, interactive_output, interactive, interact, + FloatText, Output, + HBox, VBox, Tab, Accordion, + Layout, AppLayout) drives_out = Output() drive_widgets = list() @@ -119,7 +118,7 @@ def add_drive_widget(drive_type): location=location, weights_ampa_L5Pyr=weights_ampa_L5Pyr, weights_nmda_L5Pyr=weights_nmda_L5Pyr) - elif drive_type['new'] == 'Evoked': + elif drive_type['new'] == 'Evoked': mu = FloatText(value=7.5, description='Mean time:', layout=layout) sigma = FloatText(value=8.5, description='Std dev time:', @@ -138,12 +137,12 @@ def add_drive_widget(drive_type): weights_ampa_L5Pyr, weights_nmda_L5Pyr]) drive = dict(type='Evoked', name=drive_title, mu=mu, - sigma=sigma, - numspikes=numspikes, - sync_within_trial=False, - location=location, - weights_ampa_L5Pyr=weights_ampa_L5Pyr, - weights_nmda_L5Pyr=weights_nmda_L5Pyr, + sigma=sigma, + numspikes=numspikes, + sync_within_trial=False, + location=location, + weights_ampa_L5Pyr=weights_ampa_L5Pyr, + weights_nmda_L5Pyr=weights_nmda_L5Pyr, space_constant=3.0) drive_titles.append(drive_title) @@ -174,46 +173,47 @@ def update_plot(variables, plot_out, plot_type): def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): """Run the simulation and plot outputs.""" + for drive in drive_widgets: + if drive['type'] == 'Poisson': + variables['net'].add_poisson_drive( + name=drive['name'], + tstart=drive['tstart'].value, + tstop=drive['tstop'].value, + rate_constant=dict(L5_pyramidal=drive['rate_constant'].value), + location=drive['location'].value, + weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), + weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), + space_constant=100.0 + ) + elif drive['type'] == 'Evoked': + variables['net'].add_evoked_drive( + name=drive['name'], + mu=drive['mu'].value, + sigma=drive['sigma'].value, + numspikes=drive['numspikes'].value, + sync_within_trial=False, + location=drive['location'].value, + weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), + weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), + space_constant=3.0 + ) + elif drive['type'] == 'Rhythmic': + variables['net'].add_bursty_drive( + name=drive['name'], + tstart=drive['tstart'].value, + tstart_std=drive['tstart_std'].value, + burst_rate=drive['burst_rate'].value, + burst_std=drive['burst_std'].value, + location=drive['location'].value + ) with log_out: - for drive in drive_widgets: - if drive['type'] == 'Poisson': - variables['net'].add_poisson_drive( - name=drive['name'], - tstart=drive['tstart'].value, - tstop=drive['tstop'].value, - rate_constant=dict(L5_pyramidal=drive['rate_constant'].value), - location=drive['location'].value, - weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), - weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), - space_constant=100.0 - ) - elif drive['type'] == 'Evoked': - variables['net'].add_evoked_drive( - name=drive['name'], - mu=drive['mu'].value, - sigma=drive['sigma'].value, - numspikes=drive['numspikes'].value, - sync_within_trial=False, - location=drive['location'].value, - weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), - weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), - space_constant=3.0 - ) - elif drive['type'] == 'Rhythmic': - variables['net'].add_bursty_drive( - name=drive['name'], - tstart=drive['tstart'].value, - tstart_std=drive['tstart_std'].value, - burst_rate=drive['burst_rate'].value, - burst_std=drive['burst_std'].value, - location=drive['location'].value - ) variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) with plot_out: variables['dpls'][0].plot() def run_hnn_gui(): + """Create the HNN GUI.""" hnn_core_root = op.join(op.dirname(hnn_core.__file__)) @@ -275,19 +275,16 @@ def run_hnn_gui(): left_tab.set_title(idx, title) # Dropdown menu to switch between plots + def _update_plot(plot_type): + return update_plot(variables, plot_out, plot_type) + dropdown = Dropdown( options=['input histogram', 'dipole current', 'spikes'], value='dipole current', description='Plot:', disabled=False, ) - - interactive(update_plot, plot_type='dipole current', - variables=fixed(variables), plot_out=fixed(plot_out)) - - def _update_plot(plot_type): - return update_plot(variables, plot_out, plot_type) - + interactive(_update_plot, plot_type='dipole current') dropdown.observe(_update_plot, 'value') # Run button @@ -299,7 +296,6 @@ def _on_button_clicked(b): b) run_button.on_click(_on_button_clicked) - footer = HBox([run_button, load_button, dropdown]) # Final layout of the app diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 26383499d..74538ef41 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -8,7 +8,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "69202445fbc44aac92c1521d109ef6bf", + "model_id": "0420f75f7a5a4ae3a2305cf9a38e24e8", "version_major": 2, "version_minor": 0 }, @@ -45,7 +45,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.8.8" } }, "nbformat": 4, From e4bf713499ef6537c79d1ef32cd53dec818721c4 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 09:48:41 -0400 Subject: [PATCH 12/88] ENH: spectogram option but doesn't quite work --- hnn_core/gui.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index a5b14e425..e33f1e59c 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -4,6 +4,8 @@ import os.path as op from functools import partial, update_wrapper +import numpy as np + import hnn_core from hnn_core import simulate_dipole, read_params, Network @@ -165,10 +167,13 @@ def update_plot(variables, plot_out, plot_type): with plot_out: if plot_type['new'] == 'spikes': variables['net'].cell_response.plot_spikes_raster() - elif plot_type['new'] == 'dipole current': + elif plot_type['new'] == 'current dipole': variables['dpls'][0].plot() elif plot_type['new'] == 'input histogram': variables['net'].cell_response.plot_spikes_hist() + elif plot_type['new'] == 'spectogram': + freqs = np.arange(20., 100., 1.) + variables['dpls'][0].plot_tfr_morlet(freqs) def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): @@ -279,12 +284,12 @@ def _update_plot(plot_type): return update_plot(variables, plot_out, plot_type) dropdown = Dropdown( - options=['input histogram', 'dipole current', 'spikes'], - value='dipole current', + options=['input histogram', 'current dipole', 'spikes', 'spectogram'], + value='current dipole', description='Plot:', disabled=False, ) - interactive(_update_plot, plot_type='dipole current') + interactive(_update_plot, plot_type='current dipole') dropdown.observe(_update_plot, 'value') # Run button From 8d8918252e8d192e081daf6f6e0f61f67c27f1f3 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 10:05:11 -0400 Subject: [PATCH 13/88] Add empty option in adding drives --- hnn_core/gui.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index e33f1e59c..d96eaaae6 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -147,9 +147,10 @@ def add_drive_widget(drive_type): weights_nmda_L5Pyr=weights_nmda_L5Pyr, space_constant=3.0) - drive_titles.append(drive_title) - drive_boxes.append(drive_box) - drive_widgets.append(drive) + if drive_type['new'] in ['Evoked', 'Poisson', 'Rhythmic']: + drive_titles.append(drive_title) + drive_boxes.append(drive_box) + drive_widgets.append(drive) accordion = Accordion(children=drive_boxes, selected_index=len(drive_boxes) - 1) @@ -195,9 +196,9 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): name=drive['name'], mu=drive['mu'].value, sigma=drive['sigma'].value, - numspikes=drive['numspikes'].value, - sync_within_trial=False, - location=drive['location'].value, + numspikes=drive['numspikes'].value, + sync_within_trial=False, + location=drive['location'].value, weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), space_constant=3.0 @@ -259,8 +260,8 @@ def run_hnn_gui(): # Dropdown for different drives layout = Layout(width='200px', height='auto') drives_dropdown = Dropdown( - options=['Evoked', 'Poisson', 'Rhythmic'], - value='Evoked', + options=['Evoked', 'Poisson', 'Rhythmic', ''], + value='', description='Drive:', disabled=False, layout=layout From 9e7c1f2bf295b860bdd83917f2c308ab202f00e0 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 10:25:10 -0400 Subject: [PATCH 14/88] DOC: improve layout --- hnn_core/gui.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index d96eaaae6..a90bbef50 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -23,9 +23,9 @@ drive_boxes = list() -def create_expanded_button(description, button_style): +def create_expanded_button(description, button_style, height): return Button(description=description, button_style=button_style, - layout=Layout(height='10', width='auto')) + layout=Layout(height=height, width='auto')) def update_params(params, **updates): @@ -230,12 +230,13 @@ def run_hnn_gui(): variables['net'] = Network(params, add_drives_from_params=False) # Output windows - log_out = Output(layout={'border': '1px solid gray'}) - plot_out = Output(layout={'border': '1px solid gray'}) + log_out = Output(layout={'border': '1px solid gray', 'height': '150px', + 'overflow_y': 'auto'}) + plot_out = Output(layout={'border': '1px solid gray', 'height': '350px'}) # header_button header_button = create_expanded_button('Human Neocortical Neurosolver', - 'success') + 'success', '20') # Sliders to change local-connectivity Params sliders = [_get_sliders(params, @@ -294,8 +295,8 @@ def _update_plot(plot_type): dropdown.observe(_update_plot, 'value') # Run button - run_button = create_expanded_button('Run', 'success') - load_button = create_expanded_button('Load parameters', 'success') + run_button = create_expanded_button('Run', 'success', '15') + load_button = create_expanded_button('Load parameters', 'success', '15') def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, @@ -304,11 +305,12 @@ def _on_button_clicked(b): run_button.on_click(_on_button_clicked) footer = HBox([run_button, load_button, dropdown]) + right_sidebar = VBox([plot_out, log_out]) + # Final layout of the app hnn_gui = AppLayout(header=header_button, left_sidebar=left_tab, - center=log_out, - right_sidebar=plot_out, + right_sidebar=right_sidebar, footer=footer, pane_widths=['380px', 1, 1], pane_heights=[1, '500px', 1]) From f59dd8f7800a5aaeb5d21d7c8dc2091842142d64 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 19:19:56 -0400 Subject: [PATCH 15/88] ENH: add different cell types to Poisson drive --- hnn_core/gui.py | 76 ++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index a90bbef50..0c4ed2386 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -70,22 +70,25 @@ def _get_sliders(params, param_keys): def add_drive_widget(drive_type): """Add a widget for a new drive.""" - layout = Layout(width='200px', height='auto') + layout = Layout(width='280px', height='auto') + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', + 'L2_basket'] + style = {'description_width': '150px'} drives_out.clear_output() with drives_out: drive_title = drive_type['new'] + str(len(drive_boxes)) if drive_type['new'] == 'Rhythmic': tstart = FloatText(value=7.5, description='Start time:', - layout=layout) + layout=layout, style=style) tstart_std = FloatText(value=7.5, description='Start time dev:', - layout=layout) + layout=layout, style=style) tstop = FloatText(value=7.5, description='Stop time:', - layout=layout) + layout=layout, style=style) burst_rate = FloatText(value=7.5, description='Burst rate:', - layout=layout) + layout=layout, style=style) burst_std = FloatText(value=7.5, description='Burst std dev:', - layout=layout) + layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, @@ -97,29 +100,36 @@ def add_drive_widget(drive_type): burst_std=burst_std, location=location) elif drive_type['new'] == 'Poisson': - tstart = FloatText(value=7.5, description='Start time:', - layout=layout) + tstart = FloatText(value=0.0, description='Start time:', + layout=layout, style=style) tstop = FloatText(value=8.5, description='Stop time:', - layout=layout) - rate_constant = FloatText(value=8.5, description='Rate constant:', - layout=layout) + layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) - weights_ampa_L5Pyr = FloatText(value=8.5, - description='AMPA (L5 Pyr):', - layout=layout) - weights_nmda_L5Pyr = FloatText(value=8.5, - description='NMDA (L5 Pyr):', - layout=layout) - - drive_box = VBox([tstart, tstop, rate_constant, location, - weights_ampa_L5Pyr, weights_nmda_L5Pyr]) + weights_ampa = dict() + weights_nmda = dict() + rate_constant = dict() + for cell_type in cell_types: + rate_constant[f'{cell_type}'] = FloatText( + value=8.5, description=f'Rate constant ({cell_type}):', + layout=layout, style=style) + weights_ampa[f'{cell_type}'] = FloatText( + value=0., description=f'AMPA ({cell_type}):', + layout=layout, style=style) + weights_nmda[f'{cell_type}'] = FloatText( + value=0., description=f'NMDA ({cell_type}):', layout=layout, + style=style) + + drive_box = VBox([tstart, tstop, location] + + list(rate_constant.values()) + + list(weights_ampa.values()) + + list(weights_nmda.values())) drive = dict(type='Poisson', name=drive_title, tstart=tstart, tstop=tstop, rate_constant=rate_constant, location=location, - weights_ampa_L5Pyr=weights_ampa_L5Pyr, - weights_nmda_L5Pyr=weights_nmda_L5Pyr) + weights_ampa=weights_ampa, + weights_nmda=weights_nmda) elif drive_type['new'] == 'Evoked': mu = FloatText(value=7.5, description='Mean time:', layout=layout) @@ -159,7 +169,7 @@ def add_drive_widget(drive_type): display(accordion) -def update_plot(variables, plot_out, plot_type): +def update_plot_window(variables, plot_out, plot_type): plot_out.clear_output() if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'): @@ -181,14 +191,20 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): """Run the simulation and plot outputs.""" for drive in drive_widgets: if drive['type'] == 'Poisson': + weights_ampa = {k: v.value for k, v in + drive['weights_ampa'].items()} + weights_nmda = {k: v.value for k, v in + drive['weights_nmda'].items()} + rate_constant = {k: v.value for k, v in + drive['rate_constant'].items()} variables['net'].add_poisson_drive( name=drive['name'], tstart=drive['tstart'].value, tstop=drive['tstop'].value, - rate_constant=dict(L5_pyramidal=drive['rate_constant'].value), + rate_constant=rate_constant, location=drive['location'].value, - weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), - weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, space_constant=100.0 ) elif drive['type'] == 'Evoked': @@ -282,8 +298,8 @@ def run_hnn_gui(): left_tab.set_title(idx, title) # Dropdown menu to switch between plots - def _update_plot(plot_type): - return update_plot(variables, plot_out, plot_type) + def _update_plot_window(plot_type): + return update_plot_window(variables, plot_out, plot_type) dropdown = Dropdown( options=['input histogram', 'current dipole', 'spikes', 'spectogram'], @@ -291,8 +307,8 @@ def _update_plot(plot_type): description='Plot:', disabled=False, ) - interactive(_update_plot, plot_type='current dipole') - dropdown.observe(_update_plot, 'value') + interactive(_update_plot_window, plot_type='current dipole') + dropdown.observe(_update_plot_window, 'value') # Run button run_button = create_expanded_button('Run', 'success', '15') From 9aad320b3a3f6001dcea28523e85c43be3b64ac5 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 19:35:40 -0400 Subject: [PATCH 16/88] MAINT: refactor for readability --- hnn_core/gui.py | 160 +++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 77 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 0c4ed2386..f4cf66ec7 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -68,94 +68,100 @@ def _get_sliders(params, param_keys): return sliders +def _get_rhythmic_widget(drive_title, layout, style): + tstart = FloatText(value=7.5, description='Start time:', + layout=layout, style=style) + tstart_std = FloatText(value=7.5, description='Start time dev:', + layout=layout, style=style) + tstop = FloatText(value=7.5, description='Stop time:', + layout=layout, style=style) + burst_rate = FloatText(value=7.5, description='Burst rate:', + layout=layout, style=style) + burst_std = FloatText(value=7.5, description='Burst std dev:', + layout=layout, style=style) + location = RadioButtons(options=['proximal', 'distal']) + + drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, + location]) + drive = dict(type='Rhythmic', name=drive_title, + tstart=tstart, tstart_std=tstart_std, + burst_rate=burst_rate, burst_std=burst_std, + location=location) + return drive, drive_box + + +def _get_poisson_widget(drive_title, layout, style): + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', + 'L2_basket'] + tstart = FloatText(value=0.0, description='Start time:', + layout=layout, style=style) + tstop = FloatText(value=8.5, description='Stop time:', + layout=layout, style=style) + location = RadioButtons(options=['proximal', 'distal']) + weights_ampa, weights_nmda = dict(), dict() + rate_constant = dict() + for cell_type in cell_types: + rate_constant[f'{cell_type}'] = FloatText( + value=8.5, description=f'Rate constant ({cell_type}):', + layout=layout, style=style) + weights_ampa[f'{cell_type}'] = FloatText( + value=0., description=f'AMPA ({cell_type}):', + layout=layout, style=style) + weights_nmda[f'{cell_type}'] = FloatText( + value=0., description=f'NMDA ({cell_type}):', layout=layout, + style=style) + + drive_box = VBox([tstart, tstop, location] + + list(rate_constant.values()) + + list(weights_ampa.values()) + + list(weights_nmda.values())) + drive = dict(type='Poisson', name=drive_title, tstart=tstart, + tstop=tstop, rate_constant=rate_constant, + location=location, weights_ampa=weights_ampa, + weights_nmda=weights_nmda) + return drive, drive_box + + +def _get_evoked_widget(drive_title, layout, style): + mu = FloatText(value=7.5, description='Mean time:', + layout=layout) + sigma = FloatText(value=8.5, description='Std dev time:', + layout=layout) + numspikes = FloatText(value=8.5, description='Number of spikes:', + layout=layout) + location = RadioButtons(options=['proximal', 'distal']) + weights_ampa_L5Pyr = FloatText(value=8.5, + description='AMPA (L5 Pyr):', + layout=layout) + weights_nmda_L5Pyr = FloatText(value=8.5, + description='NMDA (L5 Pyr):', + layout=layout) + + drive_box = VBox([mu, sigma, numspikes, location, + weights_ampa_L5Pyr, weights_nmda_L5Pyr]) + drive = dict(type='Evoked', name=drive_title, + mu=mu, sigma=sigma, numspikes=numspikes, + sync_within_trial=False, location=location, + weights_ampa_L5Pyr=weights_ampa_L5Pyr, + weights_nmda_L5Pyr=weights_nmda_L5Pyr, + space_constant=3.0) + return drive, drive_box + + def add_drive_widget(drive_type): """Add a widget for a new drive.""" layout = Layout(width='280px', height='auto') - cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', - 'L2_basket'] style = {'description_width': '150px'} drives_out.clear_output() with drives_out: drive_title = drive_type['new'] + str(len(drive_boxes)) if drive_type['new'] == 'Rhythmic': - tstart = FloatText(value=7.5, description='Start time:', - layout=layout, style=style) - tstart_std = FloatText(value=7.5, description='Start time dev:', - layout=layout, style=style) - tstop = FloatText(value=7.5, description='Stop time:', - layout=layout, style=style) - burst_rate = FloatText(value=7.5, description='Burst rate:', - layout=layout, style=style) - burst_std = FloatText(value=7.5, description='Burst std dev:', - layout=layout, style=style) - location = RadioButtons(options=['proximal', 'distal']) - - drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, - location]) - drive = dict(type='Rhythmic', name=drive_title, - tstart=tstart, - tstart_std=tstart_std, - burst_rate=burst_rate, - burst_std=burst_std, - location=location) + drive, drive_box = _get_rhythmic_widget(drive_title, layout, style) elif drive_type['new'] == 'Poisson': - tstart = FloatText(value=0.0, description='Start time:', - layout=layout, style=style) - tstop = FloatText(value=8.5, description='Stop time:', - layout=layout, style=style) - location = RadioButtons(options=['proximal', 'distal']) - weights_ampa = dict() - weights_nmda = dict() - rate_constant = dict() - for cell_type in cell_types: - rate_constant[f'{cell_type}'] = FloatText( - value=8.5, description=f'Rate constant ({cell_type}):', - layout=layout, style=style) - weights_ampa[f'{cell_type}'] = FloatText( - value=0., description=f'AMPA ({cell_type}):', - layout=layout, style=style) - weights_nmda[f'{cell_type}'] = FloatText( - value=0., description=f'NMDA ({cell_type}):', layout=layout, - style=style) - - drive_box = VBox([tstart, tstop, location] + - list(rate_constant.values()) + - list(weights_ampa.values()) + - list(weights_nmda.values())) - drive = dict(type='Poisson', name=drive_title, - tstart=tstart, - tstop=tstop, - rate_constant=rate_constant, - location=location, - weights_ampa=weights_ampa, - weights_nmda=weights_nmda) + drive, drive_box = _get_poisson_widget(drive_title, layout, style) elif drive_type['new'] == 'Evoked': - mu = FloatText(value=7.5, description='Mean time:', - layout=layout) - sigma = FloatText(value=8.5, description='Std dev time:', - layout=layout) - numspikes = FloatText(value=8.5, description='Number of spikes:', - layout=layout) - location = RadioButtons(options=['proximal', 'distal']) - weights_ampa_L5Pyr = FloatText(value=8.5, - description='AMPA (L5 Pyr):', - layout=layout) - weights_nmda_L5Pyr = FloatText(value=8.5, - description='NMDA (L5 Pyr):', - layout=layout) - - drive_box = VBox([mu, sigma, numspikes, location, - weights_ampa_L5Pyr, weights_nmda_L5Pyr]) - drive = dict(type='Evoked', name=drive_title, - mu=mu, - sigma=sigma, - numspikes=numspikes, - sync_within_trial=False, - location=location, - weights_ampa_L5Pyr=weights_ampa_L5Pyr, - weights_nmda_L5Pyr=weights_nmda_L5Pyr, - space_constant=3.0) + drive, drive_box = _get_evoked_widget(drive_title, layout, style) if drive_type['new'] in ['Evoked', 'Poisson', 'Rhythmic']: drive_titles.append(drive_title) From 7b6a5f2ca940ef8f51eed14c6e265a74521aaa9a Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 23:11:51 -0400 Subject: [PATCH 17/88] STY: better styling and color --- hnn_core/gui.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index f4cf66ec7..cfa7dadce 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -24,8 +24,10 @@ def create_expanded_button(description, button_style, height): + style = {'button_color': '#8A2BE2', 'font_size': height} return Button(description=description, button_style=button_style, - layout=Layout(height=height, width='auto')) + layout=Layout(height=height, width='auto'), + style=style) def update_params(params, **updates): @@ -35,16 +37,16 @@ def update_params(params, **updates): def _get_min(v): if v < 0: - return v * 10 + return v * 1.5 else: - return v * 0.1 + return v * 0.5 def _get_max(v): if v > 0: - return v * 10 + return v * 1.5 else: - return v * 0.1 + return v * 0.5 def _get_sliders(params, param_keys): @@ -257,8 +259,8 @@ def run_hnn_gui(): plot_out = Output(layout={'border': '1px solid gray', 'height': '350px'}) # header_button - header_button = create_expanded_button('Human Neocortical Neurosolver', - 'success', '20') + header_button = create_expanded_button('HUMAN NEOCORTICAL NEUROSOLVER', + 'success', height='40px') # Sliders to change local-connectivity Params sliders = [_get_sliders(params, @@ -317,8 +319,8 @@ def _update_plot_window(plot_type): dropdown.observe(_update_plot_window, 'value') # Run button - run_button = create_expanded_button('Run', 'success', '15') - load_button = create_expanded_button('Load parameters', 'success', '15') + run_button = create_expanded_button('Run', 'success', height='30px') + load_button = create_expanded_button('Load parameters', 'success', height='30px') def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, From 1caa9a6698556226806edced19b0f7045bc3e485 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 16 Apr 2021 23:36:58 -0400 Subject: [PATCH 18/88] STY: Labels for groups of widgets --- hnn_core/gui.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index cfa7dadce..d3700d9ff 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -13,7 +13,7 @@ from ipywidgets import (FloatSlider, Dropdown, Button, RadioButtons, fixed, interactive_output, interactive, interact, - FloatText, Output, + FloatText, HTML, Output, HBox, VBox, Tab, Accordion, Layout, AppLayout) @@ -100,22 +100,25 @@ def _get_poisson_widget(drive_title, layout, style): tstop = FloatText(value=8.5, description='Stop time:', layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) + labels = {'rate_constant': HTML(value="Rate constants"), + 'ampa': HTML(value="AMPA weights"), + 'nmda': HTML(value="NMDA weights")} weights_ampa, weights_nmda = dict(), dict() rate_constant = dict() for cell_type in cell_types: rate_constant[f'{cell_type}'] = FloatText( - value=8.5, description=f'Rate constant ({cell_type}):', + value=8.5, description=f'{cell_type}:', layout=layout, style=style) weights_ampa[f'{cell_type}'] = FloatText( - value=0., description=f'AMPA ({cell_type}):', + value=0., description=f'{cell_type}:', layout=layout, style=style) weights_nmda[f'{cell_type}'] = FloatText( - value=0., description=f'NMDA ({cell_type}):', layout=layout, + value=0., description=f'{cell_type}:', layout=layout, style=style) - drive_box = VBox([tstart, tstop, location] + - list(rate_constant.values()) + - list(weights_ampa.values()) + + drive_box = VBox([tstart, tstop, location] + [labels['rate_constant']] + + list(rate_constant.values()) + [labels['ampa']] + + list(weights_ampa.values()) + [labels['nmda']] + list(weights_nmda.values())) drive = dict(type='Poisson', name=drive_title, tstart=tstart, tstop=tstop, rate_constant=rate_constant, @@ -152,7 +155,7 @@ def _get_evoked_widget(drive_title, layout, style): def add_drive_widget(drive_type): """Add a widget for a new drive.""" - layout = Layout(width='280px', height='auto') + layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} drives_out.clear_output() with drives_out: From 83cd5b56eb7ec21f4591fc03f4e6f9a5bd8cbf9e Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 17 Apr 2021 00:36:07 -0400 Subject: [PATCH 19/88] MAINT: gamma example sort of works --- hnn_core/gui.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index d3700d9ff..a3582e72f 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -202,12 +202,12 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): """Run the simulation and plot outputs.""" for drive in drive_widgets: if drive['type'] == 'Poisson': - weights_ampa = {k: v.value for k, v in - drive['weights_ampa'].items()} - weights_nmda = {k: v.value for k, v in - drive['weights_nmda'].items()} - rate_constant = {k: v.value for k, v in - drive['rate_constant'].items()} + weights_ampa, weights_nmda, rate_constant = dict(), dict(), dict() + for k, v in drive['rate_constant'].items(): + if v.value > 0: + weights_ampa[k] = drive['weights_ampa'][k].value + weights_nmda[k] = drive['weights_nmda'][k].value + rate_constant[k] = drive['rate_constant'][k].value variables['net'].add_poisson_drive( name=drive['name'], tstart=drive['tstart'].value, From d683fa57eee31e2ffd37e2a68cfe285725981113 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 17 Apr 2021 11:20:40 -0400 Subject: [PATCH 20/88] ENH: file upload widget to load a network --- hnn_core/gui.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index a3582e72f..949d83d99 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -13,7 +13,7 @@ from ipywidgets import (FloatSlider, Dropdown, Button, RadioButtons, fixed, interactive_output, interactive, interact, - FloatText, HTML, Output, + FloatText, FileUpload, HTML, Output, HBox, VBox, Tab, Accordion, Layout, AppLayout) @@ -24,7 +24,7 @@ def create_expanded_button(description, button_style, height): - style = {'button_color': '#8A2BE2', 'font_size': height} + style = {'button_color': '#8A2BE2'} return Button(description=description, button_style=button_style, layout=Layout(height=height, width='auto'), style=style) @@ -321,14 +321,28 @@ def _update_plot_window(plot_type): interactive(_update_plot_window, plot_type='current dipole') dropdown.observe(_update_plot_window, 'value') - # Run button + # Run and load button run_button = create_expanded_button('Run', 'success', height='30px') - load_button = create_expanded_button('Load parameters', 'success', height='30px') + style = {'button_color': '#8A2BE2', 'font_color': 'white'} + load_button = FileUpload(accept='.json', multiple=False, style=style, + description='Load network', button_style='success') + + def _on_upload_change(change): + import json + import codecs + + if len(change['owner'].value) == 0: + return + + file_uploaded = change['owner'].value + json_data = list(file_uploaded.values())[0]['content'] + params = json.loads(json_data) def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, b) + load_button.observe(_on_upload_change) run_button.on_click(_on_button_clicked) footer = HBox([run_button, load_button, dropdown]) From 8265be7143cdfbdc2357ba251adcabdefdae1184 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 17 Apr 2021 11:36:05 -0400 Subject: [PATCH 21/88] ENH: improve slider with log scale --- hnn_core/gui.py | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 949d83d99..939815cdb 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -11,7 +11,7 @@ from IPython.display import display -from ipywidgets import (FloatSlider, Dropdown, Button, RadioButtons, +from ipywidgets import (FloatLogSlider, Dropdown, Button, RadioButtons, fixed, interactive_output, interactive, interact, FloatText, FileUpload, HTML, Output, HBox, VBox, Tab, Accordion, @@ -35,31 +35,14 @@ def update_params(params, **updates): return params -def _get_min(v): - if v < 0: - return v * 1.5 - else: - return v * 0.5 - - -def _get_max(v): - if v > 0: - return v * 1.5 - else: - return v * 0.5 - - def _get_sliders(params, param_keys): """Get sliders""" - style = {'description_width': 'initial'} + style = {'description_width': '150px'} sliders = list() for d in param_keys: - min_val = _get_min(params[d]) - max_val = _get_max(params[d]) - step = (max_val - min_val) / 10. - slider = FloatSlider( - value=params[d], min=min_val, max=max_val, step=step, - description=d, + slider = FloatLogSlider( + value=params[d], min=-5, max=1, step=0.2, + description=d.split('gbar_')[1], disabled=False, continuous_update=False, orientation='horizontal', readout=True, readout_format='.2e', style=style) From 6d6aca22637dd644ac3cd694fc9c6109229b4922 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 17 Apr 2021 11:57:33 -0400 Subject: [PATCH 22/88] ENH: update sliders with new network + create new network --- hnn_core/gui.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 939815cdb..f58c6328f 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -1,6 +1,6 @@ """IPywidgets GUI.""" # Authors: Mainak Jas - +import json import os.path as op from functools import partial, update_wrapper @@ -311,15 +311,20 @@ def _update_plot_window(plot_type): description='Load network', button_style='success') def _on_upload_change(change): - import json - import codecs - if len(change['owner'].value) == 0: return file_uploaded = change['owner'].value json_data = list(file_uploaded.values())[0]['content'] params = json.loads(json_data) + for slider in sliders: + for sl in slider: + key = 'gbar_' + sl.description + sl.value = params[key] + + external_drives = variables['net'].external_drives.copy() + variables['net'] = Network(params, add_drives_from_params=False) + variables['net'].external_drives = external_drives.copy() def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, From 8eeb56925e14d657371b97c91394dc32df47709a Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 17 Apr 2021 15:35:14 -0400 Subject: [PATCH 23/88] ENH: improve flow and readability by avoiding nested function --- hnn_core/gui.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index f58c6328f..8feea28f6 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -1,5 +1,7 @@ """IPywidgets GUI.""" + # Authors: Mainak Jas + import json import os.path as op from functools import partial, update_wrapper @@ -181,6 +183,23 @@ def update_plot_window(variables, plot_out, plot_type): variables['dpls'][0].plot_tfr_morlet(freqs) +def on_upload_change(change, sliders, variables): + if len(change['owner'].value) == 0: + return + + file_uploaded = change['owner'].value + json_data = list(file_uploaded.values())[0]['content'] + params = json.loads(json_data) + for slider in sliders: + for sl in slider: + key = 'gbar_' + sl.description + sl.value = params[key] + + external_drives = variables['net'].external_drives.copy() + variables['net'] = Network(params, add_drives_from_params=False) + variables['net'].external_drives = external_drives.copy() + + def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): """Run the simulation and plot outputs.""" for drive in drive_widgets: @@ -261,7 +280,7 @@ def run_hnn_gui(): _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] - # accordians + # accordians to group local-connectivity by cel type boxes = [VBox(slider) for slider in sliders] titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas'] accordian = Accordion(children=boxes) @@ -310,26 +329,13 @@ def _update_plot_window(plot_type): load_button = FileUpload(accept='.json', multiple=False, style=style, description='Load network', button_style='success') - def _on_upload_change(change): - if len(change['owner'].value) == 0: - return - - file_uploaded = change['owner'].value - json_data = list(file_uploaded.values())[0]['content'] - params = json.loads(json_data) - for slider in sliders: - for sl in slider: - key = 'gbar_' + sl.description - sl.value = params[key] - - external_drives = variables['net'].external_drives.copy() - variables['net'] = Network(params, add_drives_from_params=False) - variables['net'].external_drives = external_drives.copy() - def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, b) + def _on_upload_change(change): + return on_upload_change(change, sliders, variables) + load_button.observe(_on_upload_change) run_button.on_click(_on_button_clicked) footer = HBox([run_button, load_button, dropdown]) From 50a0f610fe315633b1d83e5ac5d2e7c6f935b7e3 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 17 Apr 2021 22:16:32 -0400 Subject: [PATCH 24/88] MAINT: reorganize for better readability --- hnn_core/gui.py | 53 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 8feea28f6..314dda175 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -19,11 +19,6 @@ HBox, VBox, Tab, Accordion, Layout, AppLayout) -drives_out = Output() -drive_widgets = list() -drive_titles = list() -drive_boxes = list() - def create_expanded_button(description, button_style, height): style = {'button_color': '#8A2BE2'} @@ -138,7 +133,8 @@ def _get_evoked_widget(drive_title, layout, style): return drive, drive_box -def add_drive_widget(drive_type): +def add_drive_widget(drive_type, drive_titles, drive_boxes, drive_widgets, + drives_out): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} @@ -255,10 +251,28 @@ def run_hnn_gui(): params_fname = op.join(hnn_core_root, 'param', 'default.json') params = read_params(params_fname) + drive_widgets = list() + drive_titles = list() + drive_boxes = list() variables = dict(net=None, dpls=None) variables['net'] = Network(params, add_drives_from_params=False) + def _add_drive_widget(drive_type): + return add_drive_widget(drive_type, drive_titles, drive_boxes, + drive_widgets, drives_out) + + def _on_button_clicked(b): + return on_button_clicked(log_out, plot_out, drive_widgets, variables, + b) + + def _on_upload_change(change): + return on_upload_change(change, sliders, variables) + + def _update_plot_window(plot_type): + return update_plot_window(variables, plot_out, plot_type) + # Output windows + drives_out = Output() # window to add new drives log_out = Output(layout={'border': '1px solid gray', 'height': '150px', 'overflow_y': 'auto'}) plot_out = Output(layout={'border': '1px solid gray', 'height': '350px'}) @@ -291,16 +305,13 @@ def run_hnn_gui(): layout = Layout(width='200px', height='auto') drives_dropdown = Dropdown( options=['Evoked', 'Poisson', 'Rhythmic', ''], - value='', - description='Drive:', - disabled=False, - layout=layout - ) + value='', description='Drive:',disabled=False, + layout=layout) # XXX: should be simpler to use Stacked class starting # from IPywidgets > 8.0 - interactive(add_drive_widget, drive_type='Evoked') - drives_dropdown.observe(add_drive_widget, 'value') + interactive(_add_drive_widget, drive_type='Evoked') + drives_dropdown.observe(_add_drive_widget, 'value') drives_options = VBox([drives_dropdown, drives_out]) # Tabs for left pane @@ -311,17 +322,14 @@ def run_hnn_gui(): left_tab.set_title(idx, title) # Dropdown menu to switch between plots - def _update_plot_window(plot_type): - return update_plot_window(variables, plot_out, plot_type) - - dropdown = Dropdown( + plot_dropdown = Dropdown( options=['input histogram', 'current dipole', 'spikes', 'spectogram'], value='current dipole', description='Plot:', disabled=False, ) interactive(_update_plot_window, plot_type='current dipole') - dropdown.observe(_update_plot_window, 'value') + plot_dropdown.observe(_update_plot_window, 'value') # Run and load button run_button = create_expanded_button('Run', 'success', height='30px') @@ -329,16 +337,9 @@ def _update_plot_window(plot_type): load_button = FileUpload(accept='.json', multiple=False, style=style, description='Load network', button_style='success') - def _on_button_clicked(b): - return on_button_clicked(log_out, plot_out, drive_widgets, variables, - b) - - def _on_upload_change(change): - return on_upload_change(change, sliders, variables) - load_button.observe(_on_upload_change) run_button.on_click(_on_button_clicked) - footer = HBox([run_button, load_button, dropdown]) + footer = HBox([run_button, load_button, plot_dropdown]) right_sidebar = VBox([plot_out, log_out]) From b2d47aae2ec369482ba1ce04fdcec9ef7fa8e303 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 19 Apr 2021 00:09:52 -0400 Subject: [PATCH 25/88] DOC: PSD plot --- hnn_core/gui.py | 16 +++++++++------- hnn_widget.ipynb | 6 +++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 314dda175..950ba17ac 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -174,6 +174,8 @@ def update_plot_window(variables, plot_out, plot_type): variables['dpls'][0].plot() elif plot_type['new'] == 'input histogram': variables['net'].cell_response.plot_spikes_hist() + elif plot_type['new'] == 'PSD': + variables['dpls'][0].plot_psd(fmin=0, fmax=50) elif plot_type['new'] == 'spectogram': freqs = np.arange(20., 100., 1.) variables['dpls'][0].plot_tfr_morlet(freqs) @@ -305,7 +307,7 @@ def _update_plot_window(plot_type): layout = Layout(width='200px', height='auto') drives_dropdown = Dropdown( options=['Evoked', 'Poisson', 'Rhythmic', ''], - value='', description='Drive:',disabled=False, + value='', description='Drive:', disabled=False, layout=layout) # XXX: should be simpler to use Stacked class starting @@ -323,11 +325,10 @@ def _update_plot_window(plot_type): # Dropdown menu to switch between plots plot_dropdown = Dropdown( - options=['input histogram', 'current dipole', 'spikes', 'spectogram'], - value='current dipole', - description='Plot:', - disabled=False, - ) + options=['input histogram', 'current dipole', + 'spikes', 'PSD', 'spectogram'], + value='current dipole', description='Plot:', + disabled=False) interactive(_update_plot_window, plot_type='current dipole') plot_dropdown.observe(_update_plot_window, 'value') @@ -335,7 +336,8 @@ def _update_plot_window(plot_type): run_button = create_expanded_button('Run', 'success', height='30px') style = {'button_color': '#8A2BE2', 'font_color': 'white'} load_button = FileUpload(accept='.json', multiple=False, style=style, - description='Load network', button_style='success') + description='Load network', + button_style='success') load_button.observe(_on_upload_change) run_button.on_click(_on_button_clicked) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 74538ef41..3e9665f02 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -8,12 +8,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0420f75f7a5a4ae3a2305cf9a38e24e8", + "model_id": "92fe4b4b9a914188a6c193ba5b659cb7", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "AppLayout(children=(Button(button_style='success', description='Human Neocortical Neurosolver', layout=Layout(…" + "AppLayout(children=(Button(button_style='success', description='HUMAN NEOCORTICAL NEUROSOLVER', layout=Layout(…" ] }, "metadata": {}, @@ -45,7 +45,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.8" + "version": "3.7.4" } }, "nbformat": 4, From 1dde3506457210f2f0b516fa140e22aee3be7098 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 19 Apr 2021 08:22:12 -0400 Subject: [PATCH 26/88] FIX: evoked widget works --- hnn_core/gui.py | 57 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 950ba17ac..6243738bf 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -15,7 +15,7 @@ from ipywidgets import (FloatLogSlider, Dropdown, Button, RadioButtons, fixed, interactive_output, interactive, interact, - FloatText, FileUpload, HTML, Output, + FloatText, IntText, FileUpload, HTML, Output, HBox, VBox, Tab, Accordion, Layout, AppLayout) @@ -51,7 +51,7 @@ def _get_sliders(params, param_keys): def _get_rhythmic_widget(drive_title, layout, style): - tstart = FloatText(value=7.5, description='Start time:', + tstart = FloatText(value=0., description='Start time:', layout=layout, style=style) tstart_std = FloatText(value=7.5, description='Start time dev:', layout=layout, style=style) @@ -108,27 +108,40 @@ def _get_poisson_widget(drive_title, layout, style): def _get_evoked_widget(drive_title, layout, style): - mu = FloatText(value=7.5, description='Mean time:', + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', + 'L2_basket'] + mu = FloatText(value=0, description='Mean time:', layout=layout) - sigma = FloatText(value=8.5, description='Std dev time:', + sigma = FloatText(value=1, description='Std dev time:', layout=layout) - numspikes = FloatText(value=8.5, description='Number of spikes:', - layout=layout) + numspikes = IntText(value=1, description='No. Spikes:', + layout=layout) location = RadioButtons(options=['proximal', 'distal']) - weights_ampa_L5Pyr = FloatText(value=8.5, - description='AMPA (L5 Pyr):', - layout=layout) - weights_nmda_L5Pyr = FloatText(value=8.5, - description='NMDA (L5 Pyr):', - layout=layout) - - drive_box = VBox([mu, sigma, numspikes, location, - weights_ampa_L5Pyr, weights_nmda_L5Pyr]) + labels = {'ampa': HTML(value="AMPA weights"), + 'nmda': HTML(value="NMDA weights"), + 'delays': HTML(value="Synaptic delays")} + + weights_ampa, weights_nmda, delays = dict(), dict(), dict() + for cell_type in cell_types: + weights_ampa[f'{cell_type}'] = FloatText( + value=0., description=f'{cell_type}:', + layout=layout, style=style) + weights_nmda[f'{cell_type}'] = FloatText( + value=0., description=f'{cell_type}:', layout=layout, + style=style) + delays[f'{cell_type}'] = FloatText( + value=0.1, description=f'{cell_type}:', layout=layout, + style=style) + + drive_box = VBox([mu, sigma, numspikes, location] + + [labels['ampa']] + list(weights_ampa.values()) + + [labels['nmda']] + list(weights_nmda.values()) + + [labels['delays']] + list(delays.values())) drive = dict(type='Evoked', name=drive_title, mu=mu, sigma=sigma, numspikes=numspikes, sync_within_trial=False, location=location, - weights_ampa_L5Pyr=weights_ampa_L5Pyr, - weights_nmda_L5Pyr=weights_nmda_L5Pyr, + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, delays=delays, space_constant=3.0) return drive, drive_box @@ -219,6 +232,11 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): space_constant=100.0 ) elif drive['type'] == 'Evoked': + weights_ampa = {k: v.value for k, v in + drive['weights_ampa'].items()} + weights_nmda = {k: v.value for k, v in + drive['weights_nmda'].items()} + delays = {k: v.value for k, v in drive['delays'].items()} variables['net'].add_evoked_drive( name=drive['name'], mu=drive['mu'].value, @@ -226,8 +244,9 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): numspikes=drive['numspikes'].value, sync_within_trial=False, location=drive['location'].value, - weights_ampa=dict(L5_pyramidal=drive['weights_ampa_L5Pyr'].value), - weights_nmda=dict(L5_pyramidal=drive['weights_nmda_L5Pyr'].value), + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, + synaptic_delays=delays, space_constant=3.0 ) elif drive['type'] == 'Rhythmic': From b45ac15d5f4ebe09815cf9274f8a532f89d409f6 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 19 Apr 2021 08:46:31 -0400 Subject: [PATCH 27/88] ENH: Try mybinder demo --- binder/postBuild | 3 +++ binder/requirements.txt | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 binder/postBuild create mode 100644 binder/requirements.txt diff --git a/binder/postBuild b/binder/postBuild new file mode 100644 index 000000000..eaf6b5227 --- /dev/null +++ b/binder/postBuild @@ -0,0 +1,3 @@ +set -e + +python setup.py build_mod diff --git a/binder/requirements.txt b/binder/requirements.txt new file mode 100644 index 000000000..f6ee80006 --- /dev/null +++ b/binder/requirements.txt @@ -0,0 +1,5 @@ +NEURON +voila +ipywidgets +matplotlib +scipy From e8aa84feceba7bbd90bee0d103f9ec6744ec6265 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 19 Apr 2021 13:17:36 -0400 Subject: [PATCH 28/88] ENH: cell-specific widgets in all drives --- hnn_core/gui.py | 119 +++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 6243738bf..59e53fd5c 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -50,7 +50,34 @@ def _get_sliders(params, param_keys): return sliders +def _get_cell_specific_widgets(layout, style): + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', + 'L2_basket'] + weights_ampa, weights_nmda, delays = dict(), dict(), dict() + for cell_type in cell_types: + weights_ampa[f'{cell_type}'] = FloatText( + value=0., description=f'{cell_type}:', + layout=layout, style=style) + weights_nmda[f'{cell_type}'] = FloatText( + value=0., description=f'{cell_type}:', layout=layout, + style=style) + delays[f'{cell_type}'] = FloatText( + value=0.1, description=f'{cell_type}:', layout=layout, + style=style) + + widgets_dict = {'weights_ampa': weights_ampa, + 'weights_nmda': weights_nmda, 'delays': delays} + widgets_list = ([HTML(value="AMPA weights")] + + list(weights_ampa.values()) + + [HTML(value="NMDA weights")] + + list(weights_nmda.values()) + + [HTML(value="Synaptic delays")] + + list(delays.values())) + return widgets_list, widgets_dict + + def _get_rhythmic_widget(drive_title, layout, style): + tstart = FloatText(value=0., description='Start time:', layout=layout, style=style) tstart_std = FloatText(value=7.5, description='Start time dev:', @@ -63,53 +90,45 @@ def _get_rhythmic_widget(drive_title, layout, style): layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) + widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, - location]) + location] + widgets_list) drive = dict(type='Rhythmic', name=drive_title, tstart=tstart, tstart_std=tstart_std, - burst_rate=burst_rate, burst_std=burst_std, - location=location) + burst_rate=burst_rate, burst_std=burst_std) + drive.update(widgets_dict) return drive, drive_box def _get_poisson_widget(drive_title, layout, style): - cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', - 'L2_basket'] tstart = FloatText(value=0.0, description='Start time:', layout=layout, style=style) tstop = FloatText(value=8.5, description='Stop time:', layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) - labels = {'rate_constant': HTML(value="Rate constants"), - 'ampa': HTML(value="AMPA weights"), - 'nmda': HTML(value="NMDA weights")} - weights_ampa, weights_nmda = dict(), dict() + + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', + 'L2_basket'] rate_constant = dict() for cell_type in cell_types: rate_constant[f'{cell_type}'] = FloatText( value=8.5, description=f'{cell_type}:', layout=layout, style=style) - weights_ampa[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', - layout=layout, style=style) - weights_nmda[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', layout=layout, - style=style) - drive_box = VBox([tstart, tstop, location] + [labels['rate_constant']] + - list(rate_constant.values()) + [labels['ampa']] + - list(weights_ampa.values()) + [labels['nmda']] + - list(weights_nmda.values())) + widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) + widgets_dict.update({'rate_constant': rate_constant}) + widgets_list.extend([HTML(value="Rate constants")] + + list(widgets_dict['rate_constant'].values())) + + drive_box = VBox([tstart, tstop, location] + widgets_list) drive = dict(type='Poisson', name=drive_title, tstart=tstart, tstop=tstop, rate_constant=rate_constant, - location=location, weights_ampa=weights_ampa, - weights_nmda=weights_nmda) + location=location) + drive.update(widgets_dict) return drive, drive_box def _get_evoked_widget(drive_title, layout, style): - cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', - 'L2_basket'] mu = FloatText(value=0, description='Mean time:', layout=layout) sigma = FloatText(value=1, description='Std dev time:', @@ -117,32 +136,14 @@ def _get_evoked_widget(drive_title, layout, style): numspikes = IntText(value=1, description='No. Spikes:', layout=layout) location = RadioButtons(options=['proximal', 'distal']) - labels = {'ampa': HTML(value="AMPA weights"), - 'nmda': HTML(value="NMDA weights"), - 'delays': HTML(value="Synaptic delays")} - - weights_ampa, weights_nmda, delays = dict(), dict(), dict() - for cell_type in cell_types: - weights_ampa[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', - layout=layout, style=style) - weights_nmda[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', layout=layout, - style=style) - delays[f'{cell_type}'] = FloatText( - value=0.1, description=f'{cell_type}:', layout=layout, - style=style) + widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) drive_box = VBox([mu, sigma, numspikes, location] + - [labels['ampa']] + list(weights_ampa.values()) + - [labels['nmda']] + list(weights_nmda.values()) + - [labels['delays']] + list(delays.values())) + widgets_list) drive = dict(type='Evoked', name=drive_title, mu=mu, sigma=sigma, numspikes=numspikes, - sync_within_trial=False, location=location, - weights_ampa=weights_ampa, - weights_nmda=weights_nmda, delays=delays, - space_constant=3.0) + sync_within_trial=False, location=location) + drive.update(widgets_dict) return drive, drive_box @@ -214,13 +215,16 @@ def on_upload_change(change, sliders, variables): def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): """Run the simulation and plot outputs.""" for drive in drive_widgets: + weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} + weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} + synaptic_delays = {k: v.value for k, v in drive['delays'].items()} if drive['type'] == 'Poisson': - weights_ampa, weights_nmda, rate_constant = dict(), dict(), dict() - for k, v in drive['rate_constant'].items(): - if v.value > 0: - weights_ampa[k] = drive['weights_ampa'][k].value - weights_nmda[k] = drive['weights_nmda'][k].value - rate_constant[k] = drive['rate_constant'][k].value + rate_constant = {k: v.value for k, v in + drive['rate_constant'].items() if v.value > 0} + weights_ampa = {k: v for k, v in weights_ampa.items() if k in + rate_constant} + weights_nmda = {k: v for k, v in weights_nmda.items() if k in + rate_constant} variables['net'].add_poisson_drive( name=drive['name'], tstart=drive['tstart'].value, @@ -229,14 +233,10 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): location=drive['location'].value, weights_ampa=weights_ampa, weights_nmda=weights_nmda, + synaptic_delays=synaptic_delays, space_constant=100.0 ) elif drive['type'] == 'Evoked': - weights_ampa = {k: v.value for k, v in - drive['weights_ampa'].items()} - weights_nmda = {k: v.value for k, v in - drive['weights_nmda'].items()} - delays = {k: v.value for k, v in drive['delays'].items()} variables['net'].add_evoked_drive( name=drive['name'], mu=drive['mu'].value, @@ -246,7 +246,7 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): location=drive['location'].value, weights_ampa=weights_ampa, weights_nmda=weights_nmda, - synaptic_delays=delays, + synaptic_delays=synaptic_delays, space_constant=3.0 ) elif drive['type'] == 'Rhythmic': @@ -256,7 +256,10 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): tstart_std=drive['tstart_std'].value, burst_rate=drive['burst_rate'].value, burst_std=drive['burst_std'].value, - location=drive['location'].value + location=drive['location'].value, + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, + synaptic_delays=synaptic_delays ) with log_out: variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) From ef42619d83c54b0a5e49a4681bb290471aa8867e Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Mon, 19 Apr 2021 15:20:13 -0400 Subject: [PATCH 29/88] ENH: more complete rhythmic drive --- hnn_core/gui.py | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 59e53fd5c..e957a4caa 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -80,22 +80,28 @@ def _get_rhythmic_widget(drive_title, layout, style): tstart = FloatText(value=0., description='Start time:', layout=layout, style=style) - tstart_std = FloatText(value=7.5, description='Start time dev:', + tstart_std = FloatText(value=0, description='Start time dev:', layout=layout, style=style) - tstop = FloatText(value=7.5, description='Stop time:', + tstop = FloatText(value=200, description='Stop time:', layout=layout, style=style) burst_rate = FloatText(value=7.5, description='Burst rate:', layout=layout, style=style) - burst_std = FloatText(value=7.5, description='Burst std dev:', + burst_std = FloatText(value=0, description='Burst std dev:', layout=layout, style=style) + repeats = FloatText(value=1, description='Spikes/burst:', + layout=layout, style=style) + seedcore = IntText(value=14, description='Seed: ', + layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, - location] + widgets_list) + repeats, location, seedcore] + widgets_list) drive = dict(type='Rhythmic', name=drive_title, tstart=tstart, tstart_std=tstart_std, - burst_rate=burst_rate, burst_std=burst_std) + burst_rate=burst_rate, burst_std=burst_std, + repeats=repeats, seedcore=seedcore, + location=location) drive.update(widgets_dict) return drive, drive_box @@ -105,6 +111,8 @@ def _get_poisson_widget(drive_title, layout, style): layout=layout, style=style) tstop = FloatText(value=8.5, description='Stop time:', layout=layout, style=style) + seedcore = IntText(value=14, description='Seed: ', + layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', @@ -120,10 +128,10 @@ def _get_poisson_widget(drive_title, layout, style): widgets_list.extend([HTML(value="Rate constants")] + list(widgets_dict['rate_constant'].values())) - drive_box = VBox([tstart, tstop, location] + widgets_list) + drive_box = VBox([tstart, tstop, seedcore, location] + widgets_list) drive = dict(type='Poisson', name=drive_title, tstart=tstart, tstop=tstop, rate_constant=rate_constant, - location=location) + seedcore=seedcore, location=location) drive.update(widgets_dict) return drive, drive_box @@ -135,14 +143,17 @@ def _get_evoked_widget(drive_title, layout, style): layout=layout) numspikes = IntText(value=1, description='No. Spikes:', layout=layout) + seedcore = IntText(value=14, description='Seed: ', + layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) - drive_box = VBox([mu, sigma, numspikes, location] + + drive_box = VBox([mu, sigma, numspikes, seedcore, location] + widgets_list) drive = dict(type='Evoked', name=drive_title, mu=mu, sigma=sigma, numspikes=numspikes, - sync_within_trial=False, location=location) + seedcore=seedcore, location=location, + sync_within_trial=False) drive.update(widgets_dict) return drive, drive_box @@ -234,7 +245,8 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, - space_constant=100.0 + space_constant=100.0, + seedcore=drive['seedcore'].value ) elif drive['type'] == 'Evoked': variables['net'].add_evoked_drive( @@ -247,7 +259,8 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, - space_constant=3.0 + space_constant=3.0, + seedcore=drive['seedcore'].value ) elif drive['type'] == 'Rhythmic': variables['net'].add_bursty_drive( @@ -256,10 +269,12 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): tstart_std=drive['tstart_std'].value, burst_rate=drive['burst_rate'].value, burst_std=drive['burst_std'].value, + repeats=drive['repeats'].value, location=drive['location'].value, weights_ampa=weights_ampa, weights_nmda=weights_nmda, - synaptic_delays=synaptic_delays + synaptic_delays=synaptic_delays, + seedcore=drive['seedcore'].value ) with log_out: variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) From 06beb81df9a3a6d640db7f8058bac476f3665160 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 21 Apr 2021 17:32:31 -0400 Subject: [PATCH 30/88] ENH: Add tab for simulation parameters --- hnn_core/gui.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index e957a4caa..edb3b0b21 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -223,8 +223,11 @@ def on_upload_change(change, sliders, variables): variables['net'].external_drives = external_drives.copy() -def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): +def on_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, + tstop, b): """Run the simulation and plot outputs.""" + plot_out.clear_output() + log_out.clear_output() for drive in drive_widgets: weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} @@ -277,6 +280,11 @@ def on_button_clicked(log_out, plot_out, drive_widgets, variables, b): seedcore=drive['seedcore'].value ) with log_out: + # XXX: hack, shouldn't modify variables['net'] + variables['net']._params['dt'] = tstep.value + variables['net']._params['tstop'] = tstop.value + variables['net'].cell_response._times = np.arange( + 0., tstop.value + tstep.value, tstep.value) variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) with plot_out: variables['dpls'][0].plot() @@ -302,7 +310,7 @@ def _add_drive_widget(drive_type): def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, - b) + tstep, tstop, b) def _on_upload_change(change): return on_upload_change(change, sliders, variables) @@ -320,6 +328,11 @@ def _update_plot_window(plot_type): header_button = create_expanded_button('HUMAN NEOCORTICAL NEUROSOLVER', 'success', height='40px') + # Simulation parameters + tstop = FloatText(value=170, description='tstop (s):', disabled=False) + tstep = FloatText(value=0.025, description='tstep (s):', disabled=False) + simulation_box = VBox([tstop, tstep]) + # Sliders to change local-connectivity Params sliders = [_get_sliders(params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', @@ -333,7 +346,7 @@ def _update_plot_window(plot_type): _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] - # accordians to group local-connectivity by cel type + # accordians to group local-connectivity by cell type boxes = [VBox(slider) for slider in sliders] titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas'] accordian = Accordion(children=boxes) @@ -355,8 +368,8 @@ def _update_plot_window(plot_type): # Tabs for left pane left_tab = Tab() - left_tab.children = [accordian, drives_options] - titles = ['Cell connectivity', 'Drives'] + left_tab.children = [simulation_box, accordian, drives_options] + titles = ['Simulation', 'Cell connectivity', 'Drives'] for idx, title in enumerate(titles): left_tab.set_title(idx, title) From cf40a57bf17184f772dbd53f2e8527c529be2b25 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 21 Apr 2021 17:55:12 -0400 Subject: [PATCH 31/88] ENH: Bound tstop of drive by simulation tstop --- hnn_core/gui.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index edb3b0b21..74776bb75 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -15,8 +15,8 @@ from ipywidgets import (FloatLogSlider, Dropdown, Button, RadioButtons, fixed, interactive_output, interactive, interact, - FloatText, IntText, FileUpload, HTML, Output, - HBox, VBox, Tab, Accordion, + FloatText, BoundedFloatText, IntText, FileUpload, + HTML, Output, HBox, VBox, Tab, Accordion, Layout, AppLayout) @@ -76,14 +76,15 @@ def _get_cell_specific_widgets(layout, style): return widgets_list, widgets_dict -def _get_rhythmic_widget(drive_title, layout, style): +def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): tstart = FloatText(value=0., description='Start time:', layout=layout, style=style) tstart_std = FloatText(value=0, description='Start time dev:', layout=layout, style=style) - tstop = FloatText(value=200, description='Stop time:', - layout=layout, style=style) + tstop = BoundedFloatText(value=tstop_widget.value, description='Stop time:', + max=tstop_widget.value, + layout=layout, style=style) burst_rate = FloatText(value=7.5, description='Burst rate:', layout=layout, style=style) burst_std = FloatText(value=0, description='Burst std dev:', @@ -106,11 +107,13 @@ def _get_rhythmic_widget(drive_title, layout, style): return drive, drive_box -def _get_poisson_widget(drive_title, layout, style): +def _get_poisson_widget(drive_title, tstop_widget, layout, style): tstart = FloatText(value=0.0, description='Start time:', layout=layout, style=style) - tstop = FloatText(value=8.5, description='Stop time:', - layout=layout, style=style) + tstop = BoundedFloatText(value=tstop_widget.value, + max=tstop_widget.value, + description='Stop time:', + layout=layout, style=style) seedcore = IntText(value=14, description='Seed: ', layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) @@ -159,7 +162,7 @@ def _get_evoked_widget(drive_title, layout, style): def add_drive_widget(drive_type, drive_titles, drive_boxes, drive_widgets, - drives_out): + drives_out, tstop_widget): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} @@ -168,9 +171,11 @@ def add_drive_widget(drive_type, drive_titles, drive_boxes, drive_widgets, drive_title = drive_type['new'] + str(len(drive_boxes)) if drive_type['new'] == 'Rhythmic': - drive, drive_box = _get_rhythmic_widget(drive_title, layout, style) + drive, drive_box = _get_rhythmic_widget(drive_title, tstop_widget, + layout, style) elif drive_type['new'] == 'Poisson': - drive, drive_box = _get_poisson_widget(drive_title, layout, style) + drive, drive_box = _get_poisson_widget(drive_title, tstop_widget, + layout, style) elif drive_type['new'] == 'Evoked': drive, drive_box = _get_evoked_widget(drive_title, layout, style) @@ -306,7 +311,7 @@ def run_hnn_gui(): def _add_drive_widget(drive_type): return add_drive_widget(drive_type, drive_titles, drive_boxes, - drive_widgets, drives_out) + drive_widgets, drives_out, tstop) def _on_button_clicked(b): return on_button_clicked(log_out, plot_out, drive_widgets, variables, From ae74d7e7fab48af70fb3642a3558415d33477781 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 21 Apr 2021 18:01:33 -0400 Subject: [PATCH 32/88] MAINT: use kwargs to save some lines --- hnn_core/gui.py | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 74776bb75..03251e328 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -41,8 +41,7 @@ def _get_sliders(params, param_keys): value=params[d], min=-5, max=1, step=0.2, description=d.split('gbar_')[1], disabled=False, continuous_update=False, orientation='horizontal', - readout=True, readout_format='.2e', - style=style) + readout=True, readout_format='.2e', style=style) sliders.append(slider) _update_params = partial(update_params, params) @@ -51,19 +50,17 @@ def _get_sliders(params, param_keys): def _get_cell_specific_widgets(layout, style): + kwargs = dict(layout=layout, style=style) cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] weights_ampa, weights_nmda, delays = dict(), dict(), dict() for cell_type in cell_types: weights_ampa[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', - layout=layout, style=style) + value=0., description=f'{cell_type}:', **kwargs) weights_nmda[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', layout=layout, - style=style) + value=0., description=f'{cell_type}:', **kwargs) delays[f'{cell_type}'] = FloatText( - value=0.1, description=f'{cell_type}:', layout=layout, - style=style) + value=0.1, description=f'{cell_type}:', **kwargs) widgets_dict = {'weights_ampa': weights_ampa, 'weights_nmda': weights_nmda, 'delays': delays} @@ -78,21 +75,16 @@ def _get_cell_specific_widgets(layout, style): def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): - tstart = FloatText(value=0., description='Start time:', - layout=layout, style=style) + kwargs = dict(layout=layout, style=style) + tstart = FloatText(value=0., description='Start time:', **kwargs) tstart_std = FloatText(value=0, description='Start time dev:', - layout=layout, style=style) + **kwargs) tstop = BoundedFloatText(value=tstop_widget.value, description='Stop time:', - max=tstop_widget.value, - layout=layout, style=style) - burst_rate = FloatText(value=7.5, description='Burst rate:', - layout=layout, style=style) - burst_std = FloatText(value=0, description='Burst std dev:', - layout=layout, style=style) - repeats = FloatText(value=1, description='Spikes/burst:', - layout=layout, style=style) - seedcore = IntText(value=14, description='Seed: ', - layout=layout, style=style) + max=tstop_widget.value, **kwargs) + burst_rate = FloatText(value=7.5, description='Burst rate:', **kwargs) + burst_std = FloatText(value=0, description='Burst std dev:', **kwargs) + repeats = FloatText(value=1, description='Spikes/burst:', **kwargs) + seedcore = IntText(value=14, description='Seed: ', **kwargs) location = RadioButtons(options=['proximal', 'distal']) widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) @@ -140,14 +132,13 @@ def _get_poisson_widget(drive_title, tstop_widget, layout, style): def _get_evoked_widget(drive_title, layout, style): - mu = FloatText(value=0, description='Mean time:', - layout=layout) + kwargs = dict(layout=layout, style=style) + mu = FloatText(value=0, description='Mean time:', **kwargs) sigma = FloatText(value=1, description='Std dev time:', - layout=layout) + **kwargs) numspikes = IntText(value=1, description='No. Spikes:', - layout=layout) - seedcore = IntText(value=14, description='Seed: ', - layout=layout, style=style) + **kwargs) + seedcore = IntText(value=14, description='Seed: ', **kwargs) location = RadioButtons(options=['proximal', 'distal']) widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) From 9e2f1fed5ecb8d9056f8f57719666c9b7b6a2cb2 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 21 Apr 2021 18:05:24 -0400 Subject: [PATCH 33/88] MAINT: on_button_clicked -> run_button_clicked --- hnn_core/gui.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 03251e328..3558fa109 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -219,8 +219,8 @@ def on_upload_change(change, sliders, variables): variables['net'].external_drives = external_drives.copy() -def on_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, - tstop, b): +def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, + tstop, b): """Run the simulation and plot outputs.""" plot_out.clear_output() log_out.clear_output() @@ -304,9 +304,9 @@ def _add_drive_widget(drive_type): return add_drive_widget(drive_type, drive_titles, drive_boxes, drive_widgets, drives_out, tstop) - def _on_button_clicked(b): - return on_button_clicked(log_out, plot_out, drive_widgets, variables, - tstep, tstop, b) + def _run_button_clicked(b): + return run_button_clicked(log_out, plot_out, drive_widgets, variables, + tstep, tstop, b) def _on_upload_change(change): return on_upload_change(change, sliders, variables) @@ -386,7 +386,7 @@ def _update_plot_window(plot_type): button_style='success') load_button.observe(_on_upload_change) - run_button.on_click(_on_button_clicked) + run_button.on_click(_run_button_clicked) footer = HBox([run_button, load_button, plot_dropdown]) right_sidebar = VBox([plot_out, log_out]) From 0309f148a49fe7a9ee4007603c6b78bb22d02b16 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Wed, 21 Apr 2021 18:13:32 -0400 Subject: [PATCH 34/88] ENH: local connectivity sliders should now update correctly --- hnn_core/gui.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 3558fa109..02c5bdf87 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -27,11 +27,6 @@ def create_expanded_button(description, button_style, height): style=style) -def update_params(params, **updates): - params.update(dict(**updates)) - return params - - def _get_sliders(params, param_keys): """Get sliders""" style = {'description_width': '150px'} @@ -44,8 +39,11 @@ def _get_sliders(params, param_keys): readout=True, readout_format='.2e', style=style) sliders.append(slider) - _update_params = partial(update_params, params) - interactive_output(update_params, {s.description: s for s in sliders}) + def _update_params(params, **updates): + params.update(dict(**updates)) + return params + + interactive_output(_update_params, {s.description: s for s in sliders}) return sliders @@ -329,17 +327,17 @@ def _update_plot_window(plot_type): tstep = FloatText(value=0.025, description='tstep (s):', disabled=False) simulation_box = VBox([tstop, tstep]) - # Sliders to change local-connectivity Params - sliders = [_get_sliders(params, + # Sliders to change local-connectivity params + sliders = [_get_sliders(variables['net']._params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), - _get_sliders(params, + _get_sliders(variables['net']._params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa', 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa', 'gbar_L5Basket_L5Pyr_gabab']), - _get_sliders(params, + _get_sliders(variables['net']._params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), - _get_sliders(params, + _get_sliders(variables['net']._params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] # accordians to group local-connectivity by cell type From 217823d36a4ab437ffb8362cecfe70e1a05e1ca7 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 22 Apr 2021 00:08:42 -0400 Subject: [PATCH 35/88] FIX morlet cycles depend on freq --- hnn_core/gui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 02c5bdf87..af0744b31 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -77,7 +77,7 @@ def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): tstart = FloatText(value=0., description='Start time:', **kwargs) tstart_std = FloatText(value=0, description='Start time dev:', **kwargs) - tstop = BoundedFloatText(value=tstop_widget.value, description='Stop time:', + tstop = BoundedFloatText(value=tstop_widget.value, description='Stop time', max=tstop_widget.value, **kwargs) burst_rate = FloatText(value=7.5, description='Burst rate:', **kwargs) burst_std = FloatText(value=0, description='Burst std dev:', **kwargs) @@ -196,8 +196,9 @@ def update_plot_window(variables, plot_out, plot_type): elif plot_type['new'] == 'PSD': variables['dpls'][0].plot_psd(fmin=0, fmax=50) elif plot_type['new'] == 'spectogram': - freqs = np.arange(20., 100., 1.) - variables['dpls'][0].plot_tfr_morlet(freqs) + freqs = np.arange(10., 100., 1.) + n_cycles = freqs / 8. + variables['dpls'][0].plot_tfr_morlet(freqs, n_cycles=n_cycles) def on_upload_change(change, sliders, variables): From 96cb2d57f8ba8c03cf0c7fd5bf0bb1646f069d9a Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Thu, 22 Apr 2021 00:33:53 -0400 Subject: [PATCH 36/88] FIX tweaks to rhythmic drive --- hnn_core/gui.py | 27 +++++++++++++++------------ hnn_widget.ipynb | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index af0744b31..f25f58528 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -74,15 +74,16 @@ def _get_cell_specific_widgets(layout, style): def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): kwargs = dict(layout=layout, style=style) - tstart = FloatText(value=0., description='Start time:', **kwargs) - tstart_std = FloatText(value=0, description='Start time dev:', + tstart = FloatText(value=0., description='Start time (s)', **kwargs) + tstart_std = FloatText(value=0, description='Start time dev (s)', **kwargs) - tstop = BoundedFloatText(value=tstop_widget.value, description='Stop time', + tstop = BoundedFloatText(value=tstop_widget.value, + description='Stop time (s)', max=tstop_widget.value, **kwargs) - burst_rate = FloatText(value=7.5, description='Burst rate:', **kwargs) - burst_std = FloatText(value=0, description='Burst std dev:', **kwargs) - repeats = FloatText(value=1, description='Spikes/burst:', **kwargs) - seedcore = IntText(value=14, description='Seed: ', **kwargs) + burst_rate = FloatText(value=7.5, description='Burst rate (Hz)', **kwargs) + burst_std = FloatText(value=0, description='Burst std dev (Hz)', **kwargs) + repeats = FloatText(value=1, description='Repeats', **kwargs) + seedcore = IntText(value=14, description='Seed', **kwargs) location = RadioButtons(options=['proximal', 'distal']) widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) @@ -92,19 +93,19 @@ def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): tstart=tstart, tstart_std=tstart_std, burst_rate=burst_rate, burst_std=burst_std, repeats=repeats, seedcore=seedcore, - location=location) + location=location, tstop=tstop) drive.update(widgets_dict) return drive, drive_box def _get_poisson_widget(drive_title, tstop_widget, layout, style): - tstart = FloatText(value=0.0, description='Start time:', + tstart = FloatText(value=0.0, description='Start time (s)', layout=layout, style=style) tstop = BoundedFloatText(value=tstop_widget.value, max=tstop_widget.value, - description='Stop time:', + description='Stop time (s)', layout=layout, style=style) - seedcore = IntText(value=14, description='Seed: ', + seedcore = IntText(value=14, description='Seed', layout=layout, style=style) location = RadioButtons(options=['proximal', 'distal']) @@ -269,6 +270,7 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, burst_std=drive['burst_std'].value, repeats=drive['repeats'].value, location=drive['location'].value, + tstop=drive['tstop'].value, weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, @@ -343,7 +345,8 @@ def _update_plot_window(plot_type): # accordians to group local-connectivity by cell type boxes = [VBox(slider) for slider in sliders] - titles = ['Layer 2/3 Pyr', 'Layer 5 Pyr', 'Layer 2 Bas', 'Layer 5 Bas'] + titles = ['Layer 2/3 Pyramidal', 'Layer 5 Pyramidal', 'Layer 2 Basket', + 'Layer 5 Basket'] accordian = Accordion(children=boxes) for idx, title in enumerate(titles): accordian.set_title(idx, title) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index 3e9665f02..ac0e66cd2 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -8,7 +8,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "92fe4b4b9a914188a6c193ba5b659cb7", + "model_id": "6dd8f3c03c664417be1d770ac25aa76a", "version_major": 2, "version_minor": 0 }, From dd6817e0bd2dffbd997d4a872dfc112a1275e943 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Fri, 23 Apr 2021 17:29:45 -0400 Subject: [PATCH 37/88] ENH: add plot network --- hnn_core/gui.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index f25f58528..486d1a78c 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -200,6 +200,8 @@ def update_plot_window(variables, plot_out, plot_type): freqs = np.arange(10., 100., 1.) n_cycles = freqs / 8. variables['dpls'][0].plot_tfr_morlet(freqs, n_cycles=n_cycles) + elif plot_type['new'] == 'network': + variables['net'].plot_cells() def on_upload_change(change, sliders, variables): @@ -374,7 +376,7 @@ def _update_plot_window(plot_type): # Dropdown menu to switch between plots plot_dropdown = Dropdown( options=['input histogram', 'current dipole', - 'spikes', 'PSD', 'spectogram'], + 'spikes', 'PSD', 'spectogram', 'network'], value='current dipole', description='Plot:', disabled=False) interactive(_update_plot_window, plot_type='current dipole') From e663347522d4586805beaf3126c5632777c10928 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 24 Apr 2021 23:54:50 -0400 Subject: [PATCH 38/88] MAINT: simplify by removing drive_titles variable --- hnn_core/gui.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 486d1a78c..b33ff9c2a 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -71,7 +71,7 @@ def _get_cell_specific_widgets(layout, style): return widgets_list, widgets_dict -def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): +def _get_rhythmic_widget(name, tstop_widget, layout, style): kwargs = dict(layout=layout, style=style) tstart = FloatText(value=0., description='Start time (s)', **kwargs) @@ -89,7 +89,7 @@ def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, repeats, location, seedcore] + widgets_list) - drive = dict(type='Rhythmic', name=drive_title, + drive = dict(type='Rhythmic', name=name, tstart=tstart, tstart_std=tstart_std, burst_rate=burst_rate, burst_std=burst_std, repeats=repeats, seedcore=seedcore, @@ -98,7 +98,7 @@ def _get_rhythmic_widget(drive_title, tstop_widget, layout, style): return drive, drive_box -def _get_poisson_widget(drive_title, tstop_widget, layout, style): +def _get_poisson_widget(name, tstop_widget, layout, style): tstart = FloatText(value=0.0, description='Start time (s)', layout=layout, style=style) tstop = BoundedFloatText(value=tstop_widget.value, @@ -123,14 +123,14 @@ def _get_poisson_widget(drive_title, tstop_widget, layout, style): list(widgets_dict['rate_constant'].values())) drive_box = VBox([tstart, tstop, seedcore, location] + widgets_list) - drive = dict(type='Poisson', name=drive_title, tstart=tstart, + drive = dict(type='Poisson', name=name, tstart=tstart, tstop=tstop, rate_constant=rate_constant, seedcore=seedcore, location=location) drive.update(widgets_dict) return drive, drive_box -def _get_evoked_widget(drive_title, layout, style): +def _get_evoked_widget(name, layout, style): kwargs = dict(layout=layout, style=style) mu = FloatText(value=0, description='Mean time:', **kwargs) sigma = FloatText(value=1, description='Std dev time:', @@ -143,7 +143,7 @@ def _get_evoked_widget(drive_title, layout, style): widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) drive_box = VBox([mu, sigma, numspikes, seedcore, location] + widgets_list) - drive = dict(type='Evoked', name=drive_title, + drive = dict(type='Evoked', name=name, mu=mu, sigma=sigma, numspikes=numspikes, seedcore=seedcore, location=location, sync_within_trial=False) @@ -151,33 +151,32 @@ def _get_evoked_widget(drive_title, layout, style): return drive, drive_box -def add_drive_widget(drive_type, drive_titles, drive_boxes, drive_widgets, +def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, tstop_widget): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} drives_out.clear_output() with drives_out: - drive_title = drive_type['new'] + str(len(drive_boxes)) + name = drive_type['new'] + str(len(drive_boxes)) if drive_type['new'] == 'Rhythmic': - drive, drive_box = _get_rhythmic_widget(drive_title, tstop_widget, + drive, drive_box = _get_rhythmic_widget(name, tstop_widget, layout, style) elif drive_type['new'] == 'Poisson': - drive, drive_box = _get_poisson_widget(drive_title, tstop_widget, + drive, drive_box = _get_poisson_widget(name, tstop_widget, layout, style) elif drive_type['new'] == 'Evoked': - drive, drive_box = _get_evoked_widget(drive_title, layout, style) + drive, drive_box = _get_evoked_widget(name, layout, style) if drive_type['new'] in ['Evoked', 'Poisson', 'Rhythmic']: - drive_titles.append(drive_title) drive_boxes.append(drive_box) drive_widgets.append(drive) accordion = Accordion(children=drive_boxes, selected_index=len(drive_boxes) - 1) - for idx, this_title in enumerate(drive_titles): - accordion.set_title(idx, this_title) + for idx, drive in enumerate(drive_widgets): + accordion.set_title(idx, drive['name']) display(accordion) @@ -298,13 +297,12 @@ def run_hnn_gui(): params = read_params(params_fname) drive_widgets = list() - drive_titles = list() drive_boxes = list() variables = dict(net=None, dpls=None) variables['net'] = Network(params, add_drives_from_params=False) def _add_drive_widget(drive_type): - return add_drive_widget(drive_type, drive_titles, drive_boxes, + return add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, tstop) def _run_button_clicked(b): From f000998c1c28121ec919b18f817f90f9fb02d032 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 25 Apr 2021 00:34:51 -0400 Subject: [PATCH 39/88] ENH: read also .param files --- hnn_core/gui.py | 16 +++++++++++---- hnn_core/params.py | 50 +++++++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index b33ff9c2a..813e79c88 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -3,6 +3,7 @@ # Authors: Mainak Jas import json +import codecs import os.path as op from functools import partial, update_wrapper @@ -10,6 +11,7 @@ import hnn_core from hnn_core import simulate_dipole, read_params, Network +from hnn_core.params import _read_legacy_params, _read_json from IPython.display import display @@ -207,9 +209,15 @@ def on_upload_change(change, sliders, variables): if len(change['owner'].value) == 0: return + params_fname = change['owner'].metadata[0]['name'] file_uploaded = change['owner'].value - json_data = list(file_uploaded.values())[0]['content'] - params = json.loads(json_data) + param_data = list(file_uploaded.values())[0]['content'] + param_data = codecs.decode(param_data, encoding="utf-8") + + ext = op.splitext(params_fname)[1] + read_func = {'.json': _read_json, '.param': _read_legacy_params} + params = read_func[ext](param_data) + for slider in sliders: for sl in slider: key = 'gbar_' + sl.description @@ -383,8 +391,8 @@ def _update_plot_window(plot_type): # Run and load button run_button = create_expanded_button('Run', 'success', height='30px') style = {'button_color': '#8A2BE2', 'font_color': 'white'} - load_button = FileUpload(accept='.json', multiple=False, style=style, - description='Load network', + load_button = FileUpload(accept='.json,.param', multiple=False, + style=style, description='Load network', button_style='success') load_button.observe(_on_upload_change) diff --git a/hnn_core/params.py b/hnn_core/params.py index ef97247ee..2058ad572 100644 --- a/hnn_core/params.py +++ b/hnn_core/params.py @@ -25,49 +25,44 @@ def _count_evoked_inputs(d): return nprox, ndist -def _read_json(fname): +def _read_json(param_data): """Read param values from a .json file. Parameters ---------- - fname : str - Full path to the file (.json) + param_data : str + The data read in from the param file Returns ------- params_input : dict Dictionary of parameters """ - with open(fname) as json_data: - params_input = json.load(json_data) - - return params_input + return json.loads(param_data) -def _read_legacy_params(fname): +def _read_legacy_params(param_data): """Read param values from a .param file (legacy). Parameters ---------- - fname : str - Full path to the file (.param) + param_data : str + The data read in from the param file Returns ------- params_input : dict Dictionary of parameters """ - params_input = dict() - with open(fname, 'r') as fp: - for line in fp.readlines(): - split_line = line.lstrip().split(':') - key, value = [field.strip() for field in split_line] - try: - if '.' in value or 'e' in value: - params_input[key] = float(value) - else: - params_input[key] = int(value) - except ValueError: - params_input[key] = str(value) + for line in param_data.splitlines(): + split_line = line.lstrip().split(':') + key, value = [field.strip() for field in split_line] + try: + if '.' in value or 'e' in value: + params_input[key] = float(value) + else: + params_input[key] = int(value) + except ValueError: + params_input[key] = str(value) return params_input @@ -89,14 +84,15 @@ def read_params(params_fname): split_fname = op.splitext(params_fname) ext = split_fname[1] - if ext == '.json': - params_dict = _read_json(params_fname) - elif ext == '.param': - params_dict = _read_legacy_params(params_fname) - else: + if ext not in ['.json', '.param']: raise ValueError('Unrecognized extension, expected one of' + ' .json, .param. Got %s' % ext) + read_func = {'.json': _read_json, '.param': _read_legacy_params} + with open(params_fname, 'r') as fp: + param_data = fp.read() + params_dict = read_func[ext](param_data) + if len(params_dict) == 0: raise ValueError("Failed to read parameters from file: %s" % op.normpath(params_fname)) From 3639e91f24dede41ec8866514b7b8e2de7c7da6b Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 25 Apr 2021 09:47:09 -0400 Subject: [PATCH 40/88] DOC: update readme --- README.rst | 17 ++++++++++++++++- setup.py | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 0aebe1125..0830453cf 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ hnn-core :target: https://codecov.io/gh/jonescompneurolab/hnn-core :alt: Test coverage -This is a leaner and cleaner version of the code based off the `HNN repository `_. However, a Graphical User Interface is not supported at the moment in this repository. +This is a leaner and cleaner version of the code based off the `HNN repository `_. It is early Work in Progress. Contributors are very welcome. @@ -28,6 +28,15 @@ Dependencies Optional dependencies --------------------- +GUI +~~~ + +* ipywidgets +* voila + +Parallel processing +~~~~~~~~~~~~~~~~~~~ + * joblib (for simulating trials simultaneously) * mpi4py (for simulating the cells in parallel for a single trial). Also depends on: @@ -58,6 +67,12 @@ To check if everything worked fine, you can do:: and it should not give any error messages. +**GUI installation** + +To install the GUI dependencies along with ``hnn-core``, a simple tweak to the above command is needed:: + + $ pip install hnn_core[gui] + **Parallel backends** For further instructions on installation and usage of parallel backends for using more diff --git a/setup.py b/setup.py index a47affe8b..809c98531 100644 --- a/setup.py +++ b/setup.py @@ -105,6 +105,9 @@ def run(self): 'matplotlib', 'scipy' ], + extra_requires={ + 'gui': ['ipywidgets', 'voila'] + }, packages=find_packages(), package_data={'hnn_core': [ 'param/*.json', From 95a7733486bf7b5c80f2345393c3fa257b961799 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 25 Apr 2021 18:37:57 -0400 Subject: [PATCH 41/88] FIX: update connectivity for real? --- hnn_core/gui.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 813e79c88..cc1de230c 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -29,21 +29,23 @@ def create_expanded_button(description, button_style, height): style=style) -def _get_sliders(params, param_keys): +def _get_sliders(variables, param_keys): """Get sliders""" style = {'description_width': '150px'} sliders = list() for d in param_keys: slider = FloatLogSlider( - value=params[d], min=-5, max=1, step=0.2, + value=variables['net']._params[d], min=-5, max=1, step=0.2, description=d.split('gbar_')[1], disabled=False, continuous_update=False, orientation='horizontal', readout=True, readout_format='.2e', style=style) sliders.append(slider) - def _update_params(params, **updates): - params.update(dict(**updates)) - return params + def _update_params(variables, **updates): + variables['net']._params.update(dict(**updates)) + variables['net'].connectivity = list() + # XXX: hack until there is a proper API + variables['net'].self._set_default_connections() interactive_output(_update_params, {s.description: s for s in sliders}) return sliders @@ -339,16 +341,16 @@ def _update_plot_window(plot_type): simulation_box = VBox([tstop, tstep]) # Sliders to change local-connectivity params - sliders = [_get_sliders(variables['net']._params, + sliders = [_get_sliders(variables, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), - _get_sliders(variables['net']._params, + _get_sliders(variables, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa', 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa', 'gbar_L5Basket_L5Pyr_gabab']), - _get_sliders(variables['net']._params, + _get_sliders(variables, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), - _get_sliders(variables['net']._params, + _get_sliders(variables, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] # accordians to group local-connectivity by cell type From d456b846c063bf9ccd3038ad2fba91456f38bbe1 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 27 Apr 2021 10:54:23 -0400 Subject: [PATCH 42/88] ENH: Run button works twice for real --- hnn_core/gui.py | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index cc1de230c..e7523cfc1 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -29,23 +29,20 @@ def create_expanded_button(description, button_style, height): style=style) -def _get_sliders(variables, param_keys): +def _get_sliders(params, param_keys): """Get sliders""" style = {'description_width': '150px'} sliders = list() for d in param_keys: slider = FloatLogSlider( - value=variables['net']._params[d], min=-5, max=1, step=0.2, + value=params[d], min=-5, max=1, step=0.2, description=d.split('gbar_')[1], disabled=False, continuous_update=False, orientation='horizontal', readout=True, readout_format='.2e', style=style) sliders.append(slider) def _update_params(variables, **updates): - variables['net']._params.update(dict(**updates)) - variables['net'].connectivity = list() - # XXX: hack until there is a proper API - variables['net'].self._set_default_connections() + params.update(dict(**updates)) interactive_output(_update_params, {s.description: s for s in sliders}) return sliders @@ -207,7 +204,7 @@ def update_plot_window(variables, plot_out, plot_type): variables['net'].plot_cells() -def on_upload_change(change, sliders, variables): +def on_upload_change(change, sliders, params): if len(change['owner'].value) == 0: return @@ -218,23 +215,25 @@ def on_upload_change(change, sliders, variables): ext = op.splitext(params_fname)[1] read_func = {'.json': _read_json, '.param': _read_legacy_params} - params = read_func[ext](param_data) + params_network = read_func[ext](param_data) for slider in sliders: for sl in slider: key = 'gbar_' + sl.description - sl.value = params[key] + sl.value = params_network[key] - external_drives = variables['net'].external_drives.copy() - variables['net'] = Network(params, add_drives_from_params=False) - variables['net'].external_drives = external_drives.copy() + params.update(params_network) def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, - tstop, b): + tstop, params, b): """Run the simulation and plot outputs.""" plot_out.clear_output() log_out.clear_output() + with log_out: + params['dt'] = tstep.value + params['tstop'] = tstop.value + variables['net'] = Network(params, add_drives_from_params=False) for drive in drive_widgets: weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} @@ -288,11 +287,6 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, seedcore=drive['seedcore'].value ) with log_out: - # XXX: hack, shouldn't modify variables['net'] - variables['net']._params['dt'] = tstep.value - variables['net']._params['tstop'] = tstop.value - variables['net'].cell_response._times = np.arange( - 0., tstop.value + tstep.value, tstep.value) variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) with plot_out: variables['dpls'][0].plot() @@ -309,7 +303,6 @@ def run_hnn_gui(): drive_widgets = list() drive_boxes = list() variables = dict(net=None, dpls=None) - variables['net'] = Network(params, add_drives_from_params=False) def _add_drive_widget(drive_type): return add_drive_widget(drive_type, drive_boxes, @@ -317,10 +310,10 @@ def _add_drive_widget(drive_type): def _run_button_clicked(b): return run_button_clicked(log_out, plot_out, drive_widgets, variables, - tstep, tstop, b) + tstep, tstop, params, b) def _on_upload_change(change): - return on_upload_change(change, sliders, variables) + return log_out.capture(on_upload_change(change, sliders, params)) def _update_plot_window(plot_type): return update_plot_window(variables, plot_out, plot_type) @@ -341,16 +334,16 @@ def _update_plot_window(plot_type): simulation_box = VBox([tstop, tstep]) # Sliders to change local-connectivity params - sliders = [_get_sliders(variables, + sliders = [_get_sliders(params, ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), - _get_sliders(variables, + _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa', 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa', 'gbar_L5Basket_L5Pyr_gabab']), - _get_sliders(variables, + _get_sliders(params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), - _get_sliders(variables, + _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] # accordians to group local-connectivity by cell type From 7c0cba0c08b6dccb153be606f2ea667d42b48208 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 27 Apr 2021 11:51:14 -0400 Subject: [PATCH 43/88] ENH: delete drives button --- hnn_core/gui.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index e7523cfc1..eb9684423 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -318,6 +318,14 @@ def _on_upload_change(change): def _update_plot_window(plot_type): return update_plot_window(variables, plot_out, plot_type) + def _delete_drives_clicked(b): + drives_out.clear_output() + # black magic: the following does not work + # global drive_widgets; drive_widgets = list() + while len(drive_widgets) > 0: + drive_widgets.pop() + drive_boxes.pop() + # Output windows drives_out = Output() # window to add new drives log_out = Output(layout={'border': '1px solid gray', 'height': '150px', @@ -383,16 +391,18 @@ def _update_plot_window(plot_type): interactive(_update_plot_window, plot_type='current dipole') plot_dropdown.observe(_update_plot_window, 'value') - # Run and load button + # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') style = {'button_color': '#8A2BE2', 'font_color': 'white'} load_button = FileUpload(accept='.json,.param', multiple=False, style=style, description='Load network', button_style='success') + delete_button = create_expanded_button('Delete drives', 'success', height='30px') load_button.observe(_on_upload_change) run_button.on_click(_run_button_clicked) - footer = HBox([run_button, load_button, plot_dropdown]) + delete_button.on_click(_delete_drives_clicked) + footer = HBox([run_button, load_button, delete_button, plot_dropdown]) right_sidebar = VBox([plot_out, log_out]) From 322e8c1341b92003ac4cb1c11fe4191804af485e Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 27 Apr 2021 13:59:32 -0400 Subject: [PATCH 44/88] Resize a bit --- hnn_core/gui.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index eb9684423..685658082 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -397,7 +397,8 @@ def _delete_drives_clicked(b): load_button = FileUpload(accept='.json,.param', multiple=False, style=style, description='Load network', button_style='success') - delete_button = create_expanded_button('Delete drives', 'success', height='30px') + delete_button = create_expanded_button('Delete drives', 'success', + height='30px') load_button.observe(_on_upload_change) run_button.on_click(_run_button_clicked) @@ -411,6 +412,6 @@ def _delete_drives_clicked(b): left_sidebar=left_tab, right_sidebar=right_sidebar, footer=footer, - pane_widths=['380px', 1, 1], + pane_widths=['380px', '500px', '500px'], pane_heights=[1, '500px', 1]) return hnn_gui From be75d81f02925f8ab592c83dabf7df6c8e4c72ea Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 27 Apr 2021 17:45:53 -0400 Subject: [PATCH 45/88] Add screenshot to readme --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 0830453cf..ab8f5ad1f 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,10 @@ hnn-core :target: https://codecov.io/gh/jonescompneurolab/hnn-core :alt: Test coverage +.. image:: https://user-images.githubusercontent.com/15852194/115123685-2df1ef00-9f8c-11eb-8f3b-663486466193.png + :target: https://user-images.githubusercontent.com/15852194/115123685-2df1ef00-9f8c-11eb-8f3b-663486466193.png + :alt: HNN-core GUI + This is a leaner and cleaner version of the code based off the `HNN repository `_. It is early Work in Progress. Contributors are very welcome. From bfbe34240e200893f9d895d9106899c1f625470e Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 23 Jun 2022 12:07:10 -0400 Subject: [PATCH 46/88] Fix bug & add some functions --- hnn_core/gui.py | 249 +++++++++++++++++++++++++++++------------------ hnn_widget.ipynb | 10 ++ 2 files changed, 163 insertions(+), 96 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 685658082..bb77bb47c 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -48,10 +48,13 @@ def _update_params(variables, **updates): return sliders -def _get_cell_specific_widgets(layout, style): +def _get_cell_specific_widgets(layout, style, location): kwargs = dict(layout=layout, style=style) cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] + if location == "distal": + cell_types.remove('L5_basket') + weights_ampa, weights_nmda, delays = dict(), dict(), dict() for cell_type in cell_types: weights_ampa[f'{cell_type}'] = FloatText( @@ -72,7 +75,7 @@ def _get_cell_specific_widgets(layout, style): return widgets_list, widgets_dict -def _get_rhythmic_widget(name, tstop_widget, layout, style): +def _get_rhythmic_widget(name, tstop_widget, layout, style, location): kwargs = dict(layout=layout, style=style) tstart = FloatText(value=0., description='Start time (s)', **kwargs) @@ -85,21 +88,20 @@ def _get_rhythmic_widget(name, tstop_widget, layout, style): burst_std = FloatText(value=0, description='Burst std dev (Hz)', **kwargs) repeats = FloatText(value=1, description='Repeats', **kwargs) seedcore = IntText(value=14, description='Seed', **kwargs) - location = RadioButtons(options=['proximal', 'distal']) - widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) + widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style, location) drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, - repeats, location, seedcore] + widgets_list) + repeats, seedcore] + widgets_list) drive = dict(type='Rhythmic', name=name, tstart=tstart, tstart_std=tstart_std, burst_rate=burst_rate, burst_std=burst_std, repeats=repeats, seedcore=seedcore, - location=location, tstop=tstop) + tstop=tstop) drive.update(widgets_dict) return drive, drive_box -def _get_poisson_widget(name, tstop_widget, layout, style): +def _get_poisson_widget(name, tstop_widget, layout, style, location): tstart = FloatText(value=0.0, description='Start time (s)', layout=layout, style=style) tstop = BoundedFloatText(value=tstop_widget.value, @@ -118,20 +120,21 @@ def _get_poisson_widget(name, tstop_widget, layout, style): value=8.5, description=f'{cell_type}:', layout=layout, style=style) - widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) + widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style, location) widgets_dict.update({'rate_constant': rate_constant}) widgets_list.extend([HTML(value="Rate constants")] + list(widgets_dict['rate_constant'].values())) - drive_box = VBox([tstart, tstop, seedcore, location] + widgets_list) + drive_box = VBox([tstart, tstop, seedcore] + widgets_list) drive = dict(type='Poisson', name=name, tstart=tstart, tstop=tstop, rate_constant=rate_constant, - seedcore=seedcore, location=location) + seedcore=seedcore, + ) drive.update(widgets_dict) return drive, drive_box -def _get_evoked_widget(name, layout, style): +def _get_evoked_widget(name, layout, style, location): kwargs = dict(layout=layout, style=style) mu = FloatText(value=0, description='Mean time:', **kwargs) sigma = FloatText(value=1, description='Std dev time:', @@ -139,38 +142,38 @@ def _get_evoked_widget(name, layout, style): numspikes = IntText(value=1, description='No. Spikes:', **kwargs) seedcore = IntText(value=14, description='Seed: ', **kwargs) - location = RadioButtons(options=['proximal', 'distal']) - widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style) - drive_box = VBox([mu, sigma, numspikes, seedcore, location] + + widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style, location) + + drive_box = VBox([mu, sigma, numspikes, seedcore] + widgets_list) drive = dict(type='Evoked', name=name, mu=mu, sigma=sigma, numspikes=numspikes, - seedcore=seedcore, location=location, + seedcore=seedcore, sync_within_trial=False) drive.update(widgets_dict) return drive, drive_box def add_drive_widget(drive_type, drive_boxes, drive_widgets, - drives_out, tstop_widget): + drives_out, tstop_widget, location): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} drives_out.clear_output() with drives_out: - name = drive_type['new'] + str(len(drive_boxes)) + name = drive_type + str(len(drive_boxes)) - if drive_type['new'] == 'Rhythmic': + if drive_type == 'Rhythmic': drive, drive_box = _get_rhythmic_widget(name, tstop_widget, - layout, style) - elif drive_type['new'] == 'Poisson': + layout, style, location) + elif drive_type == 'Poisson': drive, drive_box = _get_poisson_widget(name, tstop_widget, - layout, style) - elif drive_type['new'] == 'Evoked': - drive, drive_box = _get_evoked_widget(name, layout, style) + layout, style, location) + elif drive_type == 'Evoked': + drive, drive_box = _get_evoked_widget(name, layout, style, location) - if drive_type['new'] in ['Evoked', 'Poisson', 'Rhythmic']: + if drive_type in ['Evoked', 'Poisson', 'Rhythmic']: drive_boxes.append(drive_box) drive_widgets.append(drive) @@ -188,11 +191,11 @@ def update_plot_window(variables, plot_out, plot_type): return with plot_out: - if plot_type['new'] == 'spikes': + if plot_type['new'] == 'spikes': # BUG: got warning from matplotlib and numpy variables['net'].cell_response.plot_spikes_raster() elif plot_type['new'] == 'current dipole': variables['dpls'][0].plot() - elif plot_type['new'] == 'input histogram': + elif plot_type['new'] == 'input histogram': # BUG: got error here variables['net'].cell_response.plot_spikes_hist() elif plot_type['new'] == 'PSD': variables['dpls'][0].plot_psd(fmin=0, fmax=50) @@ -204,7 +207,7 @@ def update_plot_window(variables, plot_out, plot_type): variables['net'].plot_cells() -def on_upload_change(change, sliders, params): +def on_upload_change(change, sliders, params, tstop, tstep, log_out): if len(change['owner'].value) == 0: return @@ -217,16 +220,25 @@ def on_upload_change(change, sliders, params): read_func = {'.json': _read_json, '.param': _read_legacy_params} params_network = read_func[ext](param_data) + log_out.clear_output() + with log_out: + print(f"parameters: {params_network.keys()}") + for slider in sliders: for sl in slider: key = 'gbar_' + sl.description sl.value = params_network[key] + if 'tstop' in params_network.keys(): + tstop.value = params_network['tstop'] + if 'dt' in params_network.keys(): + tstep.value = params_network['dt'] + params.update(params_network) def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, - tstop, params, b): + tstop, ntrials, params, b): """Run the simulation and plot outputs.""" plot_out.clear_output() log_out.clear_output() @@ -234,64 +246,79 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, params['dt'] = tstep.value params['tstop'] = tstop.value variables['net'] = Network(params, add_drives_from_params=False) - for drive in drive_widgets: - weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} - weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} - synaptic_delays = {k: v.value for k, v in drive['delays'].items()} - if drive['type'] == 'Poisson': - rate_constant = {k: v.value for k, v in - drive['rate_constant'].items() if v.value > 0} - weights_ampa = {k: v for k, v in weights_ampa.items() if k in - rate_constant} - weights_nmda = {k: v for k, v in weights_nmda.items() if k in - rate_constant} - variables['net'].add_poisson_drive( - name=drive['name'], - tstart=drive['tstart'].value, - tstop=drive['tstop'].value, - rate_constant=rate_constant, - location=drive['location'].value, - weights_ampa=weights_ampa, - weights_nmda=weights_nmda, - synaptic_delays=synaptic_delays, - space_constant=100.0, - seedcore=drive['seedcore'].value - ) - elif drive['type'] == 'Evoked': - variables['net'].add_evoked_drive( - name=drive['name'], - mu=drive['mu'].value, - sigma=drive['sigma'].value, - numspikes=drive['numspikes'].value, - sync_within_trial=False, - location=drive['location'].value, - weights_ampa=weights_ampa, - weights_nmda=weights_nmda, - synaptic_delays=synaptic_delays, - space_constant=3.0, - seedcore=drive['seedcore'].value - ) - elif drive['type'] == 'Rhythmic': - variables['net'].add_bursty_drive( - name=drive['name'], - tstart=drive['tstart'].value, - tstart_std=drive['tstart_std'].value, - burst_rate=drive['burst_rate'].value, - burst_std=drive['burst_std'].value, - repeats=drive['repeats'].value, - location=drive['location'].value, - tstop=drive['tstop'].value, - weights_ampa=weights_ampa, - weights_nmda=weights_nmda, - synaptic_delays=synaptic_delays, - seedcore=drive['seedcore'].value - ) + + try: + for drive in drive_widgets: + weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} + weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} + synaptic_delays = {k: v.value for k, v in drive['delays'].items()} + if drive['type'] == 'Poisson': + rate_constant = {k: v.value for k, v in + drive['rate_constant'].items() if v.value > 0} + weights_ampa = {k: v for k, v in weights_ampa.items() if k in + rate_constant} + weights_nmda = {k: v for k, v in weights_nmda.items() if k in + rate_constant} + variables['net'].add_poisson_drive( + name=drive['name'], + tstart=drive['tstart'].value, + tstop=drive['tstop'].value, + rate_constant=rate_constant, + location=drive['location'].value, + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, + synaptic_delays=synaptic_delays, + space_constant=100.0, + seedcore=drive['seedcore'].value + ) + elif drive['type'] == 'Evoked': + variables['net'].add_evoked_drive( + name=drive['name'], + mu=drive['mu'].value, + sigma=drive['sigma'].value, + numspikes=drive['numspikes'].value, + # sync_within_trial=False, # BUG it seems this is something unnecessary + location=drive['location'].value, + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, + synaptic_delays=synaptic_delays, + space_constant=3.0, + seedcore=drive['seedcore'].value + ) + elif drive['type'] == 'Rhythmic': + variables['net'].add_bursty_drive( + name=drive['name'], + tstart=drive['tstart'].value, + tstart_std=drive['tstart_std'].value, + burst_rate=drive['burst_rate'].value, + burst_std=drive['burst_std'].value, + # repeats=drive['repeats'].value, # BUG + location=drive['location'].value, + tstop=drive['tstop'].value, + weights_ampa=weights_ampa, + weights_nmda=weights_nmda, + synaptic_delays=synaptic_delays, + seedcore=drive['seedcore'].value + ) + except Exception as e: + with log_out: + print(f"error in reading drives {e}") + with log_out: - variables['dpls'] = simulate_dipole(variables['net'], n_trials=1) + log_out.clear_output() + print("start simulation") + variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, n_trials=ntrials.value) with plot_out: variables['dpls'][0].plot() +def stop_button_clicked(log_out, b): + with log_out: + print("Terminating simulation...") + + pass + + def run_hnn_gui(): """Create the HNN GUI.""" @@ -304,16 +331,16 @@ def run_hnn_gui(): drive_boxes = list() variables = dict(net=None, dpls=None) - def _add_drive_widget(drive_type): - return add_drive_widget(drive_type, drive_boxes, - drive_widgets, drives_out, tstop) - def _run_button_clicked(b): return run_button_clicked(log_out, plot_out, drive_widgets, variables, - tstep, tstop, params, b) + tstep, tstop, ntrials, params, b) + + def _stop_button_clicked(b): + return stop_button_clicked(log_out, b) def _on_upload_change(change): - return log_out.capture(on_upload_change(change, sliders, params)) + return on_upload_change(change, sliders, params, tstop, tstep, log_out) + # return on_upload_change(change, sliders, params) # BUG: capture does not work, use log_out explicitly def _update_plot_window(plot_type): return update_plot_window(variables, plot_out, plot_type) @@ -339,7 +366,8 @@ def _delete_drives_clicked(b): # Simulation parameters tstop = FloatText(value=170, description='tstop (s):', disabled=False) tstep = FloatText(value=0.025, description='tstep (s):', disabled=False) - simulation_box = VBox([tstop, tstep]) + ntrials = IntText(value=1, description='Trials:', disabled=False) + simulation_box = VBox([tstop, tstep, ntrials]) # Sliders to change local-connectivity params sliders = [_get_sliders(params, @@ -363,17 +391,34 @@ def _delete_drives_clicked(b): accordian.set_title(idx, title) # Dropdown for different drives - layout = Layout(width='200px', height='auto') - drives_dropdown = Dropdown( - options=['Evoked', 'Poisson', 'Rhythmic', ''], - value='', description='Drive:', disabled=False, + layout = Layout(width='200px', height='100px') + + drive_type_selection = RadioButtons( + options=['Evoked', 'Poisson', 'Rhythmic'], + value='Evoked', + description='Drive:', + disabled=False, layout=layout) + location_selection = RadioButtons(options=['proximal', 'distal'], + value='proximal', + description='Location', + disabled=False, + layout=layout) + + add_drive_button = create_expanded_button('Add drive', 'primary', height='30px') + + def _add_drive_button_clicked(b): + return add_drive_widget(drive_type_selection.value, drive_boxes, + drive_widgets, drives_out, tstop, + location_selection.value) + + add_drive_button.on_click(_add_drive_button_clicked) + drive_selections = VBox([HBox([drive_type_selection, location_selection]), add_drive_button]) + # XXX: should be simpler to use Stacked class starting # from IPywidgets > 8.0 - interactive(_add_drive_widget, drive_type='Evoked') - drives_dropdown.observe(_add_drive_widget, 'value') - drives_options = VBox([drives_dropdown, drives_out]) + drives_options = VBox([drive_selections, drives_out]) # Tabs for left pane left_tab = Tab() @@ -393,17 +438,29 @@ def _delete_drives_clicked(b): # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') + stop_button = create_expanded_button('Stop', 'danger', height='30px') style = {'button_color': '#8A2BE2', 'font_color': 'white'} load_button = FileUpload(accept='.json,.param', multiple=False, style=style, description='Load network', button_style='success') delete_button = create_expanded_button('Delete drives', 'success', height='30px') + debug_button = create_expanded_button('Debug', 'success', height='30px') + + def _debug_change(b): + with log_out: + log_out.clear_output() + print("file uploaded") + pass + debug_button.on_click(_debug_change) + # load_button.observe(_debug_change) + # run_button.on_click(_debug_change) load_button.observe(_on_upload_change) run_button.on_click(_run_button_clicked) + stop_button.on_click(_stop_button_clicked) delete_button.on_click(_delete_drives_clicked) - footer = HBox([run_button, load_button, delete_button, plot_dropdown]) + footer = HBox([run_button, stop_button, load_button, delete_button, debug_button, plot_dropdown]) right_sidebar = VBox([plot_out, log_out]) diff --git a/hnn_widget.ipynb b/hnn_widget.ipynb index ac0e66cd2..c406faaf2 100644 --- a/hnn_widget.ipynb +++ b/hnn_widget.ipynb @@ -1,5 +1,15 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n" + ] + }, { "cell_type": "code", "execution_count": 1, From 33ccb9503c8dc6800ad772312fb38f6d1d3d92b1 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Wed, 29 Jun 2022 09:23:16 -0400 Subject: [PATCH 47/88] Added MPI, stop button and panel_1 for comparison. --- hnn_core/gui.py | 447 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 303 insertions(+), 144 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index bb77bb47c..329be4a73 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -1,30 +1,37 @@ """IPywidgets GUI.""" # Authors: Mainak Jas - -import json import codecs +import json import os.path as op +import subprocess from functools import partial, update_wrapper +import matplotlib.pyplot as plt import numpy as np +from IPython.display import display +from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, + Dropdown, FileUpload, FloatLogSlider, FloatText, Text, + HBox, IntText, Layout, Output, RadioButtons, Tab, VBox, + fixed, interact, interactive, interactive_output) import hnn_core -from hnn_core import simulate_dipole, read_params, Network -from hnn_core.params import _read_legacy_params, _read_json +from hnn_core import Network, read_params, simulate_dipole, MPIBackend +from hnn_core.params import _read_json, _read_legacy_params +import multiprocessing -from IPython.display import display -from ipywidgets import (FloatLogSlider, Dropdown, Button, RadioButtons, - fixed, interactive_output, interactive, interact, - FloatText, BoundedFloatText, IntText, FileUpload, - HTML, Output, HBox, VBox, Tab, Accordion, - Layout, AppLayout) +def cmd_exists(cmd): + return subprocess.call("type " + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) == 0 def create_expanded_button(description, button_style, height): style = {'button_color': '#8A2BE2'} - return Button(description=description, button_style=button_style, + return Button(description=description, + button_style=button_style, layout=Layout(height=height, width='auto'), style=style) @@ -34,11 +41,17 @@ def _get_sliders(params, param_keys): style = {'description_width': '150px'} sliders = list() for d in param_keys: - slider = FloatLogSlider( - value=params[d], min=-5, max=1, step=0.2, - description=d.split('gbar_')[1], - disabled=False, continuous_update=False, orientation='horizontal', - readout=True, readout_format='.2e', style=style) + slider = FloatLogSlider(value=params[d], + min=-5, + max=1, + step=0.2, + description=d.split('gbar_')[1], + disabled=False, + continuous_update=False, + orientation='horizontal', + readout=True, + readout_format='.2e', + style=style) sliders.append(slider) def _update_params(variables, **updates): @@ -50,22 +63,27 @@ def _update_params(variables, **updates): def _get_cell_specific_widgets(layout, style, location): kwargs = dict(layout=layout, style=style) - cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', - 'L2_basket'] + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] if location == "distal": cell_types.remove('L5_basket') weights_ampa, weights_nmda, delays = dict(), dict(), dict() for cell_type in cell_types: - weights_ampa[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', **kwargs) - weights_nmda[f'{cell_type}'] = FloatText( - value=0., description=f'{cell_type}:', **kwargs) - delays[f'{cell_type}'] = FloatText( - value=0.1, description=f'{cell_type}:', **kwargs) - - widgets_dict = {'weights_ampa': weights_ampa, - 'weights_nmda': weights_nmda, 'delays': delays} + weights_ampa[f'{cell_type}'] = FloatText(value=0., + description=f'{cell_type}:', + **kwargs) + weights_nmda[f'{cell_type}'] = FloatText(value=0., + description=f'{cell_type}:', + **kwargs) + delays[f'{cell_type}'] = FloatText(value=0.1, + description=f'{cell_type}:', + **kwargs) + + widgets_dict = { + 'weights_ampa': weights_ampa, + 'weights_nmda': weights_nmda, + 'delays': delays + } widgets_list = ([HTML(value="AMPA weights")] + list(weights_ampa.values()) + [HTML(value="NMDA weights")] + @@ -79,57 +97,73 @@ def _get_rhythmic_widget(name, tstop_widget, layout, style, location): kwargs = dict(layout=layout, style=style) tstart = FloatText(value=0., description='Start time (s)', **kwargs) - tstart_std = FloatText(value=0, description='Start time dev (s)', - **kwargs) + tstart_std = FloatText(value=0, description='Start time dev (s)', **kwargs) tstop = BoundedFloatText(value=tstop_widget.value, description='Stop time (s)', - max=tstop_widget.value, **kwargs) + max=tstop_widget.value, + **kwargs) burst_rate = FloatText(value=7.5, description='Burst rate (Hz)', **kwargs) burst_std = FloatText(value=0, description='Burst std dev (Hz)', **kwargs) repeats = FloatText(value=1, description='Repeats', **kwargs) seedcore = IntText(value=14, description='Seed', **kwargs) - widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style, location) - drive_box = VBox([tstart, tstart_std, tstop, burst_rate, burst_std, - repeats, seedcore] + widgets_list) - drive = dict(type='Rhythmic', name=name, - tstart=tstart, tstart_std=tstart_std, - burst_rate=burst_rate, burst_std=burst_std, - repeats=repeats, seedcore=seedcore, + widgets_list, widgets_dict = _get_cell_specific_widgets( + layout, style, location) + drive_box = VBox( + [tstart, tstart_std, tstop, burst_rate, burst_std, repeats, seedcore] + + widgets_list) + drive = dict(type='Rhythmic', + name=name, + tstart=tstart, + tstart_std=tstart_std, + burst_rate=burst_rate, + burst_std=burst_std, + repeats=repeats, + seedcore=seedcore, tstop=tstop) drive.update(widgets_dict) return drive, drive_box def _get_poisson_widget(name, tstop_widget, layout, style, location): - tstart = FloatText(value=0.0, description='Start time (s)', - layout=layout, style=style) + tstart = FloatText(value=0.0, + description='Start time (s)', + layout=layout, + style=style) tstop = BoundedFloatText(value=tstop_widget.value, max=tstop_widget.value, description='Stop time (s)', - layout=layout, style=style) - seedcore = IntText(value=14, description='Seed', - layout=layout, style=style) + layout=layout, + style=style) + seedcore = IntText(value=14, + description='Seed', + layout=layout, + style=style) location = RadioButtons(options=['proximal', 'distal']) - cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', - 'L2_basket'] + cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] rate_constant = dict() for cell_type in cell_types: - rate_constant[f'{cell_type}'] = FloatText( - value=8.5, description=f'{cell_type}:', - layout=layout, style=style) + rate_constant[f'{cell_type}'] = FloatText(value=8.5, + description=f'{cell_type}:', + layout=layout, + style=style) - widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style, location) + widgets_list, widgets_dict = _get_cell_specific_widgets( + layout, style, location) widgets_dict.update({'rate_constant': rate_constant}) widgets_list.extend([HTML(value="Rate constants")] + list(widgets_dict['rate_constant'].values())) drive_box = VBox([tstart, tstop, seedcore] + widgets_list) - drive = dict(type='Poisson', name=name, tstart=tstart, - tstop=tstop, rate_constant=rate_constant, - seedcore=seedcore, - ) + drive = dict( + type='Poisson', + name=name, + tstart=tstart, + tstop=tstop, + rate_constant=rate_constant, + seedcore=seedcore, + ) drive.update(widgets_dict) return drive, drive_box @@ -137,26 +171,27 @@ def _get_poisson_widget(name, tstop_widget, layout, style, location): def _get_evoked_widget(name, layout, style, location): kwargs = dict(layout=layout, style=style) mu = FloatText(value=0, description='Mean time:', **kwargs) - sigma = FloatText(value=1, description='Std dev time:', - **kwargs) - numspikes = IntText(value=1, description='No. Spikes:', - **kwargs) + sigma = FloatText(value=1, description='Std dev time:', **kwargs) + numspikes = IntText(value=1, description='No. Spikes:', **kwargs) seedcore = IntText(value=14, description='Seed: ', **kwargs) - widgets_list, widgets_dict = _get_cell_specific_widgets(layout, style, location) + widgets_list, widgets_dict = _get_cell_specific_widgets( + layout, style, location) - drive_box = VBox([mu, sigma, numspikes, seedcore] + - widgets_list) - drive = dict(type='Evoked', name=name, - mu=mu, sigma=sigma, numspikes=numspikes, + drive_box = VBox([mu, sigma, numspikes, seedcore] + widgets_list) + drive = dict(type='Evoked', + name=name, + mu=mu, + sigma=sigma, + numspikes=numspikes, seedcore=seedcore, sync_within_trial=False) drive.update(widgets_dict) return drive, drive_box -def add_drive_widget(drive_type, drive_boxes, drive_widgets, - drives_out, tstop_widget, location): +def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, + tstop_widget, location): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} @@ -165,13 +200,14 @@ def add_drive_widget(drive_type, drive_boxes, drive_widgets, name = drive_type + str(len(drive_boxes)) if drive_type == 'Rhythmic': - drive, drive_box = _get_rhythmic_widget(name, tstop_widget, - layout, style, location) + drive, drive_box = _get_rhythmic_widget(name, tstop_widget, layout, + style, location) elif drive_type == 'Poisson': - drive, drive_box = _get_poisson_widget(name, tstop_widget, - layout, style, location) + drive, drive_box = _get_poisson_widget(name, tstop_widget, layout, + style, location) elif drive_type == 'Evoked': - drive, drive_box = _get_evoked_widget(name, layout, style, location) + drive, drive_box = _get_evoked_widget(name, layout, style, + location) if drive_type in ['Evoked', 'Poisson', 'Rhythmic']: drive_boxes.append(drive_box) @@ -184,27 +220,59 @@ def add_drive_widget(drive_type, drive_boxes, drive_widgets, display(accordion) -def update_plot_window(variables, plot_out, plot_type): - plot_out.clear_output() +def update_plot_window(variables, _plot_out, plot_type): + _plot_out.clear_output() if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'): return - with plot_out: - if plot_type['new'] == 'spikes': # BUG: got warning from matplotlib and numpy - variables['net'].cell_response.plot_spikes_raster() + with _plot_out: + fig, ax = plt.subplots() + if plot_type[ + 'new'] == 'spikes': # BUG: got warning from matplotlib and numpy + if variables['net'] is not None and sum( + [len(_) + for _ in variables['net'].cell_response._spike_times]) > 0: + variables['net'].cell_response.plot_spikes_raster(ax=ax) + else: + print("No data") + elif plot_type['new'] == 'current dipole': - variables['dpls'][0].plot() - elif plot_type['new'] == 'input histogram': # BUG: got error here - variables['net'].cell_response.plot_spikes_hist() + if variables['dpls'] is not None: + variables['dpls'][0].plot(ax=ax) + else: + print("No data") + + elif plot_type['new'] == 'input histogram': + # BUG: got error here, need a better way to handle exception + if variables['net'] is not None and sum( + [len(_) + for _ in variables['net'].cell_response._spike_times]) > 0: + variables['net'].cell_response.plot_spikes_hist(ax=ax) + else: + print("No data") + elif plot_type['new'] == 'PSD': - variables['dpls'][0].plot_psd(fmin=0, fmax=50) + if variables['dpls'] is not None: + variables['dpls'][0].plot_psd(fmin=0, fmax=50, ax=ax) + else: + print("No data") + elif plot_type['new'] == 'spectogram': freqs = np.arange(10., 100., 1.) n_cycles = freqs / 8. - variables['dpls'][0].plot_tfr_morlet(freqs, n_cycles=n_cycles) + if variables['dpls'] is not None: + variables['dpls'][0].plot_tfr_morlet(freqs, + n_cycles=n_cycles, + ax=ax) + else: + print("No data") + elif plot_type['new'] == 'network': - variables['net'].plot_cells() + if variables['net'] is not None: + variables['net'].plot_cells(ax=ax) + else: + print("No data") def on_upload_change(change, sliders, params, tstop, tstep, log_out): @@ -238,7 +306,7 @@ def on_upload_change(change, sliders, params, tstop, tstep, log_out): def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, - tstop, ntrials, params, b): + tstop, ntrials, mpi_cmd, params, b): """Run the simulation and plot outputs.""" plot_out.clear_output() log_out.clear_output() @@ -249,16 +317,28 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, try: for drive in drive_widgets: - weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} - weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} + weights_ampa = { + k: v.value + for k, v in drive['weights_ampa'].items() + } + weights_nmda = { + k: v.value + for k, v in drive['weights_nmda'].items() + } synaptic_delays = {k: v.value for k, v in drive['delays'].items()} if drive['type'] == 'Poisson': - rate_constant = {k: v.value for k, v in - drive['rate_constant'].items() if v.value > 0} - weights_ampa = {k: v for k, v in weights_ampa.items() if k in - rate_constant} - weights_nmda = {k: v for k, v in weights_nmda.items() if k in - rate_constant} + rate_constant = { + k: v.value + for k, v in drive['rate_constant'].items() if v.value > 0 + } + weights_ampa = { + k: v + for k, v in weights_ampa.items() if k in rate_constant + } + weights_nmda = { + k: v + for k, v in weights_nmda.items() if k in rate_constant + } variables['net'].add_poisson_drive( name=drive['name'], tstart=drive['tstart'].value, @@ -269,8 +349,7 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, space_constant=100.0, - seedcore=drive['seedcore'].value - ) + seedcore=drive['seedcore'].value) elif drive['type'] == 'Evoked': variables['net'].add_evoked_drive( name=drive['name'], @@ -283,8 +362,7 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, space_constant=3.0, - seedcore=drive['seedcore'].value - ) + seedcore=drive['seedcore'].value) elif drive['type'] == 'Rhythmic': variables['net'].add_bursty_drive( name=drive['name'], @@ -298,8 +376,7 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, - seedcore=drive['seedcore'].value - ) + seedcore=drive['seedcore'].value) except Exception as e: with log_out: print(f"error in reading drives {e}") @@ -307,18 +384,45 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, with log_out: log_out.clear_output() print("start simulation") - variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, n_trials=ntrials.value) + if cmd_exists(mpi_cmd.value): + # should further allow users to adjust cores to use + # with MPIBackend(n_procs=multiprocessing.cpu_count() - 1, + # mpi_cmd=mpi_cmd.value): + # variables['dpls'] = simulate_dipole(variables['net'], + # tstop=tstop.value, + # n_trials=ntrials.value) + variables['backend'] = MPIBackend( + n_procs=multiprocessing.cpu_count() - 1, mpi_cmd=mpi_cmd.value) + variables['dpls'] = variables['backend'].simulate( + variables['net'], + tstop.value, + tstep.value, + n_trials=ntrials.value) + + else: + variables['dpls'] = simulate_dipole(variables['net'], + tstop=tstop.value, + n_trials=ntrials.value) + + # Default case with plot_out: - variables['dpls'][0].plot() + fig, ax = plt.subplots() + variables['dpls'][0].plot(ax=ax) -def stop_button_clicked(log_out, b): +def stop_button_clicked(variables, log_out, b): with log_out: - print("Terminating simulation...") - - pass + if "backend" in variables: + print("Terminating simulation...") + variables["backend"].terminate() + else: + print("No Backends or running simulations. Cannot terminate") +def test_del_widget(plot_out_1): + del plot_out_1 + pass + def run_hnn_gui(): """Create the HNN GUI.""" @@ -333,10 +437,10 @@ def run_hnn_gui(): def _run_button_clicked(b): return run_button_clicked(log_out, plot_out, drive_widgets, variables, - tstep, tstop, ntrials, params, b) + tstep, tstop, ntrials, mpi_cmd, params, b) def _stop_button_clicked(b): - return stop_button_clicked(log_out, b) + return stop_button_clicked(variables, log_out, b) def _on_upload_change(change): return on_upload_change(change, sliders, params, tstop, tstep, log_out) @@ -345,6 +449,9 @@ def _on_upload_change(change): def _update_plot_window(plot_type): return update_plot_window(variables, plot_out, plot_type) + def _update_plot_window_1(plot_type): + return update_plot_window(variables, plot_out_1, plot_type) + def _delete_drives_clicked(b): drives_out.clear_output() # black magic: the following does not work @@ -353,39 +460,68 @@ def _delete_drives_clicked(b): drive_widgets.pop() drive_boxes.pop() + def _debug_change(b): + test_del_widget(plot_out_1) + + with log_out: + log_out.clear_output() + print("file uploaded") + pass + # Output windows drives_out = Output() # window to add new drives - log_out = Output(layout={'border': '1px solid gray', 'height': '150px', - 'overflow_y': 'auto'}) - plot_out = Output(layout={'border': '1px solid gray', 'height': '350px'}) + log_out = Output(layout={ + 'border': '1px solid gray', + 'height': '150px', + 'overflow_y': 'auto' + }) + height_plot = '350px' + plot_out = Output(layout={ + 'border': '1px solid gray', + 'height': height_plot + }) + plot_out_1 = Output(layout={ + 'border': '1px solid gray', + 'height': height_plot + }) # header_button header_button = create_expanded_button('HUMAN NEOCORTICAL NEUROSOLVER', - 'success', height='40px') + 'success', + height='40px') # Simulation parameters tstop = FloatText(value=170, description='tstop (s):', disabled=False) tstep = FloatText(value=0.025, description='tstep (s):', disabled=False) ntrials = IntText(value=1, description='Trials:', disabled=False) - simulation_box = VBox([tstop, tstep, ntrials]) + mpi_cmd = Text(value='mpiexec', + placeholder='Fill if applies', + description='MPI cmd:', + disabled=False) + simulation_box = VBox([tstop, tstep, ntrials, mpi_cmd]) # Sliders to change local-connectivity params - sliders = [_get_sliders(params, - ['gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', - 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab']), - _get_sliders(params, - ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', - 'gbar_L5Pyr_L5Pyr_ampa', 'gbar_L5Pyr_L5Pyr_nmda', - 'gbar_L5Basket_L5Pyr_gabaa', 'gbar_L5Basket_L5Pyr_gabab']), - _get_sliders(params, - ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), - _get_sliders(params, - ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr'])] + sliders = [ + _get_sliders(params, [ + 'gbar_L2Pyr_L2Pyr_ampa', 'gbar_L2Pyr_L2Pyr_nmda', + 'gbar_L2Basket_L2Pyr_gabaa', 'gbar_L2Basket_L2Pyr_gabab' + ]), + _get_sliders(params, [ + 'gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr', 'gbar_L5Pyr_L5Pyr_ampa', + 'gbar_L5Pyr_L5Pyr_nmda', 'gbar_L5Basket_L5Pyr_gabaa', + 'gbar_L5Basket_L5Pyr_gabab' + ]), + _get_sliders(params, + ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), + _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr']) + ] # accordians to group local-connectivity by cell type boxes = [VBox(slider) for slider in sliders] - titles = ['Layer 2/3 Pyramidal', 'Layer 5 Pyramidal', 'Layer 2 Basket', - 'Layer 5 Basket'] + titles = [ + 'Layer 2/3 Pyramidal', 'Layer 5 Pyramidal', 'Layer 2 Basket', + 'Layer 5 Basket' + ] accordian = Accordion(children=boxes) for idx, title in enumerate(titles): accordian.set_title(idx, title) @@ -406,7 +542,9 @@ def _delete_drives_clicked(b): disabled=False, layout=layout) - add_drive_button = create_expanded_button('Add drive', 'primary', height='30px') + add_drive_button = create_expanded_button('Add drive', + 'primary', + height='30px') def _add_drive_button_clicked(b): return add_drive_widget(drive_type_selection.value, drive_boxes, @@ -414,7 +552,8 @@ def _add_drive_button_clicked(b): location_selection.value) add_drive_button.on_click(_add_drive_button_clicked) - drive_selections = VBox([HBox([drive_type_selection, location_selection]), add_drive_button]) + drive_selections = VBox( + [HBox([drive_type_selection, location_selection]), add_drive_button]) # XXX: should be simpler to use Stacked class starting # from IPywidgets > 8.0 @@ -428,47 +567,67 @@ def _add_drive_button_clicked(b): left_tab.set_title(idx, title) # Dropdown menu to switch between plots - plot_dropdown = Dropdown( - options=['input histogram', 'current dipole', - 'spikes', 'PSD', 'spectogram', 'network'], - value='current dipole', description='Plot:', - disabled=False) + plot_options = [ + 'input histogram', 'current dipole', 'spikes', 'PSD', 'spectogram', + 'network' + ] + plot_dropdown = Dropdown(options=plot_options, + value='current dipole', + description='Plot:', + disabled=False) + interactive(_update_plot_window, plot_type='current dipole') plot_dropdown.observe(_update_plot_window, 'value') + plot_dropdown_1 = Dropdown(options=plot_options, + value='current dipole', + description='Plot:', + disabled=False) + interactive(_update_plot_window_1, plot_type='current dipole') + plot_dropdown_1.observe(_update_plot_window_1, 'value') + # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') stop_button = create_expanded_button('Stop', 'danger', height='30px') style = {'button_color': '#8A2BE2', 'font_color': 'white'} - load_button = FileUpload(accept='.json,.param', multiple=False, - style=style, description='Load network', + load_button = FileUpload(accept='.json,.param', + multiple=False, + style=style, + description='Load network', button_style='success') - delete_button = create_expanded_button('Delete drives', 'success', + delete_button = create_expanded_button('Delete drives', + 'success', height='30px') debug_button = create_expanded_button('Debug', 'success', height='30px') - def _debug_change(b): - with log_out: - log_out.clear_output() - print("file uploaded") - pass + debug_button.on_click(_debug_change) # load_button.observe(_debug_change) # run_button.on_click(_debug_change) load_button.observe(_on_upload_change) run_button.on_click(_run_button_clicked) + # Not working currently stop_button.on_click(_stop_button_clicked) delete_button.on_click(_delete_drives_clicked) - footer = HBox([run_button, stop_button, load_button, delete_button, debug_button, plot_dropdown]) - - right_sidebar = VBox([plot_out, log_out]) + footer = HBox( + [run_button, stop_button, load_button, delete_button, debug_button]) + + plot_dropdown_1.layout.width = "500px" + plot_out.layout.width = "500px" + right_sidebar = VBox([ + HBox([ + VBox([plot_dropdown, plot_out]), + VBox([plot_dropdown_1, plot_out_1]) + ]), log_out + ]) # Final layout of the app - hnn_gui = AppLayout(header=header_button, - left_sidebar=left_tab, - right_sidebar=right_sidebar, - footer=footer, - pane_widths=['380px', '500px', '500px'], - pane_heights=[1, '500px', 1]) + hnn_gui = AppLayout( + header=header_button, + left_sidebar=left_tab, + right_sidebar=right_sidebar, + footer=footer, + pane_widths=['380px', '0px', '1000px'], + pane_heights=[1, '500px', 1]) return hnn_gui From a55e2564848c8c2d0d40b4c39e16926007240a6c Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Wed, 29 Jun 2022 09:34:56 -0400 Subject: [PATCH 48/88] Updated author info & removed unused imports --- hnn_core/gui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 329be4a73..5b7f65ee5 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -1,11 +1,11 @@ """IPywidgets GUI.""" # Authors: Mainak Jas +# Huzi Cheng + import codecs -import json import os.path as op import subprocess -from functools import partial, update_wrapper import matplotlib.pyplot as plt import numpy as np @@ -13,7 +13,7 @@ from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, Dropdown, FileUpload, FloatLogSlider, FloatText, Text, HBox, IntText, Layout, Output, RadioButtons, Tab, VBox, - fixed, interact, interactive, interactive_output) + interactive, interactive_output) import hnn_core from hnn_core import Network, read_params, simulate_dipole, MPIBackend @@ -221,6 +221,7 @@ def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, def update_plot_window(variables, _plot_out, plot_type): + # TODO need to add more informaion about "no data" _plot_out.clear_output() if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'): From 6ea366ec00f9ebfaa4869abc6ed963cce93d6622 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Wed, 29 Jun 2022 09:44:36 -0400 Subject: [PATCH 49/88] Fixed formatting issues --- hnn_core/gui.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 5b7f65ee5..6197dbc8d 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -229,8 +229,8 @@ def update_plot_window(variables, _plot_out, plot_type): with _plot_out: fig, ax = plt.subplots() - if plot_type[ - 'new'] == 'spikes': # BUG: got warning from matplotlib and numpy + + if plot_type['new'] == 'spikes': if variables['net'] is not None and sum( [len(_) for _ in variables['net'].cell_response._spike_times]) > 0: @@ -357,7 +357,8 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, mu=drive['mu'].value, sigma=drive['sigma'].value, numspikes=drive['numspikes'].value, - # sync_within_trial=False, # BUG it seems this is something unnecessary + # sync_within_trial=False, + # BUG it seems this is something unnecessary location=drive['location'].value, weights_ampa=weights_ampa, weights_nmda=weights_nmda, @@ -412,6 +413,7 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, def stop_button_clicked(variables, log_out, b): + # BUG: this cannot work properly now. with log_out: if "backend" in variables: print("Terminating simulation...") @@ -424,6 +426,7 @@ def test_del_widget(plot_out_1): del plot_out_1 pass + def run_hnn_gui(): """Create the HNN GUI.""" @@ -445,7 +448,8 @@ def _stop_button_clicked(b): def _on_upload_change(change): return on_upload_change(change, sliders, params, tstop, tstep, log_out) - # return on_upload_change(change, sliders, params) # BUG: capture does not work, use log_out explicitly + # BUG: capture does not work, use log_out explicitly + # return on_upload_change(change, sliders, params) def _update_plot_window(plot_type): return update_plot_window(variables, plot_out, plot_type) @@ -601,7 +605,6 @@ def _add_drive_button_clicked(b): height='30px') debug_button = create_expanded_button('Debug', 'success', height='30px') - debug_button.on_click(_debug_change) # load_button.observe(_debug_change) @@ -624,11 +627,10 @@ def _add_drive_button_clicked(b): ]) # Final layout of the app - hnn_gui = AppLayout( - header=header_button, - left_sidebar=left_tab, - right_sidebar=right_sidebar, - footer=footer, - pane_widths=['380px', '0px', '1000px'], - pane_heights=[1, '500px', 1]) + hnn_gui = AppLayout(header=header_button, + left_sidebar=left_tab, + right_sidebar=right_sidebar, + footer=footer, + pane_widths=['380px', '0px', '1000px'], + pane_heights=[1, '500px', 1]) return hnn_gui From 6c204181eb6746fb7c04406984931abc1b1b0355 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 30 Jun 2022 12:02:05 -0400 Subject: [PATCH 50/88] 1. fixed bugs in simulation 2. add backend choice 3. layout redesign --- hnn_core/gui.py | 620 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 461 insertions(+), 159 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 6197dbc8d..4524f98be 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -4,28 +4,23 @@ # Huzi Cheng import codecs +import multiprocessing import os.path as op -import subprocess - +import os import matplotlib.pyplot as plt import numpy as np from IPython.display import display from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, - Dropdown, FileUpload, FloatLogSlider, FloatText, Text, - HBox, IntText, Layout, Output, RadioButtons, Tab, VBox, - interactive, interactive_output) + Checkbox, Dropdown, FileUpload, FloatLogSlider, + FloatText, HBox, IntText, Layout, Output, RadioButtons, + Tab, Text, VBox, interactive, interactive_output) import hnn_core -from hnn_core import Network, read_params, simulate_dipole, MPIBackend -from hnn_core.params import _read_json, _read_legacy_params -import multiprocessing - - -def cmd_exists(cmd): - return subprocess.call("type " + cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) == 0 +from hnn_core import (MPIBackend, JoblibBackend, Network, jones_2009_model, + read_params, simulate_dipole) +from hnn_core.params import (_read_json, _read_legacy_params, + _extract_drive_specs_from_hnn_params) +from hnn_core.viz import plot_dipole def create_expanded_button(description, button_style, height): @@ -61,7 +56,35 @@ def _update_params(variables, **updates): return sliders -def _get_cell_specific_widgets(layout, style, location): +def _get_cell_specific_widgets(layout, + style, + location, + data={}, + default_data={ + 'weights_ampa': { + 'L5_pyramidal': 0., + 'L2_pyramidal': 0., + 'L5_basket': 0., + 'L2_basket': 0. + }, + 'weights_nmda': { + 'L5_pyramidal': 0., + 'L2_pyramidal': 0., + 'L5_basket': 0., + 'L2_basket': 0. + }, + 'delays': { + 'L5_pyramidal': 0.1, + 'L2_pyramidal': 0.1, + 'L5_basket': 0.1, + 'L2_basket': 0.1 + }, + }): + + for k in default_data.keys(): + if k in data: + default_data[k].update(data[k]) + kwargs = dict(layout=layout, style=style) cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] if location == "distal": @@ -69,15 +92,18 @@ def _get_cell_specific_widgets(layout, style, location): weights_ampa, weights_nmda, delays = dict(), dict(), dict() for cell_type in cell_types: - weights_ampa[f'{cell_type}'] = FloatText(value=0., - description=f'{cell_type}:', - **kwargs) - weights_nmda[f'{cell_type}'] = FloatText(value=0., - description=f'{cell_type}:', - **kwargs) - delays[f'{cell_type}'] = FloatText(value=0.1, - description=f'{cell_type}:', - **kwargs) + weights_ampa[f'{cell_type}'] = FloatText( + value=default_data['weights_ampa'][cell_type], + description=f'{cell_type}:', + **kwargs) + weights_nmda[f'{cell_type}'] = FloatText( + value=default_data['weights_nmda'][cell_type], + description=f'{cell_type}:', + **kwargs) + delays[f'{cell_type}'] = FloatText( + value=default_data['delays'][cell_type], + description=f'{cell_type}:', + **kwargs) widgets_dict = { 'weights_ampa': weights_ampa, @@ -93,22 +119,62 @@ def _get_cell_specific_widgets(layout, style, location): return widgets_list, widgets_dict -def _get_rhythmic_widget(name, tstop_widget, layout, style, location): - +def _get_rhythmic_widget( + name, + tstop_widget, + layout, + style, + location, + data={}, + default_data={ + 'tstart': 0., + 'tstart_std': 0., + 'tstop': 0., + 'burst_rate': 7.5, + 'burst_std': 0, + 'repeats': 1, + 'seedcore': 14, + }, + default_weights_ampa={}, + default_weights_nmda={}, + default_delays={}, +): + default_data.update(data) kwargs = dict(layout=layout, style=style) - tstart = FloatText(value=0., description='Start time (s)', **kwargs) - tstart_std = FloatText(value=0, description='Start time dev (s)', **kwargs) - tstop = BoundedFloatText(value=tstop_widget.value, + tstart = FloatText(value=default_data['tstart'], + description='Start time (ms)', + **kwargs) + tstart_std = FloatText(value=default_data['tstart_std'], + description='Start time dev (s)', + **kwargs) + tstop = BoundedFloatText(value=tstop_widget.value if default_data['tstop'] + == 0 else default_data['tstop'], description='Stop time (s)', max=tstop_widget.value, **kwargs) - burst_rate = FloatText(value=7.5, description='Burst rate (Hz)', **kwargs) - burst_std = FloatText(value=0, description='Burst std dev (Hz)', **kwargs) - repeats = FloatText(value=1, description='Repeats', **kwargs) - seedcore = IntText(value=14, description='Seed', **kwargs) + burst_rate = FloatText(value=default_data['burst_rate'], + description='Burst rate (Hz)', + **kwargs) + burst_std = FloatText(value=default_data['burst_std'], + description='Burst std dev (Hz)', + **kwargs) + repeats = FloatText(value=default_data['repeats'], + description='Repeats', + **kwargs) + seedcore = IntText(value=default_data['seedcore'], + description='Seed', + **kwargs) widgets_list, widgets_dict = _get_cell_specific_widgets( - layout, style, location) + layout, + style, + location, + data={ + 'weights_ampa': default_weights_ampa, + 'weights_nmda': default_weights_nmda, + 'delays': default_delays, + }, + ) drive_box = VBox( [tstart, tstart_std, tstop, burst_rate, burst_std, repeats, seedcore] + widgets_list) @@ -125,17 +191,40 @@ def _get_rhythmic_widget(name, tstop_widget, layout, style, location): return drive, drive_box -def _get_poisson_widget(name, tstop_widget, layout, style, location): - tstart = FloatText(value=0.0, +def _get_poisson_widget( + name, + tstop_widget, + layout, + style, + location, + data={}, + default_data={ + 'tstart': 0.0, + 'tstop': 0.0, + 'seedcore': 14, + 'rate_constant': { + 'L5_pyramidal': 8.5, + 'L2_pyramidal': 8.5, + 'L5_basket': 8.5, + 'L2_basket': 8.5, + } + }, + default_weights_ampa={}, + default_weights_nmda={}, + default_delays={}, +): + default_data.update(data) + tstart = FloatText(value=default_data['tstart'], description='Start time (s)', layout=layout, style=style) - tstop = BoundedFloatText(value=tstop_widget.value, + tstop = BoundedFloatText(value=tstop_widget.value if default_data['tstop'] + == 0 else default_data['tstop'], max=tstop_widget.value, description='Stop time (s)', layout=layout, style=style) - seedcore = IntText(value=14, + seedcore = IntText(value=default_data['seedcore'], description='Seed', layout=layout, style=style) @@ -144,13 +233,22 @@ def _get_poisson_widget(name, tstop_widget, layout, style, location): cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] rate_constant = dict() for cell_type in cell_types: - rate_constant[f'{cell_type}'] = FloatText(value=8.5, - description=f'{cell_type}:', - layout=layout, - style=style) + rate_constant[f'{cell_type}'] = FloatText( + value=default_data['rate_constant'][cell_type], + description=f'{cell_type}:', + layout=layout, + style=style) widgets_list, widgets_dict = _get_cell_specific_widgets( - layout, style, location) + layout, + style, + location, + data={ + 'weights_ampa': default_weights_ampa, + 'weights_nmda': default_weights_nmda, + 'delays': default_delays, + }, + ) widgets_dict.update({'rate_constant': rate_constant}) widgets_list.extend([HTML(value="Rate constants")] + list(widgets_dict['rate_constant'].values())) @@ -168,15 +266,47 @@ def _get_poisson_widget(name, tstop_widget, layout, style, location): return drive, drive_box -def _get_evoked_widget(name, layout, style, location): +def _get_evoked_widget( + name, + layout, + style, + location, + data={}, + default_data={ + 'mu': 0, + 'sigma': 1, + 'numspikes': 1, + 'seedcore': 14, + }, + default_weights_ampa={}, + default_weights_nmda={}, + default_delays={}, +): + default_data.update(data) kwargs = dict(layout=layout, style=style) - mu = FloatText(value=0, description='Mean time:', **kwargs) - sigma = FloatText(value=1, description='Std dev time:', **kwargs) - numspikes = IntText(value=1, description='No. Spikes:', **kwargs) - seedcore = IntText(value=14, description='Seed: ', **kwargs) + mu = FloatText(value=default_data['mu'], + description='Mean time:', + **kwargs) + sigma = FloatText(value=default_data['sigma'], + description='Std dev time:', + **kwargs) + numspikes = IntText(value=default_data['numspikes'], + description='No. Spikes:', + **kwargs) + seedcore = IntText(value=default_data['seedcore'], + description='Seed: ', + **kwargs) widgets_list, widgets_dict = _get_cell_specific_widgets( - layout, style, location) + layout, + style, + location, + data={ + 'weights_ampa': default_weights_ampa, + 'weights_nmda': default_weights_nmda, + 'delays': default_delays, + }, + ) drive_box = VBox([mu, sigma, numspikes, seedcore] + widgets_list) drive = dict(type='Evoked', @@ -190,34 +320,80 @@ def _get_evoked_widget(name, layout, style, location): return drive, drive_box -def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, - tstop_widget, location): +def add_drive_widget( + drive_type, + drive_boxes, + drive_widgets, + drives_out, + tstop_widget, + location, + prespecified_drive_name=None, + prespecified_drive_data={}, + prespecified_weights_ampa={}, + prespecified_weights_nmda={}, + prespecified_delays={}, + render=True, + expand_last_drive=True, +): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} drives_out.clear_output() with drives_out: - name = drive_type + str(len(drive_boxes)) + if not prespecified_drive_name: + name = drive_type + str(len(drive_boxes)) + else: + name = prespecified_drive_name if drive_type == 'Rhythmic': - drive, drive_box = _get_rhythmic_widget(name, tstop_widget, layout, - style, location) + drive, drive_box = _get_rhythmic_widget( + name, + tstop_widget, + layout, + style, + location, + data=prespecified_drive_data, + default_weights_ampa=prespecified_weights_ampa, + default_weights_nmda=prespecified_weights_nmda, + default_delays=prespecified_delays, + ) elif drive_type == 'Poisson': - drive, drive_box = _get_poisson_widget(name, tstop_widget, layout, - style, location) + drive, drive_box = _get_poisson_widget( + name, + tstop_widget, + layout, + style, + location, + data=prespecified_drive_data, + default_weights_ampa=prespecified_weights_ampa, + default_weights_nmda=prespecified_weights_nmda, + default_delays=prespecified_delays, + ) elif drive_type == 'Evoked': - drive, drive_box = _get_evoked_widget(name, layout, style, - location) + drive, drive_box = _get_evoked_widget( + name, + layout, + style, + location, + data=prespecified_drive_data, + default_weights_ampa=prespecified_weights_ampa, + default_weights_nmda=prespecified_weights_nmda, + default_delays=prespecified_delays, + ) if drive_type in ['Evoked', 'Poisson', 'Rhythmic']: drive_boxes.append(drive_box) drive_widgets.append(drive) - accordion = Accordion(children=drive_boxes, - selected_index=len(drive_boxes) - 1) - for idx, drive in enumerate(drive_widgets): - accordion.set_title(idx, drive['name']) - display(accordion) + if render: + accordion = Accordion( + children=drive_boxes, + selected_index=len(drive_boxes) - + 1 if expand_last_drive else None, + ) + for idx, drive in enumerate(drive_widgets): + accordion.set_title(idx, drive['name']) + display(accordion) def update_plot_window(variables, _plot_out, plot_type): @@ -228,55 +404,50 @@ def update_plot_window(variables, _plot_out, plot_type): return with _plot_out: - fig, ax = plt.subplots() - if plot_type['new'] == 'spikes': - if variables['net'] is not None and sum( - [len(_) - for _ in variables['net'].cell_response._spike_times]) > 0: - variables['net'].cell_response.plot_spikes_raster(ax=ax) - else: - print("No data") + fig, ax = plt.subplots() + variables['net'].cell_response.plot_spikes_raster(ax=ax) elif plot_type['new'] == 'current dipole': - if variables['dpls'] is not None: - variables['dpls'][0].plot(ax=ax) - else: - print("No data") + fig, ax = plt.subplots() + # variables['dpls'][0].plot(ax=ax) + plot_dipole(variables['dpls'], ax=ax, average=True) elif plot_type['new'] == 'input histogram': # BUG: got error here, need a better way to handle exception - if variables['net'] is not None and sum( - [len(_) - for _ in variables['net'].cell_response._spike_times]) > 0: - variables['net'].cell_response.plot_spikes_hist(ax=ax) - else: - print("No data") + fig, ax = plt.subplots() + variables['net'].cell_response.plot_spikes_hist(ax=ax) elif plot_type['new'] == 'PSD': - if variables['dpls'] is not None: - variables['dpls'][0].plot_psd(fmin=0, fmax=50, ax=ax) - else: - print("No data") + fig, ax = plt.subplots() + variables['dpls'][0].plot_psd(fmin=0, fmax=50, ax=ax) elif plot_type['new'] == 'spectogram': freqs = np.arange(10., 100., 1.) n_cycles = freqs / 8. - if variables['dpls'] is not None: - variables['dpls'][0].plot_tfr_morlet(freqs, - n_cycles=n_cycles, - ax=ax) - else: - print("No data") - + fig, ax = plt.subplots() + variables['dpls'][0].plot_tfr_morlet(freqs, + n_cycles=n_cycles, + ax=ax) elif plot_type['new'] == 'network': - if variables['net'] is not None: - variables['net'].plot_cells(ax=ax) - else: - print("No data") - - -def on_upload_change(change, sliders, params, tstop, tstep, log_out): + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + variables['net'].plot_cells(ax=ax) + + +def on_upload_change( + change, + sliders, + params, + tstop, + tstep, + log_out, + variables, + # for adding drives + drive_boxes, + drive_widgets, + drives_out, +): if len(change['owner'].value) == 0: return @@ -305,16 +476,59 @@ def on_upload_change(change, sliders, params, tstop, tstep, log_out): params.update(params_network) + variables['net'] = Network( + params, + add_drives_from_params=False, + ) + + log_out.clear_output() + with log_out: + drive_specs = _extract_drive_specs_from_hnn_params( + variables['net']._params, list(variables['net'].cell_types.keys())) + + # clear before adding drives + drives_out.clear_output() + while len(drive_widgets) > 0: + drive_widgets.pop() + drive_boxes.pop() + + drive_names = sorted(drive_specs.keys()) + for idx, drive_name in enumerate(drive_names): # order matters + specs = drive_specs[drive_name] + print( + f"load drive (drive_name={drive_name}, type={specs['type']})") + print(f"space_constant={specs['space_constant']}") + add_drive_widget( + specs['type'].capitalize(), + drive_boxes, + drive_widgets, + drives_out, + tstop, + specs['location'], + prespecified_drive_name=drive_name, + prespecified_drive_data=specs['dynamics'], + prespecified_weights_ampa=specs['weights_ampa'], + prespecified_weights_nmda=specs['weights_nmda'], + prespecified_delays=specs['synaptic_delays'], + render=idx == len(drive_names) - 1, + expand_last_drive=False, + ) + def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, - tstop, ntrials, mpi_cmd, params, b): + tstop, ntrials, backend_selection, mpi_cmd, + add_drive_from_params, params, b): """Run the simulation and plot outputs.""" plot_out.clear_output() log_out.clear_output() with log_out: params['dt'] = tstep.value params['tstop'] = tstop.value - variables['net'] = Network(params, add_drives_from_params=False) + variables['net'] = Network( + params, + # add_drives_from_params=add_drive_from_params.value, + add_drives_from_params=False, + ) try: for drive in drive_widgets: @@ -386,40 +600,103 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, with log_out: log_out.clear_output() print("start simulation") - if cmd_exists(mpi_cmd.value): - # should further allow users to adjust cores to use - # with MPIBackend(n_procs=multiprocessing.cpu_count() - 1, - # mpi_cmd=mpi_cmd.value): - # variables['dpls'] = simulate_dipole(variables['net'], - # tstop=tstop.value, - # n_trials=ntrials.value) + + variables['net'] = jones_2009_model() + + weights_ampa_d1 = { + 'L2_basket': 0.006562, + 'L2_pyramidal': .000007, + 'L5_pyramidal': 0.142300 + } + weights_nmda_d1 = { + 'L2_basket': 0.019482, + 'L2_pyramidal': 0.004317, + 'L5_pyramidal': 0.080074 + } + synaptic_delays_d1 = { + 'L2_basket': 0.1, + 'L2_pyramidal': 0.1, + 'L5_pyramidal': 0.1 + } + variables['net'].add_evoked_drive('evdist1', + mu=63.53, + sigma=3.85, + numspikes=1, + weights_ampa=weights_ampa_d1, + weights_nmda=weights_nmda_d1, + location='distal', + synaptic_delays=synaptic_delays_d1, + event_seed=4) + + weights_ampa_p1 = { + 'L2_basket': 0.08831, + 'L2_pyramidal': 0.01525, + 'L5_basket': 0.19934, + 'L5_pyramidal': 0.00865 + } + synaptic_delays_prox = { + 'L2_basket': 0.1, + 'L2_pyramidal': 0.1, + 'L5_basket': 1., + 'L5_pyramidal': 1. + } + # all NMDA weights are zero; pass None explicitly + variables['net'].add_evoked_drive('evprox1', + mu=26.61, + sigma=2.47, + numspikes=1, + weights_ampa=weights_ampa_p1, + weights_nmda=None, + location='proximal', + synaptic_delays=synaptic_delays_prox, + event_seed=4) + + # Second proximal evoked drive. NB: only AMPA weights differ from first + weights_ampa_p2 = { + 'L2_basket': 0.000003, + 'L2_pyramidal': 1.438840, + 'L5_basket': 0.008958, + 'L5_pyramidal': 0.684013 + } + # all NMDA weights are zero; omit weights_nmda (defaults to None) + variables['net'].add_evoked_drive('evprox2', + mu=137.12, + sigma=8.33, + numspikes=1, + weights_ampa=weights_ampa_p2, + location='proximal', + synaptic_delays=synaptic_delays_prox, + event_seed=4) + + if backend_selection.value == "MPI": variables['backend'] = MPIBackend( n_procs=multiprocessing.cpu_count() - 1, mpi_cmd=mpi_cmd.value) - variables['dpls'] = variables['backend'].simulate( - variables['net'], - tstop.value, - tstep.value, - n_trials=ntrials.value) - else: + variables['backend'] = JoblibBackend(n_jobs=2) + + with variables['backend']: variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, n_trials=ntrials.value) - # Default case with plot_out: + dpl_smooth_win = 20 + dpl_scalefctr = 12 + for dpl in variables['dpls']: + dpl.smooth(dpl_smooth_win) + dpl.scale(dpl_scalefctr) + fig, ax = plt.subplots() - variables['dpls'][0].plot(ax=ax) + # variables['dpls'][0].plot(ax=ax) + plot_dipole(variables['dpls'], ax=ax, average=True) -def stop_button_clicked(variables, log_out, b): - # BUG: this cannot work properly now. - with log_out: - if "backend" in variables: - print("Terminating simulation...") - variables["backend"].terminate() - else: - print("No Backends or running simulations. Cannot terminate") + +def handle_backend_change(backend_type, mpi_cmd_config, mpi_cmd): + mpi_cmd_config.clear_output() + if backend_type == "MPI": + with mpi_cmd_config: + display(mpi_cmd) def test_del_widget(plot_out_1): @@ -441,13 +718,14 @@ def run_hnn_gui(): def _run_button_clicked(b): return run_button_clicked(log_out, plot_out, drive_widgets, variables, - tstep, tstop, ntrials, mpi_cmd, params, b) + tstep, tstop, ntrials, backend_selection, + mpi_cmd, add_drive_from_params, params, b) - def _stop_button_clicked(b): - return stop_button_clicked(variables, log_out, b) def _on_upload_change(change): - return on_upload_change(change, sliders, params, tstop, tstep, log_out) + return on_upload_change(change, sliders, params, tstop, tstep, log_out, + variables, drive_boxes, drive_widgets, + drives_out) # BUG: capture does not work, use log_out explicitly # return on_upload_change(change, sliders, params) @@ -465,13 +743,7 @@ def _delete_drives_clicked(b): drive_widgets.pop() drive_boxes.pop() - def _debug_change(b): - test_del_widget(plot_out_1) - with log_out: - log_out.clear_output() - print("file uploaded") - pass # Output windows drives_out = Output() # window to add new drives @@ -480,7 +752,8 @@ def _debug_change(b): 'height': '150px', 'overflow_y': 'auto' }) - height_plot = '350px' + + height_plot = '500px' plot_out = Output(layout={ 'border': '1px solid gray', 'height': height_plot @@ -499,11 +772,41 @@ def _debug_change(b): tstop = FloatText(value=170, description='tstop (s):', disabled=False) tstep = FloatText(value=0.025, description='tstep (s):', disabled=False) ntrials = IntText(value=1, description='Trials:', disabled=False) + # temporarily keep this + add_drive_from_params = Checkbox(value=False, + description='Add drives from parameters:', + disabled=False) + + backend_selection = Dropdown( + options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], + value='MPI' if os.getenv("USEMPI", '0') == '1' else 'Joblib', + description='Backend:', + ) + mpi_cmd = Text(value='mpiexec', placeholder='Fill if applies', description='MPI cmd:', disabled=False) - simulation_box = VBox([tstop, tstep, ntrials, mpi_cmd]) + + mpi_cmd_config = Output() + + def _handle_backend_change(backend_type): + return handle_backend_change(backend_type.new, mpi_cmd_config, mpi_cmd) + + handle_backend_change(backend_selection.value, mpi_cmd_config, mpi_cmd) + backend_selection.observe(_handle_backend_change, 'value') + + simulation_box = VBox([ + tstop, + tstep, + ntrials, + add_drive_from_params, + backend_selection, + # mpi_cmd, + mpi_cmd_config + ]) + + # Sliders to change local-connectivity params sliders = [ @@ -593,7 +896,7 @@ def _add_drive_button_clicked(b): # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') - stop_button = create_expanded_button('Stop', 'danger', height='30px') + style = {'button_color': '#8A2BE2', 'font_color': 'white'} load_button = FileUpload(accept='.json,.param', multiple=False, @@ -603,34 +906,33 @@ def _add_drive_button_clicked(b): delete_button = create_expanded_button('Delete drives', 'success', height='30px') - debug_button = create_expanded_button('Debug', 'success', height='30px') - debug_button.on_click(_debug_change) - # load_button.observe(_debug_change) - - # run_button.on_click(_debug_change) load_button.observe(_on_upload_change) run_button.on_click(_run_button_clicked) - # Not working currently - stop_button.on_click(_stop_button_clicked) - delete_button.on_click(_delete_drives_clicked) - footer = HBox( - [run_button, stop_button, load_button, delete_button, debug_button]) - plot_dropdown_1.layout.width = "500px" - plot_out.layout.width = "500px" - right_sidebar = VBox([ + delete_button.on_click(_delete_drives_clicked) + footer = VBox([ HBox([ - VBox([plot_dropdown, plot_out]), - VBox([plot_dropdown_1, plot_out_1]) + run_button, load_button, delete_button, ]), log_out ]) + plot_out_width = "500px" + plot_out.layout.width = plot_out_width + plot_out_1.layout.width = plot_out_width + + plotting_panels = HBox( + [VBox([plot_dropdown, plot_out]), + VBox([plot_dropdown_1, plot_out_1])]) + # Final layout of the app - hnn_gui = AppLayout(header=header_button, - left_sidebar=left_tab, - right_sidebar=right_sidebar, - footer=footer, - pane_widths=['380px', '0px', '1000px'], - pane_heights=[1, '500px', 1]) + hnn_gui = AppLayout( + header=header_button, + left_sidebar=left_tab, + right_sidebar=plotting_panels, + footer=footer, + # pane_widths=['380px', '0px', '1000px'], + pane_widths=['380px', '0', '1'], + pane_heights=['50px', '500px', '200px'], + ) return hnn_gui From 0d5ddef140956e2990b448c7c9d763e870414939 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Fri, 1 Jul 2022 02:57:19 -0400 Subject: [PATCH 51/88] Added configurable visualization output --- hnn_core/gui.py | 294 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 214 insertions(+), 80 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 4524f98be..22207042c 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -6,14 +6,15 @@ import codecs import multiprocessing import os.path as op +import numpy as np import os import matplotlib.pyplot as plt -import numpy as np from IPython.display import display from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, Checkbox, Dropdown, FileUpload, FloatLogSlider, FloatText, HBox, IntText, Layout, Output, RadioButtons, - Tab, Text, VBox, interactive, interactive_output) + Tab, Text, VBox, interactive, interactive_output, + GridspecLayout) import hnn_core from hnn_core import (MPIBackend, JoblibBackend, Network, jones_2009_model, @@ -396,10 +397,13 @@ def add_drive_widget( display(accordion) +def _debug_update_plot_window(variables, _plot_out, plot_type, idx): + update_plot_window(variables, _plot_out, plot_type) + + def update_plot_window(variables, _plot_out, plot_type): # TODO need to add more informaion about "no data" _plot_out.clear_output() - if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'): return @@ -515,11 +519,11 @@ def on_upload_change( ) -def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, - tstop, ntrials, backend_selection, mpi_cmd, - add_drive_from_params, params, b): +def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, + ntrials, backend_selection, mpi_cmd, + add_drive_from_params, params, plot_outputs_list, + plot_dropdowns_list, b): """Run the simulation and plot outputs.""" - plot_out.clear_output() log_out.clear_output() with log_out: params['dt'] = tstep.value @@ -679,17 +683,21 @@ def run_button_clicked(log_out, plot_out, drive_widgets, variables, tstep, tstop=tstop.value, n_trials=ntrials.value) - with plot_out: - dpl_smooth_win = 20 - dpl_scalefctr = 12 - for dpl in variables['dpls']: - dpl.smooth(dpl_smooth_win) - dpl.scale(dpl_scalefctr) - - fig, ax = plt.subplots() - # variables['dpls'][0].plot(ax=ax) - plot_dipole(variables['dpls'], ax=ax, average=True) + dpl_smooth_win = 20 + dpl_scalefctr = 12 + for dpl in variables['dpls']: + dpl.smooth(dpl_smooth_win) + dpl.scale(dpl_scalefctr) + for idx in range(len(plot_outputs_list)): + with log_out: + print(f"updating {idx}") + update_plot_window( + variables, plot_outputs_list[idx], { + "type": "change", + "name": "value", + "new": plot_dropdowns_list[idx].value + }) def handle_backend_change(backend_type, mpi_cmd_config, mpi_cmd): @@ -699,9 +707,144 @@ def handle_backend_change(backend_type, mpi_cmd_config, mpi_cmd): display(mpi_cmd) -def test_del_widget(plot_out_1): - del plot_out_1 - pass +def init_LR_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + border='1px solid gray'): + height_plot = window_height + plot_outputs_L = Output(layout={'border': border, 'height': height_plot}) + + plot_dropdown_L = Dropdown( + options=plot_options, + value=plot_options[0], + description='Plot:', + disabled=False, + ) + plot_dropdown_L.observe( + lambda plot_type: _debug_update_plot_window( + variables, + plot_outputs_L, + plot_type, + "Left", + ), + 'value', + ) + + plot_outputs.append(plot_outputs_L) + plot_dropdowns.append(plot_dropdown_L) + + plot_outputs_R = Output(layout={'border': border, 'height': height_plot}) + + plot_dropdown_R = Dropdown( + options=plot_options, + value=plot_options[1], + description='Plot:', + disabled=False, + ) + plot_dropdown_R.observe( + lambda plot_type: _debug_update_plot_window( + variables, + plot_outputs_R, + plot_type, + "Right", + ), + 'value', + ) + plot_outputs.append(plot_outputs_R) + plot_dropdowns.append(plot_dropdown_R) + + grid = GridspecLayout(1, 2, height=window_height) + grid[0, 0] = VBox([plot_dropdown_L, plot_outputs_L]) + grid[0, 1] = VBox([plot_dropdown_R, plot_outputs_R]) + return grid + + +def init_UD_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + border='1px solid gray'): + height_plot = window_height + plot_outputs_U = Output(layout={ + 'border': border, + 'height': f"{float(height_plot[:-2])/2}px" + }) + + plot_dropdown_U = Dropdown( + options=plot_options, + value=plot_options[0], + description='Plot:', + disabled=False, + ) + plot_dropdown_U.observe( + lambda plot_type: _debug_update_plot_window( + variables, + plot_outputs_U, + plot_type, + "Left", + ), + 'value', + ) + + plot_outputs.append(plot_outputs_U) + plot_dropdowns.append(plot_dropdown_U) + + # Down + plot_outputs_D = Output(layout={'border': border, 'height': height_plot}) + + plot_dropdown_D = Dropdown( + options=plot_options, + value=plot_options[1], + description='Plot:', + disabled=False, + ) + plot_dropdown_D.observe( + lambda plot_type: _debug_update_plot_window( + variables, + plot_outputs_D, + plot_type, + "Right", + ), + 'value', + ) + plot_outputs.append(plot_outputs_D) + plot_dropdowns.append(plot_dropdown_D) + + grid = GridspecLayout(2, 1, height=window_height) + grid[0, 0] = VBox([plot_dropdown_U, plot_outputs_U]) + grid[1, 0] = VBox([plot_dropdown_D, plot_outputs_D]) + return grid + + +def initialize_viz_window(viz_window, + variables, + plot_outputs, + plot_dropdowns, + window_width, + window_height, + layout_option="L-R"): + plot_options = [ + 'current dipole', 'input histogram', 'spikes', 'PSD', 'spectogram', + 'network' + ] + viz_window.clear_output() + while len(plot_outputs) > 0: + plot_outputs.pop() + plot_dropdowns.pop() + + with viz_window: + # L-R configurations + if layout_option == "L-R": + grid = init_LR_viz_layout(plot_outputs, plot_dropdowns, + window_height, variables, plot_options) + elif layout_option == "U-D": + grid = init_UD_viz_layout(plot_outputs, plot_dropdowns, + window_height, variables, plot_options) + + display(grid) def run_hnn_gui(): @@ -716,11 +859,14 @@ def run_hnn_gui(): drive_boxes = list() variables = dict(net=None, dpls=None) - def _run_button_clicked(b): - return run_button_clicked(log_out, plot_out, drive_widgets, variables, - tstep, tstop, ntrials, backend_selection, - mpi_cmd, add_drive_from_params, params, b) + plot_outputs_list = list() + plot_dropdowns_list = list() + def _run_button_clicked(b): + return run_button_clicked(log_out, drive_widgets, variables, tstep, + tstop, ntrials, backend_selection, mpi_cmd, + add_drive_from_params, params, + plot_outputs_list, plot_dropdowns_list, b) def _on_upload_change(change): return on_upload_change(change, sliders, params, tstop, tstep, log_out, @@ -729,12 +875,6 @@ def _on_upload_change(change): # BUG: capture does not work, use log_out explicitly # return on_upload_change(change, sliders, params) - def _update_plot_window(plot_type): - return update_plot_window(variables, plot_out, plot_type) - - def _update_plot_window_1(plot_type): - return update_plot_window(variables, plot_out_1, plot_type) - def _delete_drives_clicked(b): drives_out.clear_output() # black magic: the following does not work @@ -743,8 +883,6 @@ def _delete_drives_clicked(b): drive_widgets.pop() drive_boxes.pop() - - # Output windows drives_out = Output() # window to add new drives log_out = Output(layout={ @@ -752,15 +890,11 @@ def _delete_drives_clicked(b): 'height': '150px', 'overflow_y': 'auto' }) - - height_plot = '500px' - plot_out = Output(layout={ - 'border': '1px solid gray', - 'height': height_plot - }) - plot_out_1 = Output(layout={ - 'border': '1px solid gray', - 'height': height_plot + viz_width = "1000px" + viz_height = "500px" + viz_window = Output(layout={ + 'height': viz_height, + 'width': viz_width, }) # header_button @@ -769,14 +903,43 @@ def _delete_drives_clicked(b): height='40px') # Simulation parameters - tstop = FloatText(value=170, description='tstop (s):', disabled=False) - tstep = FloatText(value=0.025, description='tstep (s):', disabled=False) + tstop = FloatText(value=170, description='tstop (ms):', disabled=False) + tstep = FloatText(value=0.025, description='tstep (ms):', disabled=False) ntrials = IntText(value=1, description='Trials:', disabled=False) # temporarily keep this add_drive_from_params = Checkbox(value=False, description='Add drives from parameters:', disabled=False) + viz_layout_selection = Dropdown( + options=[('Horizontal', 'L-R'), ('Vertical', 'U-D')], + value='L-R', + description='Layout:', + ) + # initialize + initialize_viz_window( + viz_window, + variables, + plot_outputs_list, + plot_dropdowns_list, + viz_width, + viz_height, + layout_option=viz_layout_selection.value, + ) + + def handle_viz_layout_change(layout_option): + return initialize_viz_window( + viz_window, + variables, + plot_outputs_list, + plot_dropdowns_list, + viz_width, + viz_height, + layout_option=layout_option.new, + ) + + viz_layout_selection.observe(handle_viz_layout_change, 'value') + backend_selection = Dropdown( options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], value='MPI' if os.getenv("USEMPI", '0') == '1' else 'Joblib', @@ -802,12 +965,9 @@ def _handle_backend_change(backend_type): ntrials, add_drive_from_params, backend_selection, - # mpi_cmd, - mpi_cmd_config + mpi_cmd_config, ]) - - # Sliders to change local-connectivity params sliders = [ _get_sliders(params, [ @@ -874,26 +1034,6 @@ def _add_drive_button_clicked(b): for idx, title in enumerate(titles): left_tab.set_title(idx, title) - # Dropdown menu to switch between plots - plot_options = [ - 'input histogram', 'current dipole', 'spikes', 'PSD', 'spectogram', - 'network' - ] - plot_dropdown = Dropdown(options=plot_options, - value='current dipole', - description='Plot:', - disabled=False) - - interactive(_update_plot_window, plot_type='current dipole') - plot_dropdown.observe(_update_plot_window, 'value') - - plot_dropdown_1 = Dropdown(options=plot_options, - value='current dipole', - description='Plot:', - disabled=False) - interactive(_update_plot_window_1, plot_type='current dipole') - plot_dropdown_1.observe(_update_plot_window_1, 'value') - # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') @@ -911,28 +1051,22 @@ def _add_drive_button_clicked(b): run_button.on_click(_run_button_clicked) delete_button.on_click(_delete_drives_clicked) + left_width = '380px' footer = VBox([ HBox([ - run_button, load_button, delete_button, + HBox([run_button, load_button, delete_button], + layout={"width": left_width}), + viz_layout_selection, ]), log_out ]) - plot_out_width = "500px" - plot_out.layout.width = plot_out_width - plot_out_1.layout.width = plot_out_width - - plotting_panels = HBox( - [VBox([plot_dropdown, plot_out]), - VBox([plot_dropdown_1, plot_out_1])]) - # Final layout of the app hnn_gui = AppLayout( header=header_button, left_sidebar=left_tab, - right_sidebar=plotting_panels, + right_sidebar=viz_window, footer=footer, - # pane_widths=['380px', '0px', '1000px'], - pane_widths=['380px', '0', '1'], - pane_heights=['50px', '500px', '200px'], + pane_widths=[left_width, '0px', viz_width], + pane_heights=['50px', viz_height, '200px'], ) return hnn_gui From 4015eb751377fdeecf4762682d547bcb2a6b8e08 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Fri, 1 Jul 2022 03:18:29 -0400 Subject: [PATCH 52/88] Removed debug model --- hnn_core/gui.py | 85 +++---------------------------------------------- 1 file changed, 5 insertions(+), 80 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 22207042c..b8aaf91c0 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -533,17 +533,12 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, # add_drives_from_params=add_drive_from_params.value, add_drives_from_params=False, ) + # add connections here + - try: for drive in drive_widgets: - weights_ampa = { - k: v.value - for k, v in drive['weights_ampa'].items() - } - weights_nmda = { - k: v.value - for k, v in drive['weights_nmda'].items() - } + weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} + weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} synaptic_delays = {k: v.value for k, v in drive['delays'].items()} if drive['type'] == 'Poisson': rate_constant = { @@ -597,81 +592,11 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, seedcore=drive['seedcore'].value) - except Exception as e: - with log_out: - print(f"error in reading drives {e}") + log_out.clear_output() with log_out: - log_out.clear_output() print("start simulation") - variables['net'] = jones_2009_model() - - weights_ampa_d1 = { - 'L2_basket': 0.006562, - 'L2_pyramidal': .000007, - 'L5_pyramidal': 0.142300 - } - weights_nmda_d1 = { - 'L2_basket': 0.019482, - 'L2_pyramidal': 0.004317, - 'L5_pyramidal': 0.080074 - } - synaptic_delays_d1 = { - 'L2_basket': 0.1, - 'L2_pyramidal': 0.1, - 'L5_pyramidal': 0.1 - } - variables['net'].add_evoked_drive('evdist1', - mu=63.53, - sigma=3.85, - numspikes=1, - weights_ampa=weights_ampa_d1, - weights_nmda=weights_nmda_d1, - location='distal', - synaptic_delays=synaptic_delays_d1, - event_seed=4) - - weights_ampa_p1 = { - 'L2_basket': 0.08831, - 'L2_pyramidal': 0.01525, - 'L5_basket': 0.19934, - 'L5_pyramidal': 0.00865 - } - synaptic_delays_prox = { - 'L2_basket': 0.1, - 'L2_pyramidal': 0.1, - 'L5_basket': 1., - 'L5_pyramidal': 1. - } - # all NMDA weights are zero; pass None explicitly - variables['net'].add_evoked_drive('evprox1', - mu=26.61, - sigma=2.47, - numspikes=1, - weights_ampa=weights_ampa_p1, - weights_nmda=None, - location='proximal', - synaptic_delays=synaptic_delays_prox, - event_seed=4) - - # Second proximal evoked drive. NB: only AMPA weights differ from first - weights_ampa_p2 = { - 'L2_basket': 0.000003, - 'L2_pyramidal': 1.438840, - 'L5_basket': 0.008958, - 'L5_pyramidal': 0.684013 - } - # all NMDA weights are zero; omit weights_nmda (defaults to None) - variables['net'].add_evoked_drive('evprox2', - mu=137.12, - sigma=8.33, - numspikes=1, - weights_ampa=weights_ampa_p2, - location='proximal', - synaptic_delays=synaptic_delays_prox, - event_seed=4) - if backend_selection.value == "MPI": variables['backend'] = MPIBackend( n_procs=multiprocessing.cpu_count() - 1, mpi_cmd=mpi_cmd.value) From 76eff426dcb2eb6ea2c726831d6c44b24900db8b Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 2 Jul 2022 01:51:55 -0400 Subject: [PATCH 53/88] Fixed some bugs --- hnn_core/gui.py | 213 ++++++++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 98 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index b8aaf91c0..97319a704 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -11,14 +11,13 @@ import matplotlib.pyplot as plt from IPython.display import display from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, - Checkbox, Dropdown, FileUpload, FloatLogSlider, - FloatText, HBox, IntText, Layout, Output, RadioButtons, - Tab, Text, VBox, interactive, interactive_output, - GridspecLayout) + Dropdown, FileUpload, FloatLogSlider, FloatText, HBox, + IntText, Layout, Output, RadioButtons, Tab, Text, VBox, + interactive_output, GridspecLayout) import hnn_core -from hnn_core import (MPIBackend, JoblibBackend, Network, jones_2009_model, - read_params, simulate_dipole) +from hnn_core import (MPIBackend, JoblibBackend, Network, read_params, + simulate_dipole) from hnn_core.params import (_read_json, _read_legacy_params, _extract_drive_specs_from_hnn_params) from hnn_core.viz import plot_dipole @@ -146,11 +145,11 @@ def _get_rhythmic_widget( description='Start time (ms)', **kwargs) tstart_std = FloatText(value=default_data['tstart_std'], - description='Start time dev (s)', + description='Start time dev (ms)', **kwargs) tstop = BoundedFloatText(value=tstop_widget.value if default_data['tstop'] == 0 else default_data['tstop'], - description='Stop time (s)', + description='Stop time (ms)', max=tstop_widget.value, **kwargs) burst_rate = FloatText(value=default_data['burst_rate'], @@ -187,6 +186,7 @@ def _get_rhythmic_widget( burst_std=burst_std, repeats=repeats, seedcore=seedcore, + location=location, tstop=tstop) drive.update(widgets_dict) return drive, drive_box @@ -216,20 +216,19 @@ def _get_poisson_widget( ): default_data.update(data) tstart = FloatText(value=default_data['tstart'], - description='Start time (s)', + description='Start time (ms)', layout=layout, style=style) tstop = BoundedFloatText(value=tstop_widget.value if default_data['tstop'] == 0 else default_data['tstop'], max=tstop_widget.value, - description='Stop time (s)', + description='Stop time (ms)', layout=layout, style=style) seedcore = IntText(value=default_data['seedcore'], description='Seed', layout=layout, style=style) - location = RadioButtons(options=['proximal', 'distal']) cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] rate_constant = dict() @@ -262,6 +261,7 @@ def _get_poisson_widget( tstop=tstop, rate_constant=rate_constant, seedcore=seedcore, + location=location, # notice this is a widget but a str! ) drive.update(widgets_dict) return drive, drive_box @@ -316,6 +316,7 @@ def _get_evoked_widget( sigma=sigma, numspikes=numspikes, seedcore=seedcore, + location=location, sync_within_trial=False) drive.update(widgets_dict) return drive, drive_box @@ -335,11 +336,14 @@ def add_drive_widget( prespecified_delays={}, render=True, expand_last_drive=True, + event_seed=14, ): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} drives_out.clear_output() + + prespecified_drive_data.update({"seedcore": max(event_seed, 2)}) with drives_out: if not prespecified_drive_name: name = drive_type + str(len(drive_boxes)) @@ -382,7 +386,11 @@ def add_drive_widget( default_delays=prespecified_delays, ) - if drive_type in ['Evoked', 'Poisson', 'Rhythmic']: + if drive_type in [ + 'Evoked', + 'Poisson', + 'Rhythmic', + ]: drive_boxes.append(drive_box) drive_widgets.append(drive) @@ -402,7 +410,6 @@ def _debug_update_plot_window(variables, _plot_out, plot_type, idx): def update_plot_window(variables, _plot_out, plot_type): - # TODO need to add more informaion about "no data" _plot_out.clear_output() if not (plot_type['type'] == 'change' and plot_type['name'] == 'value'): return @@ -439,52 +446,11 @@ def update_plot_window(variables, _plot_out, plot_type): variables['net'].plot_cells(ax=ax) -def on_upload_change( - change, - sliders, - params, - tstop, - tstep, - log_out, - variables, - # for adding drives - drive_boxes, - drive_widgets, - drives_out, -): - if len(change['owner'].value) == 0: - return - - params_fname = change['owner'].metadata[0]['name'] - file_uploaded = change['owner'].value - param_data = list(file_uploaded.values())[0]['content'] - param_data = codecs.decode(param_data, encoding="utf-8") - - ext = op.splitext(params_fname)[1] - read_func = {'.json': _read_json, '.param': _read_legacy_params} - params_network = read_func[ext](param_data) - - log_out.clear_output() - with log_out: - print(f"parameters: {params_network.keys()}") - - for slider in sliders: - for sl in slider: - key = 'gbar_' + sl.description - sl.value = params_network[key] - - if 'tstop' in params_network.keys(): - tstop.value = params_network['tstop'] - if 'dt' in params_network.keys(): - tstep.value = params_network['dt'] - - params.update(params_network) - - variables['net'] = Network( - params, - add_drives_from_params=False, - ) - +def load_drives(variables, params, log_out, drives_out, drive_widgets, + drive_boxes, tstop): + """Add drive ipywidgets from params. + """ + variables['net'] = Network(params) log_out.clear_output() with log_out: drive_specs = _extract_drive_specs_from_hnn_params( @@ -499,9 +465,13 @@ def on_upload_change( drive_names = sorted(drive_specs.keys()) for idx, drive_name in enumerate(drive_names): # order matters specs = drive_specs[drive_name] - print( - f"load drive (drive_name={drive_name}, type={specs['type']})") - print(f"space_constant={specs['space_constant']}") + print(f"""load drive: + (name={drive_name}, + type={specs['type']}, + seed={specs['event_seed']}, + space_constant={specs['space_constant']} + ) + """) add_drive_widget( specs['type'].capitalize(), drive_boxes, @@ -516,30 +486,80 @@ def on_upload_change( prespecified_delays=specs['synaptic_delays'], render=idx == len(drive_names) - 1, expand_last_drive=False, + event_seed=specs['event_seed'], ) +def on_upload_change( + change, + sliders, + params, + tstop, + tstep, + log_out, + variables, + # for adding drives + drive_boxes, + drive_widgets, + drives_out, +): + if len(change['owner'].value) == 0: + return + + params_fname = change['owner'].metadata[0]['name'] + file_uploaded = change['owner'].value + param_data = list(file_uploaded.values())[0]['content'] + param_data = codecs.decode(param_data, encoding="utf-8") + + ext = op.splitext(params_fname)[1] + read_func = {'.json': _read_json, '.param': _read_legacy_params} + params_network = read_func[ext](param_data) + + log_out.clear_output() + with log_out: + print(f"parameter key: {params_network.keys()}") + for slider in sliders: + for sl in slider: + key = 'gbar_' + sl.description + sl.value = params_network[key] + + if 'tstop' in params_network.keys(): + tstop.value = params_network['tstop'] + if 'dt' in params_network.keys(): + tstep.value = params_network['dt'] + + params.update(params_network) + load_drives(variables, params, log_out, drives_out, drive_widgets, + drive_boxes, tstop) + + def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, - ntrials, backend_selection, mpi_cmd, - add_drive_from_params, params, plot_outputs_list, - plot_dropdowns_list, b): + ntrials, backend_selection, mpi_cmd, params, + plot_outputs_list, plot_dropdowns_list, b): """Run the simulation and plot outputs.""" log_out.clear_output() with log_out: + print(f"drive_widgets length={len(drive_widgets)}") params['dt'] = tstep.value params['tstop'] = tstop.value variables['net'] = Network( params, - # add_drives_from_params=add_drive_from_params.value, add_drives_from_params=False, ) - # add connections here - - + # add drives to network for drive in drive_widgets: - weights_ampa = {k: v.value for k, v in drive['weights_ampa'].items()} - weights_nmda = {k: v.value for k, v in drive['weights_nmda'].items()} + print(drive['type'], drive['name'], drive['seedcore'].value) + weights_ampa = { + k: v.value + for k, v in drive['weights_ampa'].items() + } + weights_nmda = { + k: v.value + for k, v in drive['weights_nmda'].items() + } synaptic_delays = {k: v.value for k, v in drive['delays'].items()} + print( + f"drive type is {drive['type']}, location={drive['location']}") if drive['type'] == 'Poisson': rate_constant = { k: v.value @@ -558,26 +578,24 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, tstart=drive['tstart'].value, tstop=drive['tstop'].value, rate_constant=rate_constant, - location=drive['location'].value, + location=drive['location'], weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, space_constant=100.0, - seedcore=drive['seedcore'].value) + event_seed=drive['seedcore'].value) elif drive['type'] == 'Evoked': variables['net'].add_evoked_drive( name=drive['name'], mu=drive['mu'].value, sigma=drive['sigma'].value, numspikes=drive['numspikes'].value, - # sync_within_trial=False, - # BUG it seems this is something unnecessary - location=drive['location'].value, + location=drive['location'], weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, space_constant=3.0, - seedcore=drive['seedcore'].value) + event_seed=drive['seedcore'].value) elif drive['type'] == 'Rhythmic': variables['net'].add_bursty_drive( name=drive['name'], @@ -585,18 +603,14 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, tstart_std=drive['tstart_std'].value, burst_rate=drive['burst_rate'].value, burst_std=drive['burst_std'].value, - # repeats=drive['repeats'].value, # BUG - location=drive['location'].value, + location=drive['location'], tstop=drive['tstop'].value, weights_ampa=weights_ampa, weights_nmda=weights_nmda, synaptic_delays=synaptic_delays, - seedcore=drive['seedcore'].value) + event_seed=drive['seedcore'].value) - log_out.clear_output() - with log_out: print("start simulation") - if backend_selection.value == "MPI": variables['backend'] = MPIBackend( n_procs=multiprocessing.cpu_count() - 1, mpi_cmd=mpi_cmd.value) @@ -608,15 +622,14 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, tstop=tstop.value, n_trials=ntrials.value) - dpl_smooth_win = 20 - dpl_scalefctr = 12 + window_len, scaling_factor = 30, 3000 for dpl in variables['dpls']: - dpl.smooth(dpl_smooth_win) - dpl.scale(dpl_scalefctr) + dpl.smooth(window_len).scale(scaling_factor) + # Update all plotting panels for idx in range(len(plot_outputs_list)): with log_out: - print(f"updating {idx}") + print(f"updating panel {idx}") update_plot_window( variables, plot_outputs_list[idx], { "type": "change", @@ -761,13 +774,15 @@ def initialize_viz_window(viz_window, plot_dropdowns.pop() with viz_window: - # L-R configurations + # Left-Rright configuration if layout_option == "L-R": grid = init_LR_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, plot_options) + # Upper-Down configuration elif layout_option == "U-D": grid = init_UD_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, plot_options) + # TODO: 2x2 grids display(grid) @@ -790,8 +805,8 @@ def run_hnn_gui(): def _run_button_clicked(b): return run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, ntrials, backend_selection, mpi_cmd, - add_drive_from_params, params, - plot_outputs_list, plot_dropdowns_list, b) + params, plot_outputs_list, + plot_dropdowns_list, b) def _on_upload_change(change): return on_upload_change(change, sliders, params, tstop, tstep, log_out, @@ -810,9 +825,11 @@ def _delete_drives_clicked(b): # Output windows drives_out = Output() # window to add new drives + + log_out_heiht = "100px" log_out = Output(layout={ 'border': '1px solid gray', - 'height': '150px', + 'height': log_out_heiht, 'overflow_y': 'auto' }) viz_width = "1000px" @@ -832,9 +849,6 @@ def _delete_drives_clicked(b): tstep = FloatText(value=0.025, description='tstep (ms):', disabled=False) ntrials = IntText(value=1, description='Trials:', disabled=False) # temporarily keep this - add_drive_from_params = Checkbox(value=False, - description='Add drives from parameters:', - disabled=False) viz_layout_selection = Dropdown( options=[('Horizontal', 'L-R'), ('Vertical', 'U-D')], @@ -888,7 +902,6 @@ def _handle_backend_change(backend_type): tstop, tstep, ntrials, - add_drive_from_params, backend_selection, mpi_cmd_config, ]) @@ -923,7 +936,7 @@ def _handle_backend_change(backend_type): layout = Layout(width='200px', height='100px') drive_type_selection = RadioButtons( - options=['Evoked', 'Poisson', 'Rhythmic'], + options=['Evoked', 'Poisson', 'Rhythmic', 'Bursty'], value='Evoked', description='Drive:', disabled=False, @@ -985,6 +998,10 @@ def _add_drive_button_clicked(b): ]), log_out ]) + # initialize drive ipywidgets + load_drives(variables, params, log_out, drives_out, drive_widgets, + drive_boxes, tstop) + # Final layout of the app hnn_gui = AppLayout( header=header_button, @@ -992,6 +1009,6 @@ def _add_drive_button_clicked(b): right_sidebar=viz_window, footer=footer, pane_widths=[left_width, '0px', viz_width], - pane_heights=['50px', viz_height, '200px'], + pane_heights=['50px', viz_height, "1"], ) return hnn_gui From 9cf8d218b909a1237e35dfc8b5b3dc8c4bb7d162 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 2 Jul 2022 02:01:40 -0400 Subject: [PATCH 54/88] Fixed W503 --- hnn_core/gui.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 97319a704..c7c9637e8 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -147,11 +147,13 @@ def _get_rhythmic_widget( tstart_std = FloatText(value=default_data['tstart_std'], description='Start time dev (ms)', **kwargs) - tstop = BoundedFloatText(value=tstop_widget.value if default_data['tstop'] - == 0 else default_data['tstop'], - description='Stop time (ms)', - max=tstop_widget.value, - **kwargs) + tstop = BoundedFloatText( + value=tstop_widget.value + if default_data['tstop'] == 0 else default_data['tstop'], + description='Stop time (ms)', + max=tstop_widget.value, + **kwargs, + ) burst_rate = FloatText(value=default_data['burst_rate'], description='Burst rate (Hz)', **kwargs) @@ -219,12 +221,14 @@ def _get_poisson_widget( description='Start time (ms)', layout=layout, style=style) - tstop = BoundedFloatText(value=tstop_widget.value if default_data['tstop'] - == 0 else default_data['tstop'], - max=tstop_widget.value, - description='Stop time (ms)', - layout=layout, - style=style) + tstop = BoundedFloatText( + value=tstop_widget.value + if default_data['tstop'] == 0 else default_data['tstop'], + max=tstop_widget.value, + description='Stop time (ms)', + layout=layout, + style=style, + ) seedcore = IntText(value=default_data['seedcore'], description='Seed', layout=layout, @@ -782,7 +786,7 @@ def initialize_viz_window(viz_window, elif layout_option == "U-D": grid = init_UD_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, plot_options) - # TODO: 2x2 grids + # TODO: 2x2 display(grid) From d075a7ddd78ca9e4270754be0fa2bcb368e84c37 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 2 Jul 2022 02:57:50 -0400 Subject: [PATCH 55/88] Remove bursty --- hnn_core/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index c7c9637e8..f20ada57e 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -940,7 +940,7 @@ def _handle_backend_change(backend_type): layout = Layout(width='200px', height='100px') drive_type_selection = RadioButtons( - options=['Evoked', 'Poisson', 'Rhythmic', 'Bursty'], + options=['Evoked', 'Poisson', 'Rhythmic'], value='Evoked', description='Drive:', disabled=False, From ce3dfdb829d24c879cdffb82f9253ca8baacbfb5 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 2 Jul 2022 03:07:52 -0400 Subject: [PATCH 56/88] Renamed accordian --- hnn_core/gui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index f20ada57e..53575952e 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -932,9 +932,9 @@ def _handle_backend_change(backend_type): 'Layer 2/3 Pyramidal', 'Layer 5 Pyramidal', 'Layer 2 Basket', 'Layer 5 Basket' ] - accordian = Accordion(children=boxes) + cell_connectivity = Accordion(children=boxes) for idx, title in enumerate(titles): - accordian.set_title(idx, title) + cell_connectivity.set_title(idx, title) # Dropdown for different drives layout = Layout(width='200px', height='100px') @@ -971,7 +971,7 @@ def _add_drive_button_clicked(b): # Tabs for left pane left_tab = Tab() - left_tab.children = [simulation_box, accordian, drives_options] + left_tab.children = [simulation_box, cell_connectivity, drives_options] titles = ['Simulation', 'Cell connectivity', 'Drives'] for idx, title in enumerate(titles): left_tab.set_title(idx, title) From 296d23c8b9eff29e0191ce05cfad841282868a8c Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Mon, 4 Jul 2022 12:15:45 -0400 Subject: [PATCH 57/88] Fixed mutable default parameters --- hnn_core/gui.py | 59 ++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 53575952e..848e95941 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -56,10 +56,14 @@ def _update_params(variables, **updates): return sliders +def _add_connectivity(): + pass + + def _get_cell_specific_widgets(layout, style, location, - data={}, + data=None, default_data={ 'weights_ampa': { 'L5_pyramidal': 0., @@ -80,10 +84,10 @@ def _get_cell_specific_widgets(layout, 'L2_basket': 0.1 }, }): - - for k in default_data.keys(): - if k in data: - default_data[k].update(data[k]) + if isinstance(data, dict): + for k in default_data.keys(): + if k in data: + default_data[k].update(data[k]) kwargs = dict(layout=layout, style=style) cell_types = ['L5_pyramidal', 'L2_pyramidal', 'L5_basket', 'L2_basket'] @@ -125,7 +129,7 @@ def _get_rhythmic_widget( layout, style, location, - data={}, + data=None, default_data={ 'tstart': 0., 'tstart_std': 0., @@ -135,11 +139,12 @@ def _get_rhythmic_widget( 'repeats': 1, 'seedcore': 14, }, - default_weights_ampa={}, - default_weights_nmda={}, - default_delays={}, + default_weights_ampa=None, + default_weights_nmda=None, + default_delays=None, ): - default_data.update(data) + if isinstance(data, dict): + default_data.update(data) kwargs = dict(layout=layout, style=style) tstart = FloatText(value=default_data['tstart'], description='Start time (ms)', @@ -200,7 +205,7 @@ def _get_poisson_widget( layout, style, location, - data={}, + data=None, default_data={ 'tstart': 0.0, 'tstop': 0.0, @@ -212,11 +217,12 @@ def _get_poisson_widget( 'L2_basket': 8.5, } }, - default_weights_ampa={}, - default_weights_nmda={}, - default_delays={}, + default_weights_ampa=None, + default_weights_nmda=None, + default_delays=None, ): - default_data.update(data) + if isinstance(data, dict): + default_data.update(data) tstart = FloatText(value=default_data['tstart'], description='Start time (ms)', layout=layout, @@ -276,18 +282,19 @@ def _get_evoked_widget( layout, style, location, - data={}, + data=None, default_data={ 'mu': 0, 'sigma': 1, 'numspikes': 1, 'seedcore': 14, }, - default_weights_ampa={}, - default_weights_nmda={}, - default_delays={}, + default_weights_ampa=None, + default_weights_nmda=None, + default_delays=None, ): - default_data.update(data) + if isinstance(data, dict): + default_data.update(data) kwargs = dict(layout=layout, style=style) mu = FloatText(value=default_data['mu'], description='Mean time:', @@ -334,10 +341,10 @@ def add_drive_widget( tstop_widget, location, prespecified_drive_name=None, - prespecified_drive_data={}, - prespecified_weights_ampa={}, - prespecified_weights_nmda={}, - prespecified_delays={}, + prespecified_drive_data=None, + prespecified_weights_ampa=None, + prespecified_weights_nmda=None, + prespecified_delays=None, render=True, expand_last_drive=True, event_seed=14, @@ -346,8 +353,10 @@ def add_drive_widget( layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} drives_out.clear_output() - + if not prespecified_drive_data: + prespecified_drive_data = {} prespecified_drive_data.update({"seedcore": max(event_seed, 2)}) + with drives_out: if not prespecified_drive_name: name = drive_type + str(len(drive_boxes)) From eac866f2e8d5c47ce0788cf568a737a33daccdef Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Mon, 4 Jul 2022 12:20:05 -0400 Subject: [PATCH 58/88] Re-formatted --- hnn_core/gui.py | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 848e95941..02cfd7b3a 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -60,30 +60,31 @@ def _add_connectivity(): pass -def _get_cell_specific_widgets(layout, - style, - location, - data=None, - default_data={ - 'weights_ampa': { - 'L5_pyramidal': 0., - 'L2_pyramidal': 0., - 'L5_basket': 0., - 'L2_basket': 0. - }, - 'weights_nmda': { - 'L5_pyramidal': 0., - 'L2_pyramidal': 0., - 'L5_basket': 0., - 'L2_basket': 0. - }, - 'delays': { - 'L5_pyramidal': 0.1, - 'L2_pyramidal': 0.1, - 'L5_basket': 0.1, - 'L2_basket': 0.1 - }, - }): +def _get_cell_specific_widgets( + layout, + style, + location, + data=None, + default_data={ + 'weights_ampa': { + 'L5_pyramidal': 0., + 'L2_pyramidal': 0., + 'L5_basket': 0., + 'L2_basket': 0. + }, + 'weights_nmda': { + 'L5_pyramidal': 0., + 'L2_pyramidal': 0., + 'L5_basket': 0., + 'L2_basket': 0. + }, + 'delays': { + 'L5_pyramidal': 0.1, + 'L2_pyramidal': 0.1, + 'L5_basket': 0.1, + 'L2_basket': 0.1 + }, + }): if isinstance(data, dict): for k in default_data.keys(): if k in data: From 673569c4439cefd45260372ed6b030c1e9ebbde2 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 00:11:27 -0400 Subject: [PATCH 59/88] Removed mutable default arguments --- hnn_core/gui.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 02cfd7b3a..af42578d1 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -65,7 +65,8 @@ def _get_cell_specific_widgets( style, location, data=None, - default_data={ +): + default_data = { 'weights_ampa': { 'L5_pyramidal': 0., 'L2_pyramidal': 0., @@ -84,7 +85,7 @@ def _get_cell_specific_widgets( 'L5_basket': 0.1, 'L2_basket': 0.1 }, - }): + } if isinstance(data, dict): for k in default_data.keys(): if k in data: @@ -131,7 +132,11 @@ def _get_rhythmic_widget( style, location, data=None, - default_data={ + default_weights_ampa=None, + default_weights_nmda=None, + default_delays=None, +): + default_data = { 'tstart': 0., 'tstart_std': 0., 'tstop': 0., @@ -139,11 +144,7 @@ def _get_rhythmic_widget( 'burst_std': 0, 'repeats': 1, 'seedcore': 14, - }, - default_weights_ampa=None, - default_weights_nmda=None, - default_delays=None, -): + } if isinstance(data, dict): default_data.update(data) kwargs = dict(layout=layout, style=style) @@ -207,7 +208,11 @@ def _get_poisson_widget( style, location, data=None, - default_data={ + default_weights_ampa=None, + default_weights_nmda=None, + default_delays=None, +): + default_data = { 'tstart': 0.0, 'tstop': 0.0, 'seedcore': 14, @@ -217,11 +222,7 @@ def _get_poisson_widget( 'L5_basket': 8.5, 'L2_basket': 8.5, } - }, - default_weights_ampa=None, - default_weights_nmda=None, - default_delays=None, -): + } if isinstance(data, dict): default_data.update(data) tstart = FloatText(value=default_data['tstart'], @@ -284,16 +285,16 @@ def _get_evoked_widget( style, location, data=None, - default_data={ - 'mu': 0, - 'sigma': 1, - 'numspikes': 1, - 'seedcore': 14, - }, default_weights_ampa=None, default_weights_nmda=None, default_delays=None, ): + default_data = { + 'mu': 0, + 'sigma': 1, + 'numspikes': 1, + 'seedcore': 14, + } if isinstance(data, dict): default_data.update(data) kwargs = dict(layout=layout, style=style) From fec6ff983726cb276f49a7782c323607d79e7da9 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 00:25:37 -0400 Subject: [PATCH 60/88] Replaced Network with jones_2009_model --- hnn_core/gui.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index af42578d1..86c34966b 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -16,8 +16,8 @@ interactive_output, GridspecLayout) import hnn_core -from hnn_core import (MPIBackend, JoblibBackend, Network, read_params, - simulate_dipole) +from hnn_core import (MPIBackend, JoblibBackend, read_params, + simulate_dipole, jones_2009_model) from hnn_core.params import (_read_json, _read_legacy_params, _extract_drive_specs_from_hnn_params) from hnn_core.viz import plot_dipole @@ -56,10 +56,6 @@ def _update_params(variables, **updates): return sliders -def _add_connectivity(): - pass - - def _get_cell_specific_widgets( layout, style, @@ -465,7 +461,7 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, drive_boxes, tstop): """Add drive ipywidgets from params. """ - variables['net'] = Network(params) + variables['net'] = jones_2009_model(params) log_out.clear_output() with log_out: drive_specs = _extract_drive_specs_from_hnn_params( @@ -557,7 +553,7 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, print(f"drive_widgets length={len(drive_widgets)}") params['dt'] = tstep.value params['tstop'] = tstop.value - variables['net'] = Network( + variables['net'] = jones_2009_model( params, add_drives_from_params=False, ) From d0088fafac83fe13f4220a2c51505edbf4db8eb7 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 00:38:57 -0400 Subject: [PATCH 61/88] Added all possible connections --- hnn_core/gui.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 86c34966b..da7af3063 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -16,8 +16,8 @@ interactive_output, GridspecLayout) import hnn_core -from hnn_core import (MPIBackend, JoblibBackend, read_params, - simulate_dipole, jones_2009_model) +from hnn_core import (MPIBackend, JoblibBackend, read_params, simulate_dipole, + jones_2009_model) from hnn_core.params import (_read_json, _read_legacy_params, _extract_drive_specs_from_hnn_params) from hnn_core.viz import plot_dipole @@ -930,7 +930,10 @@ def _handle_backend_change(backend_type): ]), _get_sliders(params, ['gbar_L2Pyr_L2Basket', 'gbar_L2Basket_L2Basket']), - _get_sliders(params, ['gbar_L2Pyr_L5Pyr', 'gbar_L2Basket_L5Pyr']) + _get_sliders(params, [ + 'gbar_L2Pyr_L5Basket', 'gbar_L5Pyr_L5Basket', + 'gbar_L5Basket_L5Basket' + ]) ] # accordians to group local-connectivity by cell type From cac88df8d8b6a29a6bb1836af0ed42395de06729 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 14:53:29 -0400 Subject: [PATCH 62/88] Fixed func names --- hnn_core/gui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index da7af3063..03470b0aa 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -656,7 +656,7 @@ def handle_backend_change(backend_type, mpi_cmd_config, mpi_cmd): display(mpi_cmd) -def init_LR_viz_layout(plot_outputs, +def init_left_right_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, @@ -710,7 +710,7 @@ def init_LR_viz_layout(plot_outputs, return grid -def init_UD_viz_layout(plot_outputs, +def init_upper_down_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, @@ -787,11 +787,11 @@ def initialize_viz_window(viz_window, with viz_window: # Left-Rright configuration if layout_option == "L-R": - grid = init_LR_viz_layout(plot_outputs, plot_dropdowns, + grid = init_left_right_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, plot_options) # Upper-Down configuration elif layout_option == "U-D": - grid = init_UD_viz_layout(plot_outputs, plot_dropdowns, + grid = init_upper_down_viz_layout(plot_outputs, plot_dropdowns, window_height, variables, plot_options) # TODO: 2x2 From 012567a83038618417dd29aa7f97f6f15a9c26af Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 15:07:54 -0400 Subject: [PATCH 63/88] Fixed various formatting issues --- hnn_core/gui.py | 58 ++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 03470b0aa..419f99740 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -5,30 +5,32 @@ import codecs import multiprocessing -import os.path as op -import numpy as np import os +import os.path as op + import matplotlib.pyplot as plt +import numpy as np from IPython.display import display from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, - Dropdown, FileUpload, FloatLogSlider, FloatText, HBox, - IntText, Layout, Output, RadioButtons, Tab, Text, VBox, - interactive_output, GridspecLayout) + Dropdown, FileUpload, FloatLogSlider, FloatText, + GridspecLayout, HBox, IntText, Layout, Output, + RadioButtons, Tab, Text, VBox, interactive_output) import hnn_core -from hnn_core import (MPIBackend, JoblibBackend, read_params, simulate_dipole, - jones_2009_model) -from hnn_core.params import (_read_json, _read_legacy_params, - _extract_drive_specs_from_hnn_params) +from hnn_core import (JoblibBackend, MPIBackend, jones_2009_model, read_params, + simulate_dipole) +from hnn_core.params import (_extract_drive_specs_from_hnn_params, _read_json, + _read_legacy_params) from hnn_core.viz import plot_dipole -def create_expanded_button(description, button_style, height): +def create_expanded_button(description, button_style, height, disabled=False): style = {'button_color': '#8A2BE2'} return Button(description=description, button_style=button_style, layout=Layout(height=height, width='auto'), - style=style) + style=style, + disabled=disabled) def _get_sliders(params, param_keys): @@ -345,7 +347,7 @@ def add_drive_widget( prespecified_delays=None, render=True, expand_last_drive=True, - event_seed=14, + event_seed=14 ): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') @@ -459,8 +461,7 @@ def update_plot_window(variables, _plot_out, plot_type): def load_drives(variables, params, log_out, drives_out, drive_widgets, drive_boxes, tstop): - """Add drive ipywidgets from params. - """ + """Add drive ipywidgets from params.""" variables['net'] = jones_2009_model(params) log_out.clear_output() with log_out: @@ -657,11 +658,11 @@ def handle_backend_change(backend_type, mpi_cmd_config, mpi_cmd): def init_left_right_viz_layout(plot_outputs, - plot_dropdowns, - window_height, - variables, - plot_options, - border='1px solid gray'): + plot_dropdowns, + window_height, + variables, + plot_options, + border='1px solid gray'): height_plot = window_height plot_outputs_L = Output(layout={'border': border, 'height': height_plot}) @@ -711,11 +712,11 @@ def init_left_right_viz_layout(plot_outputs, def init_upper_down_viz_layout(plot_outputs, - plot_dropdowns, - window_height, - variables, - plot_options, - border='1px solid gray'): + plot_dropdowns, + window_height, + variables, + plot_options, + border='1px solid gray'): height_plot = window_height plot_outputs_U = Output(layout={ 'border': border, @@ -788,11 +789,13 @@ def initialize_viz_window(viz_window, # Left-Rright configuration if layout_option == "L-R": grid = init_left_right_viz_layout(plot_outputs, plot_dropdowns, - window_height, variables, plot_options) + window_height, variables, + plot_options) # Upper-Down configuration elif layout_option == "U-D": grid = init_upper_down_viz_layout(plot_outputs, plot_dropdowns, - window_height, variables, plot_options) + window_height, variables, + plot_options) # TODO: 2x2 display(grid) @@ -853,7 +856,8 @@ def _delete_drives_clicked(b): # header_button header_button = create_expanded_button('HUMAN NEOCORTICAL NEUROSOLVER', 'success', - height='40px') + height='40px', + disabled=True) # Simulation parameters tstop = FloatText(value=170, description='tstop (ms):', disabled=False) From c3a910ccf87f3e5b22a05e3d5af582dcd519497e Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 15:20:33 -0400 Subject: [PATCH 64/88] Changed render option assignment --- hnn_core/gui.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 419f99740..e45e52714 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -333,22 +333,20 @@ def _get_evoked_widget( return drive, drive_box -def add_drive_widget( - drive_type, - drive_boxes, - drive_widgets, - drives_out, - tstop_widget, - location, - prespecified_drive_name=None, - prespecified_drive_data=None, - prespecified_weights_ampa=None, - prespecified_weights_nmda=None, - prespecified_delays=None, - render=True, - expand_last_drive=True, - event_seed=14 -): +def add_drive_widget(drive_type, + drive_boxes, + drive_widgets, + drives_out, + tstop_widget, + location, + prespecified_drive_name=None, + prespecified_drive_data=None, + prespecified_weights_ampa=None, + prespecified_weights_nmda=None, + prespecified_delays=None, + render=True, + expand_last_drive=True, + event_seed=14): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} @@ -477,6 +475,7 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, drive_names = sorted(drive_specs.keys()) for idx, drive_name in enumerate(drive_names): # order matters specs = drive_specs[drive_name] + should_render = idx == (len(drive_names) - 1) print(f"""load drive: (name={drive_name}, type={specs['type']}, @@ -496,7 +495,7 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, prespecified_weights_ampa=specs['weights_ampa'], prespecified_weights_nmda=specs['weights_nmda'], prespecified_delays=specs['synaptic_delays'], - render=idx == len(drive_names) - 1, + render=should_render, expand_last_drive=False, event_seed=specs['event_seed'], ) From a38520aec26c728a548539930f14f0d12a3d9e6f Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 15:44:19 -0400 Subject: [PATCH 65/88] Updated Joblib backend config --- hnn_core/gui.py | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index e45e52714..ba50d3012 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -11,10 +11,11 @@ import matplotlib.pyplot as plt import numpy as np from IPython.display import display -from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, Button, - Dropdown, FileUpload, FloatLogSlider, FloatText, - GridspecLayout, HBox, IntText, Layout, Output, - RadioButtons, Tab, Text, VBox, interactive_output) +from ipywidgets import (HTML, Accordion, AppLayout, BoundedIntText, + BoundedFloatText, Button, Dropdown, FileUpload, + FloatLogSlider, FloatText, GridspecLayout, HBox, + IntText, Layout, Output, RadioButtons, Tab, Text, VBox, + interactive_output) import hnn_core from hnn_core import (JoblibBackend, MPIBackend, jones_2009_model, read_params, @@ -545,8 +546,8 @@ def on_upload_change( def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, - ntrials, backend_selection, mpi_cmd, params, - plot_outputs_list, plot_dropdowns_list, b): + ntrials, backend_selection, mpi_cmd, joblib_cores, + params, plot_outputs_list, plot_dropdowns_list, b): """Run the simulation and plot outputs.""" log_out.clear_output() with log_out: @@ -626,8 +627,8 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, variables['backend'] = MPIBackend( n_procs=multiprocessing.cpu_count() - 1, mpi_cmd=mpi_cmd.value) else: - variables['backend'] = JoblibBackend(n_jobs=2) - + variables['backend'] = JoblibBackend(n_jobs=joblib_cores.value) + print(f"Using Joblib as backend with {joblib_cores.value} core(s).") with variables['backend']: variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, @@ -649,11 +650,13 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, }) -def handle_backend_change(backend_type, mpi_cmd_config, mpi_cmd): - mpi_cmd_config.clear_output() - if backend_type == "MPI": - with mpi_cmd_config: +def handle_backend_change(backend_type, backend_config, mpi_cmd, joblib_cores): + backend_config.clear_output() + with backend_config: + if backend_type == "MPI": display(mpi_cmd) + elif backend_type == "Joblib": + display(joblib_cores) def init_left_right_viz_layout(plot_outputs, @@ -818,7 +821,7 @@ def run_hnn_gui(): def _run_button_clicked(b): return run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, ntrials, backend_selection, mpi_cmd, - params, plot_outputs_list, + joblib_cores, params, plot_outputs_list, plot_dropdowns_list, b) def _on_upload_change(change): @@ -904,12 +907,20 @@ def handle_viz_layout_change(layout_option): description='MPI cmd:', disabled=False) - mpi_cmd_config = Output() + joblib_cores = BoundedIntText(value=1, + min=1, + max=multiprocessing.cpu_count(), + description='Cores:', + disabled=False) + + backend_config = Output() def _handle_backend_change(backend_type): - return handle_backend_change(backend_type.new, mpi_cmd_config, mpi_cmd) + return handle_backend_change(backend_type.new, backend_config, mpi_cmd, + joblib_cores) - handle_backend_change(backend_selection.value, mpi_cmd_config, mpi_cmd) + handle_backend_change(backend_selection.value, backend_config, mpi_cmd, + joblib_cores) backend_selection.observe(_handle_backend_change, 'value') simulation_box = VBox([ @@ -917,7 +928,7 @@ def _handle_backend_change(backend_type): tstep, ntrials, backend_selection, - mpi_cmd_config, + backend_config, ]) # Sliders to change local-connectivity params From 6a21748dfc31238c4c88963d1c0c8a13d1072bca Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Tue, 5 Jul 2022 15:57:43 -0400 Subject: [PATCH 66/88] Fix formatting issues --- hnn_core/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index ba50d3012..d4e853bd0 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -628,7 +628,7 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, n_procs=multiprocessing.cpu_count() - 1, mpi_cmd=mpi_cmd.value) else: variables['backend'] = JoblibBackend(n_jobs=joblib_cores.value) - print(f"Using Joblib as backend with {joblib_cores.value} core(s).") + print(f"Using Joblib with {joblib_cores.value} core(s).") with variables['backend']: variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, From 69c52de4e80ec8b2a6cf038210e8c5ae3ce21318 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Tue, 5 Jul 2022 19:05:45 -0400 Subject: [PATCH 67/88] ENH: allow launching GUI with command hnn-gui --- hnn_core/gui.py | 5 +++++ setup.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index d4e853bd0..0671b9522 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -1040,3 +1040,8 @@ def _add_drive_button_clicked(b): pane_heights=['50px', viz_height, "1"], ) return hnn_gui + +def launch(): + from voila.app import main + notebook_path = op.join(op.dirname(__file__), '..', 'hnn_widget.ipynb') + main([notebook_path]) diff --git a/setup.py b/setup.py index 809c98531..b0b1fd96e 100644 --- a/setup.py +++ b/setup.py @@ -114,5 +114,6 @@ def run(self): 'mod/*', 'mod/x86_64/*', 'mod/x86_64/.lib/*']}, - cmdclass={'build_py': build_py_mod, 'build_mod': BuildMod} + cmdclass={'build_py': build_py_mod, 'build_mod': BuildMod}, + entry_points={'console_scripts': ['hnn-gui=hnn_core.gui:launch']} ) From f5d9545eb3ee549a0fdaab4d78efe6a093d4b3c4 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Wed, 6 Jul 2022 10:35:24 -0400 Subject: [PATCH 68/88] Fix flake8 E302 --- hnn_core/gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 0671b9522..21a8f27e9 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -1041,6 +1041,7 @@ def _add_drive_button_clicked(b): ) return hnn_gui + def launch(): from voila.app import main notebook_path = op.join(op.dirname(__file__), '..', 'hnn_widget.ipynb') From ad64abaea724cb4c5ddec99de7095551d6d102e1 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Wed, 6 Jul 2022 11:05:31 -0400 Subject: [PATCH 69/88] Preserved previous plots --- hnn_core/gui.py | 78 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 14 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 21a8f27e9..f09509967 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -664,13 +664,19 @@ def init_left_right_viz_layout(plot_outputs, window_height, variables, plot_options, - border='1px solid gray'): + previous_outputs, + border='1px solid gray', + init=False): height_plot = window_height plot_outputs_L = Output(layout={'border': border, 'height': height_plot}) + default_plot_types = [plot_options[0], plot_options[1]] + for idx, plot_type in enumerate(previous_outputs[:2]): + default_plot_types[idx] = plot_type + plot_dropdown_L = Dropdown( options=plot_options, - value=plot_options[0], + value=default_plot_types[0], description='Plot:', disabled=False, ) @@ -691,7 +697,7 @@ def init_left_right_viz_layout(plot_outputs, plot_dropdown_R = Dropdown( options=plot_options, - value=plot_options[1], + value=default_plot_types[1], description='Plot:', disabled=False, ) @@ -704,9 +710,22 @@ def init_left_right_viz_layout(plot_outputs, ), 'value', ) + plot_outputs.append(plot_outputs_R) plot_dropdowns.append(plot_dropdown_R) + if not init: + update_plot_window(variables, plot_outputs_L, { + "type": "change", + "name": "value", + "new": default_plot_types[0] + }) + update_plot_window(variables, plot_outputs_R, { + "type": "change", + "name": "value", + "new": default_plot_types[1] + }) + grid = GridspecLayout(1, 2, height=window_height) grid[0, 0] = VBox([plot_dropdown_L, plot_outputs_L]) grid[0, 1] = VBox([plot_dropdown_R, plot_outputs_R]) @@ -718,8 +737,14 @@ def init_upper_down_viz_layout(plot_outputs, window_height, variables, plot_options, - border='1px solid gray'): + previous_outputs, + border='1px solid gray', + init=False): height_plot = window_height + default_plot_types = [plot_options[0], plot_options[1]] + for idx, plot_type in enumerate(previous_outputs[:2]): + default_plot_types[idx] = plot_type + plot_outputs_U = Output(layout={ 'border': border, 'height': f"{float(height_plot[:-2])/2}px" @@ -727,7 +752,7 @@ def init_upper_down_viz_layout(plot_outputs, plot_dropdown_U = Dropdown( options=plot_options, - value=plot_options[0], + value=default_plot_types[0], description='Plot:', disabled=False, ) @@ -749,10 +774,11 @@ def init_upper_down_viz_layout(plot_outputs, plot_dropdown_D = Dropdown( options=plot_options, - value=plot_options[1], + value=default_plot_types[1], description='Plot:', disabled=False, ) + plot_dropdown_D.observe( lambda plot_type: _debug_update_plot_window( variables, @@ -765,6 +791,18 @@ def init_upper_down_viz_layout(plot_outputs, plot_outputs.append(plot_outputs_D) plot_dropdowns.append(plot_dropdown_D) + if not init: + update_plot_window(variables, plot_outputs_U, { + "type": "change", + "name": "value", + "new": default_plot_types[0] + }) + update_plot_window(variables, plot_outputs_D, { + "type": "change", + "name": "value", + "new": default_plot_types[1] + }) + grid = GridspecLayout(2, 1, height=window_height) grid[0, 0] = VBox([plot_dropdown_U, plot_outputs_U]) grid[1, 0] = VBox([plot_dropdown_D, plot_outputs_D]) @@ -777,27 +815,38 @@ def initialize_viz_window(viz_window, plot_dropdowns, window_width, window_height, - layout_option="L-R"): + layout_option="L-R", + init=False): plot_options = [ 'current dipole', 'input histogram', 'spikes', 'PSD', 'spectogram', 'network' ] viz_window.clear_output() + previous_plot_outputs_values = [] while len(plot_outputs) > 0: plot_outputs.pop() - plot_dropdowns.pop() + # plot_dropdowns.pop() + previous_plot_outputs_values.append(plot_dropdowns.pop().value) with viz_window: # Left-Rright configuration if layout_option == "L-R": - grid = init_left_right_viz_layout(plot_outputs, plot_dropdowns, - window_height, variables, - plot_options) + grid = init_left_right_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + previous_plot_outputs_values, + init=init) # Upper-Down configuration elif layout_option == "U-D": - grid = init_upper_down_viz_layout(plot_outputs, plot_dropdowns, - window_height, variables, - plot_options) + grid = init_upper_down_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + previous_plot_outputs_values, + init=init) # TODO: 2x2 display(grid) @@ -881,6 +930,7 @@ def _delete_drives_clicked(b): viz_width, viz_height, layout_option=viz_layout_selection.value, + init=True ) def handle_viz_layout_change(layout_option): From bf2831b9a981eac808e8744b58b1582b09814de7 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Wed, 6 Jul 2022 23:38:53 -0400 Subject: [PATCH 70/88] Added test, removed deprecated settings & add debug logging --- hnn_core/gui.py | 19 ++++++++++++++++--- hnn_core/tests/test_gui.py | 10 ++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 hnn_core/tests/test_gui.py diff --git a/hnn_core/gui.py b/hnn_core/gui.py index f09509967..5aa01d721 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -4,6 +4,7 @@ # Huzi Cheng import codecs +import logging import multiprocessing import os import os.path as op @@ -25,6 +26,17 @@ from hnn_core.viz import plot_dipole +log_file = os.getenv("HNNGUI_LOGFILE", None) +debug_gui = os.getenv("DEBUG_HNNGUI", "0") +if debug_gui == '1' and log_file is not None: + logging.basicConfig(filename=log_file, + filemode='w', + level=logging.DEBUG, + format='%(name)s - %(levelname)s - %(message)s') +else: + logging.basicConfig(level=logging.ERROR) + + def create_expanded_button(description, button_style, height, disabled=False): style = {'button_color': '#8A2BE2'} return Button(description=description, @@ -52,7 +64,8 @@ def _get_sliders(params, param_keys): style=style) sliders.append(slider) - def _update_params(variables, **updates): + def _update_params(**updates): + logging.debug(f'Connectivity parameters updates: {updates}') params.update(dict(**updates)) interactive_output(_update_params, {s.description: s for s in sliders}) @@ -895,7 +908,7 @@ def _delete_drives_clicked(b): log_out = Output(layout={ 'border': '1px solid gray', 'height': log_out_heiht, - 'overflow_y': 'auto' + 'overflow': 'auto' }) viz_width = "1000px" viz_height = "500px" @@ -1053,7 +1066,7 @@ def _add_drive_button_clicked(b): # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') - style = {'button_color': '#8A2BE2', 'font_color': 'white'} + style = {'button_color': '#8A2BE2'} load_button = FileUpload(accept='.json,.param', multiple=False, style=style, diff --git a/hnn_core/tests/test_gui.py b/hnn_core/tests/test_gui.py new file mode 100644 index 000000000..9276c9b64 --- /dev/null +++ b/hnn_core/tests/test_gui.py @@ -0,0 +1,10 @@ +# Authors: Huzi Cheng +from hnn_core.gui import run_hnn_gui +import os + + +def test_run_gui(): + """Test if main gui function gives proper ipywidget""" + os.environ["DEBUG_HNNGUI"] = "0" + gui = run_hnn_gui() + assert gui is not None From 316cafc9f5ab2b48cc324066cf4f7fffd1931f02 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 7 Jul 2022 11:05:21 -0400 Subject: [PATCH 71/88] Updated optional dependencies --- .github/workflows/unit_tests.yml | 3 ++- setup.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index fb1abf450..f8c3c112a 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -44,7 +44,8 @@ jobs: - name: Install HNN-core shell: bash -el {0} run: | - python setup.py --verbose install + python -m pip install --upgrade pip + pip install -e .[gui] - name: Lint with flake8 shell: bash -el {0} run: | diff --git a/setup.py b/setup.py index b0b1fd96e..fddb40489 100644 --- a/setup.py +++ b/setup.py @@ -105,7 +105,7 @@ def run(self): 'matplotlib', 'scipy' ], - extra_requires={ + extras_require={ 'gui': ['ipywidgets', 'voila'] }, packages=find_packages(), From 738c9e6c1b7582dc962483fcb50c27a317b9c2ac Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 7 Jul 2022 11:10:57 -0400 Subject: [PATCH 72/88] Fix import order --- hnn_core/tests/test_gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hnn_core/tests/test_gui.py b/hnn_core/tests/test_gui.py index 9276c9b64..f05ff5f22 100644 --- a/hnn_core/tests/test_gui.py +++ b/hnn_core/tests/test_gui.py @@ -1,7 +1,8 @@ # Authors: Huzi Cheng -from hnn_core.gui import run_hnn_gui import os +from hnn_core.gui import run_hnn_gui + def test_run_gui(): """Test if main gui function gives proper ipywidget""" From 1610197e172ba2ec98533223c633eb4385928809 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 7 Jul 2022 14:32:49 -0400 Subject: [PATCH 73/88] Update unit test settings --- .github/workflows/unit_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index f8c3c112a..31e92158b 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -36,6 +36,7 @@ jobs: pip install flake8 pytest pytest-cov pip install mne psutil joblib pip install NEURON + pip install ipywidgets voila if [ "${{ matrix.os }}" == "ubuntu-latest" ]; then pip install mpi4py else @@ -44,8 +45,7 @@ jobs: - name: Install HNN-core shell: bash -el {0} run: | - python -m pip install --upgrade pip - pip install -e .[gui] + python setup.py --verbose install - name: Lint with flake8 shell: bash -el {0} run: | @@ -57,4 +57,4 @@ jobs: - name: Upload code coverage shell: bash -el {0} run: | - bash <(curl -s https://codecov.io/bash) -f ./coverage.xml + bash <(curl -s https://codecov.io/bash) -f ./coverage.xml \ No newline at end of file From d4a81cd669a64903968ca44cfade1bc5b8e01832 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 7 Jul 2022 16:47:24 -0400 Subject: [PATCH 74/88] Allow passing voila arguments --- hnn_core/gui.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/hnn_core/gui.py b/hnn_core/gui.py index 5aa01d721..9dca475bf 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui.py @@ -8,12 +8,13 @@ import multiprocessing import os import os.path as op +import sys import matplotlib.pyplot as plt import numpy as np from IPython.display import display -from ipywidgets import (HTML, Accordion, AppLayout, BoundedIntText, - BoundedFloatText, Button, Dropdown, FileUpload, +from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, + BoundedIntText, Button, Dropdown, FileUpload, FloatLogSlider, FloatText, GridspecLayout, HBox, IntText, Layout, Output, RadioButtons, Tab, Text, VBox, interactive_output) @@ -25,7 +26,6 @@ _read_legacy_params) from hnn_core.viz import plot_dipole - log_file = os.getenv("HNNGUI_LOGFILE", None) debug_gui = os.getenv("DEBUG_HNNGUI", "0") if debug_gui == '1' and log_file is not None: @@ -935,16 +935,14 @@ def _delete_drives_clicked(b): description='Layout:', ) # initialize - initialize_viz_window( - viz_window, - variables, - plot_outputs_list, - plot_dropdowns_list, - viz_width, - viz_height, - layout_option=viz_layout_selection.value, - init=True - ) + initialize_viz_window(viz_window, + variables, + plot_outputs_list, + plot_dropdowns_list, + viz_width, + viz_height, + layout_option=viz_layout_selection.value, + init=True) def handle_viz_layout_change(layout_option): return initialize_viz_window( @@ -1108,4 +1106,4 @@ def _add_drive_button_clicked(b): def launch(): from voila.app import main notebook_path = op.join(op.dirname(__file__), '..', 'hnn_widget.ipynb') - main([notebook_path]) + main([notebook_path, *sys.argv[1:]]) From 49098b270c2703167d41359efee42f86a12adb8e Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Thu, 7 Jul 2022 17:01:02 -0400 Subject: [PATCH 75/88] Re-organized code --- hnn_core/gui/__init__.py | 0 hnn_core/{ => gui}/gui.py | 2 +- hnn_widget.ipynb => hnn_core/gui/hnn_widget.ipynb | 2 +- hnn_core/tests/test_gui.py | 2 +- setup.py | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 hnn_core/gui/__init__.py rename hnn_core/{ => gui}/gui.py (99%) rename hnn_widget.ipynb => hnn_core/gui/hnn_widget.ipynb (96%) diff --git a/hnn_core/gui/__init__.py b/hnn_core/gui/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/hnn_core/gui.py b/hnn_core/gui/gui.py similarity index 99% rename from hnn_core/gui.py rename to hnn_core/gui/gui.py index 9dca475bf..d2dc8d1ae 100644 --- a/hnn_core/gui.py +++ b/hnn_core/gui/gui.py @@ -1105,5 +1105,5 @@ def _add_drive_button_clicked(b): def launch(): from voila.app import main - notebook_path = op.join(op.dirname(__file__), '..', 'hnn_widget.ipynb') + notebook_path = op.join(op.dirname(__file__), 'hnn_widget.ipynb') main([notebook_path, *sys.argv[1:]]) diff --git a/hnn_widget.ipynb b/hnn_core/gui/hnn_widget.ipynb similarity index 96% rename from hnn_widget.ipynb rename to hnn_core/gui/hnn_widget.ipynb index c406faaf2..72046fd28 100644 --- a/hnn_widget.ipynb +++ b/hnn_core/gui/hnn_widget.ipynb @@ -31,7 +31,7 @@ } ], "source": [ - "from hnn_core.gui import run_hnn_gui\n", + "from hnn_core.gui.gui import run_hnn_gui\n", "from IPython.display import display\n", "\n", "display(run_hnn_gui())" diff --git a/hnn_core/tests/test_gui.py b/hnn_core/tests/test_gui.py index f05ff5f22..06ba7d5b0 100644 --- a/hnn_core/tests/test_gui.py +++ b/hnn_core/tests/test_gui.py @@ -1,7 +1,7 @@ # Authors: Huzi Cheng import os -from hnn_core.gui import run_hnn_gui +from hnn_core.gui.gui import run_hnn_gui def test_run_gui(): diff --git a/setup.py b/setup.py index fddb40489..bb0d17393 100644 --- a/setup.py +++ b/setup.py @@ -115,5 +115,5 @@ def run(self): 'mod/x86_64/*', 'mod/x86_64/.lib/*']}, cmdclass={'build_py': build_py_mod, 'build_mod': BuildMod}, - entry_points={'console_scripts': ['hnn-gui=hnn_core.gui:launch']} + entry_points={'console_scripts': ['hnn-gui=hnn_core.gui.gui:launch']} ) From a8968d5d84eb0286190002a0e9e6aae8a725a400 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 01:55:04 -0400 Subject: [PATCH 76/88] Add drive location, status button & change header --- hnn_core/gui/gui.py | 48 ++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index d2dc8d1ae..976697807 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -16,8 +16,8 @@ from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, BoundedIntText, Button, Dropdown, FileUpload, FloatLogSlider, FloatText, GridspecLayout, HBox, - IntText, Layout, Output, RadioButtons, Tab, Text, VBox, - interactive_output) + IntText, Label, Layout, Output, RadioButtons, Tab, + Text, VBox, interactive_output) import hnn_core from hnn_core import (JoblibBackend, MPIBackend, jones_2009_model, read_params, @@ -37,8 +37,11 @@ logging.basicConfig(level=logging.ERROR) +THEMECOLOR = "#8A2BE2" + + def create_expanded_button(description, button_style, height, disabled=False): - style = {'button_color': '#8A2BE2'} + style = {'button_color': THEMECOLOR} return Button(description=description, button_style=button_style, layout=Layout(height=height, width='auto'), @@ -196,9 +199,10 @@ def _get_rhythmic_widget( 'delays': default_delays, }, ) - drive_box = VBox( - [tstart, tstart_std, tstop, burst_rate, burst_std, repeats, seedcore] + - widgets_list) + drive_box = VBox([ + HTML(value=f"

Location: {location}

"), tstart, tstart_std, tstop, + burst_rate, burst_std, repeats, seedcore + ] + widgets_list) drive = dict(type='Rhythmic', name=name, tstart=tstart, @@ -277,7 +281,9 @@ def _get_poisson_widget( widgets_list.extend([HTML(value="Rate constants")] + list(widgets_dict['rate_constant'].values())) - drive_box = VBox([tstart, tstop, seedcore] + widgets_list) + drive_box = VBox( + [HTML(value=f"

Location: {location}

"), tstart, tstop, seedcore] + + widgets_list) drive = dict( type='Poisson', name=name, @@ -334,7 +340,10 @@ def _get_evoked_widget( }, ) - drive_box = VBox([mu, sigma, numspikes, seedcore] + widgets_list) + drive_box = VBox([ + HTML(value=f"

Location: {location}

"), mu, sigma, numspikes, + seedcore + ] + widgets_list) drive = dict(type='Evoked', name=name, mu=mu, @@ -560,7 +569,8 @@ def on_upload_change( def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, ntrials, backend_selection, mpi_cmd, joblib_cores, - params, plot_outputs_list, plot_dropdowns_list, b): + params, plot_outputs_list, plot_dropdowns_list, + simulation_status, b): """Run the simulation and plot outputs.""" log_out.clear_output() with log_out: @@ -643,11 +653,13 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, variables['backend'] = JoblibBackend(n_jobs=joblib_cores.value) print(f"Using Joblib with {joblib_cores.value} core(s).") with variables['backend']: + simulation_status.value = "Running..." variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, n_trials=ntrials.value) window_len, scaling_factor = 30, 3000 + simulation_status.value = "Simulation finished" for dpl in variables['dpls']: dpl.smooth(window_len).scale(scaling_factor) @@ -884,7 +896,7 @@ def _run_button_clicked(b): return run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, ntrials, backend_selection, mpi_cmd, joblib_cores, params, plot_outputs_list, - plot_dropdowns_list, b) + plot_dropdowns_list, simulation_status, b) def _on_upload_change(change): return on_upload_change(change, sliders, params, tstop, tstep, log_out, @@ -918,10 +930,10 @@ def _delete_drives_clicked(b): }) # header_button - header_button = create_expanded_button('HUMAN NEOCORTICAL NEUROSOLVER', - 'success', - height='40px', - disabled=True) + header_button = HTML( + value= + f"""
+ HUMAN NEOCORTICAL NEUROSOLVER
""") # Simulation parameters tstop = FloatText(value=170, description='tstop (ms):', disabled=False) @@ -1061,10 +1073,13 @@ def _add_drive_button_clicked(b): for idx, title in enumerate(titles): left_tab.set_title(idx, title) + # Running status + simulation_status = Label(value="Not running") + # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') - style = {'button_color': '#8A2BE2'} + style = {'button_color': THEMECOLOR} load_button = FileUpload(accept='.json,.param', multiple=False, style=style, @@ -1084,7 +1099,7 @@ def _add_drive_button_clicked(b): HBox([run_button, load_button, delete_button], layout={"width": left_width}), viz_layout_selection, - ]), log_out + ]), log_out, simulation_status ]) # initialize drive ipywidgets @@ -1107,3 +1122,4 @@ def launch(): from voila.app import main notebook_path = op.join(op.dirname(__file__), 'hnn_widget.ipynb') main([notebook_path, *sys.argv[1:]]) + From 0ade9372517008617cad33ced5210ff5574667b6 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 02:29:25 -0400 Subject: [PATCH 77/88] Add all drives --- hnn_core/gui/gui.py | 63 +++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 976697807..542402b70 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -30,7 +30,7 @@ debug_gui = os.getenv("DEBUG_HNNGUI", "0") if debug_gui == '1' and log_file is not None: logging.basicConfig(filename=log_file, - filemode='w', + filemode='a', level=logging.DEBUG, format='%(name)s - %(levelname)s - %(message)s') else: @@ -170,8 +170,7 @@ def _get_rhythmic_widget( description='Start time dev (ms)', **kwargs) tstop = BoundedFloatText( - value=tstop_widget.value - if default_data['tstop'] == 0 else default_data['tstop'], + value=default_data['tstop'], description='Stop time (ms)', max=tstop_widget.value, **kwargs, @@ -246,8 +245,7 @@ def _get_poisson_widget( layout=layout, style=style) tstop = BoundedFloatText( - value=tstop_widget.value - if default_data['tstop'] == 0 else default_data['tstop'], + value=default_data['tstop'], max=tstop_widget.value, description='Stop time (ms)', layout=layout, @@ -383,8 +381,8 @@ def add_drive_widget(drive_type, name = drive_type + str(len(drive_boxes)) else: name = prespecified_drive_name - - if drive_type == 'Rhythmic': + logging.debug(f"add drive type to widget: {drive_type}") + if drive_type in ('Rhythmic', 'Bursty'): drive, drive_box = _get_rhythmic_widget( name, tstop_widget, @@ -408,7 +406,7 @@ def add_drive_widget(drive_type, default_weights_nmda=prespecified_weights_nmda, default_delays=prespecified_delays, ) - elif drive_type == 'Evoked': + elif drive_type in ('Evoked', 'Gaussian'): drive, drive_box = _get_evoked_widget( name, layout, @@ -424,6 +422,8 @@ def add_drive_widget(drive_type, 'Evoked', 'Poisson', 'Rhythmic', + 'Bursty', + 'Gaussian', ]: drive_boxes.append(drive_box) drive_widgets.append(drive) @@ -499,29 +499,25 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, for idx, drive_name in enumerate(drive_names): # order matters specs = drive_specs[drive_name] should_render = idx == (len(drive_names) - 1) - print(f"""load drive: - (name={drive_name}, - type={specs['type']}, - seed={specs['event_seed']}, - space_constant={specs['space_constant']} + try: + add_drive_widget( + specs['type'].capitalize(), + drive_boxes, + drive_widgets, + drives_out, + tstop, + specs['location'], + prespecified_drive_name=drive_name, + prespecified_drive_data=specs['dynamics'], + prespecified_weights_ampa=specs['weights_ampa'], + prespecified_weights_nmda=specs['weights_nmda'], + prespecified_delays=specs['synaptic_delays'], + render=should_render, + expand_last_drive=False, + event_seed=specs['event_seed'], ) - """) - add_drive_widget( - specs['type'].capitalize(), - drive_boxes, - drive_widgets, - drives_out, - tstop, - specs['location'], - prespecified_drive_name=drive_name, - prespecified_drive_data=specs['dynamics'], - prespecified_weights_ampa=specs['weights_ampa'], - prespecified_weights_nmda=specs['weights_nmda'], - prespecified_delays=specs['synaptic_delays'], - render=should_render, - expand_last_drive=False, - event_seed=specs['event_seed'], - ) + except Exception as e: + logging.debug(f"load drive from params err: {e}") def on_upload_change( @@ -583,7 +579,7 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, ) # add drives to network for drive in drive_widgets: - print(drive['type'], drive['name'], drive['seedcore'].value) + logging.debug(f"add drive type to network: {drive['type']}") weights_ampa = { k: v.value for k, v in drive['weights_ampa'].items() @@ -619,7 +615,7 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, synaptic_delays=synaptic_delays, space_constant=100.0, event_seed=drive['seedcore'].value) - elif drive['type'] == 'Evoked': + elif drive['type'] in ('Evoked', 'Gaussian'): variables['net'].add_evoked_drive( name=drive['name'], mu=drive['mu'].value, @@ -631,7 +627,7 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, synaptic_delays=synaptic_delays, space_constant=3.0, event_seed=drive['seedcore'].value) - elif drive['type'] == 'Rhythmic': + elif drive['type'] in ('Rhythmic', 'Bursty'): variables['net'].add_bursty_drive( name=drive['name'], tstart=drive['tstart'].value, @@ -1122,4 +1118,3 @@ def launch(): from voila.app import main notebook_path = op.join(op.dirname(__file__), 'hnn_widget.ipynb') main([notebook_path, *sys.argv[1:]]) - From 79eb35dfc7442b653add9acc843702945064f79c Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 02:37:44 -0400 Subject: [PATCH 78/88] Remove try except --- hnn_core/gui/gui.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 542402b70..7817667be 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -499,25 +499,24 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, for idx, drive_name in enumerate(drive_names): # order matters specs = drive_specs[drive_name] should_render = idx == (len(drive_names) - 1) - try: - add_drive_widget( - specs['type'].capitalize(), - drive_boxes, - drive_widgets, - drives_out, - tstop, - specs['location'], - prespecified_drive_name=drive_name, - prespecified_drive_data=specs['dynamics'], - prespecified_weights_ampa=specs['weights_ampa'], - prespecified_weights_nmda=specs['weights_nmda'], - prespecified_delays=specs['synaptic_delays'], - render=should_render, - expand_last_drive=False, - event_seed=specs['event_seed'], - ) - except Exception as e: - logging.debug(f"load drive from params err: {e}") + + add_drive_widget( + specs['type'].capitalize(), + drive_boxes, + drive_widgets, + drives_out, + tstop, + specs['location'], + prespecified_drive_name=drive_name, + prespecified_drive_data=specs['dynamics'], + prespecified_weights_ampa=specs['weights_ampa'], + prespecified_weights_nmda=specs['weights_nmda'], + prespecified_delays=specs['synaptic_delays'], + render=should_render, + expand_last_drive=False, + event_seed=specs['event_seed'], + ) + def on_upload_change( From 2cd74a2da85bebae8af334366ea9ff66fb183ad1 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 02:43:24 -0400 Subject: [PATCH 79/88] Fix formatting issues --- hnn_core/gui/gui.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 7817667be..09f8948c3 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -36,7 +36,6 @@ else: logging.basicConfig(level=logging.ERROR) - THEMECOLOR = "#8A2BE2" @@ -518,7 +517,6 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, ) - def on_upload_change( change, sliders, @@ -925,10 +923,9 @@ def _delete_drives_clicked(b): }) # header_button - header_button = HTML( - value= - f"""
- HUMAN NEOCORTICAL NEUROSOLVER
""") + header_button = HTML(value=f"""
+ HUMAN NEOCORTICAL NEUROSOLVER
""") # Simulation parameters tstop = FloatText(value=170, description='tstop (ms):', disabled=False) From 094a8ba03ef25d65d60b86146330f9071b281507 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 02:49:03 -0400 Subject: [PATCH 80/88] Update gui screenshot --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ab8f5ad1f..f296f82a9 100644 --- a/README.rst +++ b/README.rst @@ -13,8 +13,8 @@ hnn-core :target: https://codecov.io/gh/jonescompneurolab/hnn-core :alt: Test coverage -.. image:: https://user-images.githubusercontent.com/15852194/115123685-2df1ef00-9f8c-11eb-8f3b-663486466193.png - :target: https://user-images.githubusercontent.com/15852194/115123685-2df1ef00-9f8c-11eb-8f3b-663486466193.png +.. image:: https://user-images.githubusercontent.com/11160442/178095018-35d2619a-6a82-4e27-91c9-ff2796fab435.png + :target: https://user-images.githubusercontent.com/11160442/178095018-35d2619a-6a82-4e27-91c9-ff2796fab435.png :alt: HNN-core GUI This is a leaner and cleaner version of the code based off the `HNN repository `_. From 2f57f607a147f365ef5cddbb13d6e6fed4b96ebb Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 9 Jul 2022 13:23:42 -0400 Subject: [PATCH 81/88] MAINT: undo auto black code formatting --- MANIFEST.in | 1 + hnn_core/gui/gui.py | 215 ++++++++++++-------------------------------- 2 files changed, 60 insertions(+), 156 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 9b96281de..34a62f7d4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ recursive-include hnn_core/mod/x86_64 * recursive-include hnn_core/mod/x86_64/.libs * recursive-include hnn_core *.py +recursive-include hnn_core *.ipynb ### Exclude diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 09f8948c3..1ee46214b 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -139,17 +139,9 @@ def _get_cell_specific_widgets( return widgets_list, widgets_dict -def _get_rhythmic_widget( - name, - tstop_widget, - layout, - style, - location, - data=None, - default_weights_ampa=None, - default_weights_nmda=None, - default_delays=None, -): +def _get_rhythmic_widget(name, tstop_widget, layout, style, location, + data=None, default_weights_ampa=None, + default_weights_nmda=None, default_delays=None): default_data = { 'tstart': 0., 'tstart_std': 0., @@ -188,9 +180,7 @@ def _get_rhythmic_widget( **kwargs) widgets_list, widgets_dict = _get_cell_specific_widgets( - layout, - style, - location, + layout, style, location, data={ 'weights_ampa': default_weights_ampa, 'weights_nmda': default_weights_nmda, @@ -215,17 +205,9 @@ def _get_rhythmic_widget( return drive, drive_box -def _get_poisson_widget( - name, - tstop_widget, - layout, - style, - location, - data=None, - default_weights_ampa=None, - default_weights_nmda=None, - default_delays=None, -): +def _get_poisson_widget(name, tstop_widget, layout, style, location, data=None, + default_weights_ampa=None, default_weights_nmda=None, + default_delays=None): default_data = { 'tstart': 0.0, 'tstop': 0.0, @@ -294,16 +276,9 @@ def _get_poisson_widget( return drive, drive_box -def _get_evoked_widget( - name, - layout, - style, - location, - data=None, - default_weights_ampa=None, - default_weights_nmda=None, - default_delays=None, -): +def _get_evoked_widget(name, layout, style, location, data=None, + default_weights_ampa=None, default_weights_nmda=None, + default_delays=None): default_data = { 'mu': 0, 'sigma': 1, @@ -353,20 +328,13 @@ def _get_evoked_widget( return drive, drive_box -def add_drive_widget(drive_type, - drive_boxes, - drive_widgets, - drives_out, - tstop_widget, - location, - prespecified_drive_name=None, - prespecified_drive_data=None, - prespecified_weights_ampa=None, +def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, + tstop_widget, location, + prespecified_drive_name=None, prespecified_drive_data=None, + prespecified_weights_ampa=None, prespecified_weights_nmda=None, prespecified_delays=None, - render=True, - expand_last_drive=True, - event_seed=14): + render=True, expand_last_drive=True, event_seed=14): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} @@ -417,13 +385,8 @@ def add_drive_widget(drive_type, default_delays=prespecified_delays, ) - if drive_type in [ - 'Evoked', - 'Poisson', - 'Rhythmic', - 'Bursty', - 'Gaussian', - ]: + if drive_type in ['Evoked', 'Poisson', 'Rhythmic', 'Bursty', + 'Gaussian']: drive_boxes.append(drive_box) drive_widgets.append(drive) @@ -500,12 +463,8 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, should_render = idx == (len(drive_names) - 1) add_drive_widget( - specs['type'].capitalize(), - drive_boxes, - drive_widgets, - drives_out, - tstop, - specs['location'], + specs['type'].capitalize(), drive_boxes, + drive_widgets, drives_out, tstop, specs['location'], prespecified_drive_name=drive_name, prespecified_drive_data=specs['dynamics'], prespecified_weights_ampa=specs['weights_ampa'], @@ -517,19 +476,8 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, ) -def on_upload_change( - change, - sliders, - params, - tstop, - tstep, - log_out, - variables, - # for adding drives - drive_boxes, - drive_widgets, - drives_out, -): +def on_upload_change(change, sliders, params, tstop, tstep, log_out, variables, + drive_boxes, drive_widgets, drives_out): if len(change['owner'].value) == 0: return @@ -677,14 +625,9 @@ def handle_backend_change(backend_type, backend_config, mpi_cmd, joblib_cores): display(joblib_cores) -def init_left_right_viz_layout(plot_outputs, - plot_dropdowns, - window_height, - variables, - plot_options, - previous_outputs, - border='1px solid gray', - init=False): +def init_left_right_viz_layout(plot_outputs, plot_dropdowns, window_height, + variables, plot_options, previous_outputs, + border='1px solid gray', init=False): height_plot = window_height plot_outputs_L = Output(layout={'border': border, 'height': height_plot}) @@ -750,13 +693,9 @@ def init_left_right_viz_layout(plot_outputs, return grid -def init_upper_down_viz_layout(plot_outputs, - plot_dropdowns, - window_height, - variables, - plot_options, - previous_outputs, - border='1px solid gray', +def init_upper_down_viz_layout(plot_outputs, plot_dropdowns, + window_height, variables, plot_options, + previous_outputs, border='1px solid gray', init=False): height_plot = window_height default_plot_types = [plot_options[0], plot_options[1]] @@ -827,14 +766,9 @@ def init_upper_down_viz_layout(plot_outputs, return grid -def initialize_viz_window(viz_window, - variables, - plot_outputs, - plot_dropdowns, - window_width, - window_height, - layout_option="L-R", - init=False): +def initialize_viz_window(viz_window, variables, plot_outputs, + plot_dropdowns, window_width, window_height, + layout_option="L-R", init=False): plot_options = [ 'current dipole', 'input histogram', 'spikes', 'PSD', 'spectogram', 'network' @@ -843,29 +777,21 @@ def initialize_viz_window(viz_window, previous_plot_outputs_values = [] while len(plot_outputs) > 0: plot_outputs.pop() - # plot_dropdowns.pop() previous_plot_outputs_values.append(plot_dropdowns.pop().value) with viz_window: # Left-Rright configuration if layout_option == "L-R": - grid = init_left_right_viz_layout(plot_outputs, - plot_dropdowns, - window_height, - variables, - plot_options, - previous_plot_outputs_values, - init=init) + grid = init_left_right_viz_layout( + plot_outputs, plot_dropdowns, window_height, variables, + plot_options, previous_plot_outputs_values, init=init) + # Upper-Down configuration elif layout_option == "U-D": - grid = init_upper_down_viz_layout(plot_outputs, - plot_dropdowns, - window_height, - variables, - plot_options, - previous_plot_outputs_values, - init=init) - # TODO: 2x2 + grid = init_upper_down_viz_layout( + plot_outputs, plot_dropdowns, window_height, + variables, plot_options, previous_plot_outputs_values, + init=init) display(grid) @@ -931,52 +857,38 @@ def _delete_drives_clicked(b): tstop = FloatText(value=170, description='tstop (ms):', disabled=False) tstep = FloatText(value=0.025, description='tstep (ms):', disabled=False) ntrials = IntText(value=1, description='Trials:', disabled=False) - # temporarily keep this - + + # visualization layout viz_layout_selection = Dropdown( options=[('Horizontal', 'L-R'), ('Vertical', 'U-D')], - value='L-R', - description='Layout:', - ) + value='L-R', description='Layout:') # initialize - initialize_viz_window(viz_window, - variables, - plot_outputs_list, - plot_dropdowns_list, - viz_width, - viz_height, - layout_option=viz_layout_selection.value, + initialize_viz_window(viz_window, variables, plot_outputs_list, + plot_dropdowns_list, viz_width, + viz_height, layout_option=viz_layout_selection.value, init=True) def handle_viz_layout_change(layout_option): - return initialize_viz_window( - viz_window, - variables, - plot_outputs_list, - plot_dropdowns_list, - viz_width, - viz_height, - layout_option=layout_option.new, - ) + return initialize_viz_window(viz_window, variables, + plot_outputs_list, plot_dropdowns_list, + viz_width, viz_height, + layout_option=layout_option.new) viz_layout_selection.observe(handle_viz_layout_change, 'value') + # select backends backend_selection = Dropdown( options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], value='MPI' if os.getenv("USEMPI", '0') == '1' else 'Joblib', description='Backend:', ) - mpi_cmd = Text(value='mpiexec', - placeholder='Fill if applies', - description='MPI cmd:', - disabled=False) + mpi_cmd = Text(value='mpiexec', placeholder='Fill if applies', + description='MPI cmd:', disabled=False) - joblib_cores = BoundedIntText(value=1, - min=1, + joblib_cores = BoundedIntText(value=1, min=1, max=multiprocessing.cpu_count(), - description='Cores:', - disabled=False) + description='Cores:', disabled=False) backend_config = Output() @@ -988,13 +900,8 @@ def _handle_backend_change(backend_type): joblib_cores) backend_selection.observe(_handle_backend_change, 'value') - simulation_box = VBox([ - tstop, - tstep, - ntrials, - backend_selection, - backend_config, - ]) + simulation_box = VBox([tstop, tstep, ntrials, backend_selection, + backend_config]) # Sliders to change local-connectivity params sliders = [ @@ -1041,8 +948,7 @@ def _handle_backend_change(backend_type): disabled=False, layout=layout) - add_drive_button = create_expanded_button('Add drive', - 'primary', + add_drive_button = create_expanded_button('Add drive', 'primary', height='30px') def _add_drive_button_clicked(b): @@ -1072,13 +978,10 @@ def _add_drive_button_clicked(b): run_button = create_expanded_button('Run', 'success', height='30px') style = {'button_color': THEMECOLOR} - load_button = FileUpload(accept='.json,.param', - multiple=False, - style=style, - description='Load network', + load_button = FileUpload(accept='.json,.param', multiple=False, + style=style, description='Load network', button_style='success') - delete_button = create_expanded_button('Delete drives', - 'success', + delete_button = create_expanded_button('Delete drives', 'success', height='30px') load_button.observe(_on_upload_change) From 01e47aa2d71bf3c1c9ad7ef5347703bf9a14cee0 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 9 Jul 2022 14:09:33 -0400 Subject: [PATCH 82/88] MAINT: no env variables --- hnn_core/gui/gui.py | 22 +++------------------- hnn_core/tests/test_gui.py | 1 - 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 1ee46214b..ba9c43ae8 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -26,15 +26,6 @@ _read_legacy_params) from hnn_core.viz import plot_dipole -log_file = os.getenv("HNNGUI_LOGFILE", None) -debug_gui = os.getenv("DEBUG_HNNGUI", "0") -if debug_gui == '1' and log_file is not None: - logging.basicConfig(filename=log_file, - filemode='a', - level=logging.DEBUG, - format='%(name)s - %(levelname)s - %(message)s') -else: - logging.basicConfig(level=logging.ERROR) THEMECOLOR = "#8A2BE2" @@ -74,12 +65,7 @@ def _update_params(**updates): return sliders -def _get_cell_specific_widgets( - layout, - style, - location, - data=None, -): +def _get_cell_specific_widgets(layout, style, location, data=None): default_data = { 'weights_ampa': { 'L5_pyramidal': 0., @@ -878,10 +864,8 @@ def handle_viz_layout_change(layout_option): # select backends backend_selection = Dropdown( - options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], - value='MPI' if os.getenv("USEMPI", '0') == '1' else 'Joblib', - description='Backend:', - ) + options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], value='Joblib', + description='Backend:') mpi_cmd = Text(value='mpiexec', placeholder='Fill if applies', description='MPI cmd:', disabled=False) diff --git a/hnn_core/tests/test_gui.py b/hnn_core/tests/test_gui.py index 06ba7d5b0..2232d06c1 100644 --- a/hnn_core/tests/test_gui.py +++ b/hnn_core/tests/test_gui.py @@ -6,6 +6,5 @@ def test_run_gui(): """Test if main gui function gives proper ipywidget""" - os.environ["DEBUG_HNNGUI"] = "0" gui = run_hnn_gui() assert gui is not None From d49bd0dfb5b55574c63caf890eb3a6b4534b4ac9 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 9 Jul 2022 14:26:17 -0400 Subject: [PATCH 83/88] Cleanup --- hnn_core/gui/gui.py | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index ba9c43ae8..bc99a3bb5 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -6,12 +6,12 @@ import codecs import logging import multiprocessing -import os import os.path as op import sys import matplotlib.pyplot as plt import numpy as np + from IPython.display import display from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, BoundedIntText, Button, Dropdown, FileUpload, @@ -316,8 +316,9 @@ def _get_evoked_widget(name, layout, style, location, data=None, def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, tstop_widget, location, - prespecified_drive_name=None, prespecified_drive_data=None, - prespecified_weights_ampa=None, + prespecified_drive_name=None, + prespecified_drive_data=None, + prespecified_weights_ampa=None, prespecified_weights_nmda=None, prespecified_delays=None, render=True, expand_last_drive=True, event_seed=14): @@ -449,7 +450,7 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, should_render = idx == (len(drive_names) - 1) add_drive_widget( - specs['type'].capitalize(), drive_boxes, + specs['type'].capitalize(), drive_boxes, drive_widgets, drives_out, tstop, specs['location'], prespecified_drive_name=drive_name, prespecified_drive_data=specs['dynamics'], @@ -797,6 +798,8 @@ def run_hnn_gui(): plot_outputs_list = list() plot_dropdowns_list = list() + # ##### Callbacks ####### + def _run_button_clicked(b): return run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, ntrials, backend_selection, mpi_cmd, @@ -818,6 +821,23 @@ def _delete_drives_clicked(b): drive_widgets.pop() drive_boxes.pop() + def handle_viz_layout_change(layout_option): + return initialize_viz_window(viz_window, variables, + plot_outputs_list, plot_dropdowns_list, + viz_width, viz_height, + layout_option=layout_option.new) + + def _handle_backend_change(backend_type): + return handle_backend_change(backend_type.new, backend_config, mpi_cmd, + joblib_cores) + + def _add_drive_button_clicked(b): + return add_drive_widget(drive_type_selection.value, drive_boxes, + drive_widgets, drives_out, tstop, + location_selection.value) + + # ##### Layout ####### + # Output windows drives_out = Output() # window to add new drives @@ -843,7 +863,7 @@ def _delete_drives_clicked(b): tstop = FloatText(value=170, description='tstop (ms):', disabled=False) tstep = FloatText(value=0.025, description='tstep (ms):', disabled=False) ntrials = IntText(value=1, description='Trials:', disabled=False) - + # visualization layout viz_layout_selection = Dropdown( options=[('Horizontal', 'L-R'), ('Vertical', 'U-D')], @@ -854,12 +874,6 @@ def _delete_drives_clicked(b): viz_height, layout_option=viz_layout_selection.value, init=True) - def handle_viz_layout_change(layout_option): - return initialize_viz_window(viz_window, variables, - plot_outputs_list, plot_dropdowns_list, - viz_width, viz_height, - layout_option=layout_option.new) - viz_layout_selection.observe(handle_viz_layout_change, 'value') # select backends @@ -876,10 +890,6 @@ def handle_viz_layout_change(layout_option): backend_config = Output() - def _handle_backend_change(backend_type): - return handle_backend_change(backend_type.new, backend_config, mpi_cmd, - joblib_cores) - handle_backend_change(backend_selection.value, backend_config, mpi_cmd, joblib_cores) backend_selection.observe(_handle_backend_change, 'value') @@ -935,11 +945,6 @@ def _handle_backend_change(backend_type): add_drive_button = create_expanded_button('Add drive', 'primary', height='30px') - def _add_drive_button_clicked(b): - return add_drive_widget(drive_type_selection.value, drive_boxes, - drive_widgets, drives_out, tstop, - location_selection.value) - add_drive_button.on_click(_add_drive_button_clicked) drive_selections = VBox( [HBox([drive_type_selection, location_selection]), add_drive_button]) From b41405d63034bc1b5d5916a64001992dad951aa6 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sat, 9 Jul 2022 14:27:45 -0400 Subject: [PATCH 84/88] DOC: Readme --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index f296f82a9..602d1b99a 100644 --- a/README.rst +++ b/README.rst @@ -77,6 +77,10 @@ To install the GUI dependencies along with ``hnn-core``, a simple tweak to the a $ pip install hnn_core[gui] +To start the GUI, please do:: + + $ hnn-gui + **Parallel backends** For further instructions on installation and usage of parallel backends for using more From 3d9082d13d03109e022119b2f64dba868338a1bb Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 23:49:25 -0400 Subject: [PATCH 85/88] Fix error --- hnn_core/gui/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index bc99a3bb5..52a8bb4ff 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -88,7 +88,7 @@ def _get_cell_specific_widgets(layout, style, location, data=None): } if isinstance(data, dict): for k in default_data.keys(): - if k in data: + if k in data and data[k] is not None: default_data[k].update(data[k]) kwargs = dict(layout=layout, style=style) From fde873c935abb214c2167dd941245190c05af781 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Sat, 9 Jul 2022 23:53:40 -0400 Subject: [PATCH 86/88] Fix formatting error --- hnn_core/tests/test_gui.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hnn_core/tests/test_gui.py b/hnn_core/tests/test_gui.py index 2232d06c1..53b93efea 100644 --- a/hnn_core/tests/test_gui.py +++ b/hnn_core/tests/test_gui.py @@ -1,6 +1,4 @@ # Authors: Huzi Cheng -import os - from hnn_core.gui.gui import run_hnn_gui From e857f243d715f683997de649e351137036951180 Mon Sep 17 00:00:00 2001 From: Mainak Jas Date: Sun, 10 Jul 2022 18:34:46 -0400 Subject: [PATCH 87/88] MAINT: add connecting to callbacks at the end --- hnn_core/gui/gui.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 52a8bb4ff..25744fd99 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -841,10 +841,10 @@ def _add_drive_button_clicked(b): # Output windows drives_out = Output() # window to add new drives - log_out_heiht = "100px" + log_out_height = "100px" log_out = Output(layout={ 'border': '1px solid gray', - 'height': log_out_heiht, + 'height': log_out_height, 'overflow': 'auto' }) viz_width = "1000px" @@ -874,8 +874,6 @@ def _add_drive_button_clicked(b): viz_height, layout_option=viz_layout_selection.value, init=True) - viz_layout_selection.observe(handle_viz_layout_change, 'value') - # select backends backend_selection = Dropdown( options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], value='Joblib', @@ -889,10 +887,8 @@ def _add_drive_button_clicked(b): description='Cores:', disabled=False) backend_config = Output() - handle_backend_change(backend_selection.value, backend_config, mpi_cmd, joblib_cores) - backend_selection.observe(_handle_backend_change, 'value') simulation_box = VBox([tstop, tstep, ntrials, backend_selection, backend_config]) @@ -945,7 +941,6 @@ def _add_drive_button_clicked(b): add_drive_button = create_expanded_button('Add drive', 'primary', height='30px') - add_drive_button.on_click(_add_drive_button_clicked) drive_selections = VBox( [HBox([drive_type_selection, location_selection]), add_drive_button]) @@ -970,17 +965,13 @@ def _add_drive_button_clicked(b): load_button = FileUpload(accept='.json,.param', multiple=False, style=style, description='Load network', button_style='success') - delete_button = create_expanded_button('Delete drives', 'success', - height='30px') - - load_button.observe(_on_upload_change) - run_button.on_click(_run_button_clicked) + delete_drive_button = create_expanded_button('Delete drives', 'success', + height='30px') - delete_button.on_click(_delete_drives_clicked) left_width = '380px' footer = VBox([ HBox([ - HBox([run_button, load_button, delete_button], + HBox([run_button, load_button, delete_drive_button], layout={"width": left_width}), viz_layout_selection, ]), log_out, simulation_status @@ -999,6 +990,15 @@ def _add_drive_button_clicked(b): pane_widths=[left_width, '0px', viz_width], pane_heights=['50px', viz_height, "1"], ) + + # ######## connect to callbacks ########## + backend_selection.observe(_handle_backend_change, 'value') + add_drive_button.on_click(_add_drive_button_clicked) + delete_drive_button.on_click(_delete_drives_clicked) + load_button.observe(_on_upload_change) + run_button.on_click(_run_button_clicked) + viz_layout_selection.observe(handle_viz_layout_change, 'value') + return hnn_gui From 471058f9ca58d818b93778a99981379ee6c834d5 Mon Sep 17 00:00:00 2001 From: chenghuzi Date: Mon, 11 Jul 2022 01:23:45 -0400 Subject: [PATCH 88/88] Fix layout --- hnn_core/gui/gui.py | 269 +++++++++++++++++++++++++++++--------------- 1 file changed, 177 insertions(+), 92 deletions(-) diff --git a/hnn_core/gui/gui.py b/hnn_core/gui/gui.py index 25744fd99..26819ca16 100644 --- a/hnn_core/gui/gui.py +++ b/hnn_core/gui/gui.py @@ -16,7 +16,7 @@ from ipywidgets import (HTML, Accordion, AppLayout, BoundedFloatText, BoundedIntText, Button, Dropdown, FileUpload, FloatLogSlider, FloatText, GridspecLayout, HBox, - IntText, Label, Layout, Output, RadioButtons, Tab, + IntText, Layout, Output, RadioButtons, Tab, Text, VBox, interactive_output) import hnn_core @@ -26,7 +26,6 @@ _read_legacy_params) from hnn_core.viz import plot_dipole - THEMECOLOR = "#8A2BE2" @@ -125,9 +124,15 @@ def _get_cell_specific_widgets(layout, style, location, data=None): return widgets_list, widgets_dict -def _get_rhythmic_widget(name, tstop_widget, layout, style, location, - data=None, default_weights_ampa=None, - default_weights_nmda=None, default_delays=None): +def _get_rhythmic_widget(name, + tstop_widget, + layout, + style, + location, + data=None, + default_weights_ampa=None, + default_weights_nmda=None, + default_delays=None): default_data = { 'tstart': 0., 'tstart_std': 0., @@ -166,17 +171,18 @@ def _get_rhythmic_widget(name, tstop_widget, layout, style, location, **kwargs) widgets_list, widgets_dict = _get_cell_specific_widgets( - layout, style, location, + layout, + style, + location, data={ 'weights_ampa': default_weights_ampa, 'weights_nmda': default_weights_nmda, 'delays': default_delays, }, ) - drive_box = VBox([ - HTML(value=f"

Location: {location}

"), tstart, tstart_std, tstop, - burst_rate, burst_std, repeats, seedcore - ] + widgets_list) + drive_box = VBox( + [tstart, tstart_std, tstop, burst_rate, burst_std, repeats, seedcore] + + widgets_list) drive = dict(type='Rhythmic', name=name, tstart=tstart, @@ -191,8 +197,14 @@ def _get_rhythmic_widget(name, tstop_widget, layout, style, location, return drive, drive_box -def _get_poisson_widget(name, tstop_widget, layout, style, location, data=None, - default_weights_ampa=None, default_weights_nmda=None, +def _get_poisson_widget(name, + tstop_widget, + layout, + style, + location, + data=None, + default_weights_ampa=None, + default_weights_nmda=None, default_delays=None): default_data = { 'tstart': 0.0, @@ -246,9 +258,7 @@ def _get_poisson_widget(name, tstop_widget, layout, style, location, data=None, widgets_list.extend([HTML(value="Rate constants")] + list(widgets_dict['rate_constant'].values())) - drive_box = VBox( - [HTML(value=f"

Location: {location}

"), tstart, tstop, seedcore] + - widgets_list) + drive_box = VBox([tstart, tstop, seedcore] + widgets_list) drive = dict( type='Poisson', name=name, @@ -262,8 +272,13 @@ def _get_poisson_widget(name, tstop_widget, layout, style, location, data=None, return drive, drive_box -def _get_evoked_widget(name, layout, style, location, data=None, - default_weights_ampa=None, default_weights_nmda=None, +def _get_evoked_widget(name, + layout, + style, + location, + data=None, + default_weights_ampa=None, + default_weights_nmda=None, default_delays=None): default_data = { 'mu': 0, @@ -298,10 +313,7 @@ def _get_evoked_widget(name, layout, style, location, data=None, }, ) - drive_box = VBox([ - HTML(value=f"

Location: {location}

"), mu, sigma, numspikes, - seedcore - ] + widgets_list) + drive_box = VBox([mu, sigma, numspikes, seedcore] + widgets_list) drive = dict(type='Evoked', name=name, mu=mu, @@ -314,14 +326,20 @@ def _get_evoked_widget(name, layout, style, location, data=None, return drive, drive_box -def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, - tstop_widget, location, +def add_drive_widget(drive_type, + drive_boxes, + drive_widgets, + drives_out, + tstop_widget, + location, prespecified_drive_name=None, prespecified_drive_data=None, prespecified_weights_ampa=None, prespecified_weights_nmda=None, prespecified_delays=None, - render=True, expand_last_drive=True, event_seed=14): + render=True, + expand_last_drive=True, + event_seed=14): """Add a widget for a new drive.""" layout = Layout(width='270px', height='auto') style = {'description_width': '150px'} @@ -372,8 +390,9 @@ def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, default_delays=prespecified_delays, ) - if drive_type in ['Evoked', 'Poisson', 'Rhythmic', 'Bursty', - 'Gaussian']: + if drive_type in [ + 'Evoked', 'Poisson', 'Rhythmic', 'Bursty', 'Gaussian' + ]: drive_boxes.append(drive_box) drive_widgets.append(drive) @@ -384,7 +403,8 @@ def add_drive_widget(drive_type, drive_boxes, drive_widgets, drives_out, 1 if expand_last_drive else None, ) for idx, drive in enumerate(drive_widgets): - accordion.set_title(idx, drive['name']) + accordion.set_title(idx, + f"{drive['name']} ({drive['location']})") display(accordion) @@ -399,34 +419,51 @@ def update_plot_window(variables, _plot_out, plot_type): with _plot_out: if plot_type['new'] == 'spikes': - fig, ax = plt.subplots() - variables['net'].cell_response.plot_spikes_raster(ax=ax) + if variables['net'].cell_response: + fig, ax = plt.subplots() + variables['net'].cell_response.plot_spikes_raster(ax=ax) + else: + print("No cell response data") elif plot_type['new'] == 'current dipole': - fig, ax = plt.subplots() - # variables['dpls'][0].plot(ax=ax) - plot_dipole(variables['dpls'], ax=ax, average=True) + if variables['dpls'] is not None: + fig, ax = plt.subplots() + plot_dipole(variables['dpls'], ax=ax, average=True) + else: + print("No dipole data") elif plot_type['new'] == 'input histogram': # BUG: got error here, need a better way to handle exception - fig, ax = plt.subplots() - variables['net'].cell_response.plot_spikes_hist(ax=ax) + if variables['net'].cell_response: + fig, ax = plt.subplots() + variables['net'].cell_response.plot_spikes_hist(ax=ax) + else: + print("No cell response data") elif plot_type['new'] == 'PSD': - fig, ax = plt.subplots() - variables['dpls'][0].plot_psd(fmin=0, fmax=50, ax=ax) + if variables['dpls'] is not None: + fig, ax = plt.subplots() + variables['dpls'][0].plot_psd(fmin=0, fmax=50, ax=ax) + else: + print("No dipole data") elif plot_type['new'] == 'spectogram': - freqs = np.arange(10., 100., 1.) - n_cycles = freqs / 8. - fig, ax = plt.subplots() - variables['dpls'][0].plot_tfr_morlet(freqs, - n_cycles=n_cycles, - ax=ax) + if variables['dpls'] is not None: + freqs = np.arange(10., 100., 1.) + n_cycles = freqs / 8. + fig, ax = plt.subplots() + variables['dpls'][0].plot_tfr_morlet(freqs, + n_cycles=n_cycles, + ax=ax) + else: + print("No dipole data") elif plot_type['new'] == 'network': - fig = plt.figure() - ax = fig.add_subplot(111, projection='3d') - variables['net'].plot_cells(ax=ax) + if variables['net']: + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + variables['net'].plot_cells(ax=ax) + else: + print("No network data") def load_drives(variables, params, log_out, drives_out, drive_widgets, @@ -450,8 +487,12 @@ def load_drives(variables, params, log_out, drives_out, drive_widgets, should_render = idx == (len(drive_names) - 1) add_drive_widget( - specs['type'].capitalize(), drive_boxes, - drive_widgets, drives_out, tstop, specs['location'], + specs['type'].capitalize(), + drive_boxes, + drive_widgets, + drives_out, + tstop, + specs['location'], prespecified_drive_name=drive_name, prespecified_drive_data=specs['dynamics'], prespecified_weights_ampa=specs['weights_ampa'], @@ -581,13 +622,17 @@ def run_button_clicked(log_out, drive_widgets, variables, tstep, tstop, variables['backend'] = JoblibBackend(n_jobs=joblib_cores.value) print(f"Using Joblib with {joblib_cores.value} core(s).") with variables['backend']: - simulation_status.value = "Running..." + simulation_status.value = """
+ Running...
""" variables['dpls'] = simulate_dipole(variables['net'], tstop=tstop.value, n_trials=ntrials.value) window_len, scaling_factor = 30, 3000 - simulation_status.value = "Simulation finished" + simulation_status.value = """
+ Simulation finished
""" for dpl in variables['dpls']: dpl.smooth(window_len).scale(scaling_factor) @@ -612,9 +657,14 @@ def handle_backend_change(backend_type, backend_config, mpi_cmd, joblib_cores): display(joblib_cores) -def init_left_right_viz_layout(plot_outputs, plot_dropdowns, window_height, - variables, plot_options, previous_outputs, - border='1px solid gray', init=False): +def init_left_right_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + previous_outputs, + border='1px solid gray', + init=False): height_plot = window_height plot_outputs_L = Output(layout={'border': border, 'height': height_plot}) @@ -680,9 +730,13 @@ def init_left_right_viz_layout(plot_outputs, plot_dropdowns, window_height, return grid -def init_upper_down_viz_layout(plot_outputs, plot_dropdowns, - window_height, variables, plot_options, - previous_outputs, border='1px solid gray', +def init_upper_down_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + previous_outputs, + border='1px solid gray', init=False): height_plot = window_height default_plot_types = [plot_options[0], plot_options[1]] @@ -753,9 +807,14 @@ def init_upper_down_viz_layout(plot_outputs, plot_dropdowns, return grid -def initialize_viz_window(viz_window, variables, plot_outputs, - plot_dropdowns, window_width, window_height, - layout_option="L-R", init=False): +def initialize_viz_window(viz_window, + variables, + plot_outputs, + plot_dropdowns, + window_width, + window_height, + layout_option="L-R", + init=False): plot_options = [ 'current dipole', 'input histogram', 'spikes', 'PSD', 'spectogram', 'network' @@ -769,16 +828,23 @@ def initialize_viz_window(viz_window, variables, plot_outputs, with viz_window: # Left-Rright configuration if layout_option == "L-R": - grid = init_left_right_viz_layout( - plot_outputs, plot_dropdowns, window_height, variables, - plot_options, previous_plot_outputs_values, init=init) + grid = init_left_right_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + previous_plot_outputs_values, + init=init) # Upper-Down configuration elif layout_option == "U-D": - grid = init_upper_down_viz_layout( - plot_outputs, plot_dropdowns, window_height, - variables, plot_options, previous_plot_outputs_values, - init=init) + grid = init_upper_down_viz_layout(plot_outputs, + plot_dropdowns, + window_height, + variables, + plot_options, + previous_plot_outputs_values, + init=init) display(grid) @@ -822,9 +888,12 @@ def _delete_drives_clicked(b): drive_boxes.pop() def handle_viz_layout_change(layout_option): - return initialize_viz_window(viz_window, variables, - plot_outputs_list, plot_dropdowns_list, - viz_width, viz_height, + return initialize_viz_window(viz_window, + variables, + plot_outputs_list, + plot_dropdowns_list, + viz_width, + viz_height, layout_option=layout_option.new) def _handle_backend_change(backend_type): @@ -865,33 +934,43 @@ def _add_drive_button_clicked(b): ntrials = IntText(value=1, description='Trials:', disabled=False) # visualization layout - viz_layout_selection = Dropdown( - options=[('Horizontal', 'L-R'), ('Vertical', 'U-D')], - value='L-R', description='Layout:') + viz_layout_selection = Dropdown(options=[('Horizontal', 'L-R'), + ('Vertical', 'U-D')], + value='L-R', + description='Layout:') # initialize - initialize_viz_window(viz_window, variables, plot_outputs_list, - plot_dropdowns_list, viz_width, - viz_height, layout_option=viz_layout_selection.value, + initialize_viz_window(viz_window, + variables, + plot_outputs_list, + plot_dropdowns_list, + viz_width, + viz_height, + layout_option=viz_layout_selection.value, init=True) # select backends - backend_selection = Dropdown( - options=[('Joblib', 'Joblib'), ('MPI', 'MPI')], value='Joblib', - description='Backend:') - - mpi_cmd = Text(value='mpiexec', placeholder='Fill if applies', - description='MPI cmd:', disabled=False) - - joblib_cores = BoundedIntText(value=1, min=1, + backend_selection = Dropdown(options=[('Joblib', 'Joblib'), + ('MPI', 'MPI')], + value='Joblib', + description='Backend:') + + mpi_cmd = Text(value='mpiexec', + placeholder='Fill if applies', + description='MPI cmd:', + disabled=False) + + joblib_cores = BoundedIntText(value=1, + min=1, max=multiprocessing.cpu_count(), - description='Cores:', disabled=False) + description='Cores:', + disabled=False) backend_config = Output() handle_backend_change(backend_selection.value, backend_config, mpi_cmd, joblib_cores) - simulation_box = VBox([tstop, tstep, ntrials, backend_selection, - backend_config]) + simulation_box = VBox( + [tstop, tstep, ntrials, backend_selection, backend_config]) # Sliders to change local-connectivity params sliders = [ @@ -923,7 +1002,7 @@ def _add_drive_button_clicked(b): cell_connectivity.set_title(idx, title) # Dropdown for different drives - layout = Layout(width='200px', height='100px') + layout = Layout(width='200px') drive_type_selection = RadioButtons( options=['Evoked', 'Poisson', 'Rhythmic'], @@ -938,11 +1017,12 @@ def _add_drive_button_clicked(b): disabled=False, layout=layout) - add_drive_button = create_expanded_button('Add drive', 'primary', + add_drive_button = create_expanded_button('Add drive', + 'primary', height='30px') drive_selections = VBox( - [HBox([drive_type_selection, location_selection]), add_drive_button]) + [drive_type_selection, location_selection, add_drive_button]) # XXX: should be simpler to use Stacked class starting # from IPywidgets > 8.0 @@ -956,16 +1036,21 @@ def _add_drive_button_clicked(b): left_tab.set_title(idx, title) # Running status - simulation_status = Label(value="Not running") + simulation_status = HTML(value="""
+ Not running
""") # Run, delete drives and load button run_button = create_expanded_button('Run', 'success', height='30px') style = {'button_color': THEMECOLOR} - load_button = FileUpload(accept='.json,.param', multiple=False, - style=style, description='Load network', + load_button = FileUpload(accept='.json,.param', + multiple=False, + style=style, + description='Load network', button_style='success') - delete_drive_button = create_expanded_button('Delete drives', 'success', + delete_drive_button = create_expanded_button('Delete drives', + 'success', height='30px') left_width = '380px'