bmtool 0.7.1.6__py3-none-any.whl → 0.7.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bmtool/bmplot/spikes.py CHANGED
@@ -20,6 +20,7 @@ def raster(
20
20
  tstart: Optional[float] = None,
21
21
  tstop: Optional[float] = None,
22
22
  color_map: Optional[Dict[str, str]] = None,
23
+ dot_size: Optional[float] = 0.3,
23
24
  ) -> Axes:
24
25
  """
25
26
  Plots a raster plot of neural spikes, with different colors for each population.
@@ -40,6 +41,8 @@ def raster(
40
41
  Stop time for filtering spikes; only spikes with timestamps less than `tstop` will be plotted.
41
42
  color_map : dict, optional
42
43
  Dictionary specifying colors for each population. Keys should be population names, and values should be color values.
44
+ dot_size: float, optional
45
+ Size of the dot to display on the scatterplot
43
46
 
44
47
  Returns:
45
48
  -------
@@ -53,6 +56,7 @@ def raster(
53
56
  - If `color_map` is provided, it should contain colors for all unique `pop_name` values in `spikes_df`.
54
57
  """
55
58
  # Initialize axes if none provided
59
+ sns.set_style("whitegrid")
56
60
  if ax is None:
57
61
  _, ax = plt.subplots(1, 1)
58
62
 
@@ -102,15 +106,17 @@ def raster(
102
106
  raise ValueError(f"color_map is missing colors for populations: {missing_colors}")
103
107
 
104
108
  # Plot each population with its specified or generated color
109
+ legend_handles = []
105
110
  for pop_name, group in spikes_df.groupby(groupby):
106
- ax.scatter(
107
- group["timestamps"], group["node_ids"], label=pop_name, color=color_map[pop_name], s=0.5
108
- )
111
+ ax.scatter(group["timestamps"], group["node_ids"], color=color_map[pop_name], s=dot_size)
112
+ # Dummy scatter for consistent legend appearance
113
+ handle = ax.scatter([], [], color=color_map[pop_name], label=pop_name, s=20)
114
+ legend_handles.append(handle)
109
115
 
110
116
  # Label axes
111
117
  ax.set_xlabel("Time")
112
118
  ax.set_ylabel("Node ID")
113
- ax.legend(title="Population", loc="upper right", framealpha=0.9, markerfirst=False)
119
+ ax.legend(handles=legend_handles, title="Population", loc="upper right", framealpha=0.9)
114
120
 
115
121
  return ax
116
122
 
@@ -142,6 +148,7 @@ def plot_firing_rate_pop_stats(
142
148
  Axes with the bar plot.
143
149
  """
144
150
  # Ensure groupby is a list for consistent handling
151
+ sns.set_style("whitegrid")
145
152
  if isinstance(groupby, str):
146
153
  groupby = [groupby]
147
154
 
@@ -234,6 +241,7 @@ def plot_firing_rate_distribution(
234
241
  matplotlib.axes.Axes
235
242
  Axes with the selected plot type(s) overlayed.
236
243
  """
244
+ sns.set_style("whitegrid")
237
245
  # Ensure groupby is a list for consistent handling
238
246
  if isinstance(groupby, str):
239
247
  groupby = [groupby]
@@ -287,8 +295,9 @@ def plot_firing_rate_distribution(
287
295
  y="firing_rate",
288
296
  ax=ax,
289
297
  palette=color_map,
290
- inner="quartile",
298
+ inner="box",
291
299
  alpha=0.4,
300
+ cut=0, # This prevents the KDE from extending beyond the data range
292
301
  )
293
302
  elif pt == "swarm":
294
303
  sns.swarmplot(
@@ -308,3 +317,107 @@ def plot_firing_rate_distribution(
308
317
  ax.grid(axis="y", linestyle="--", alpha=0.7)
309
318
 
310
319
  return ax
320
+
321
+
322
+ def plot_firing_rate_vs_node_attribute(
323
+ individual_stats: Optional[pd.DataFrame] = None,
324
+ config: Optional[str] = None,
325
+ nodes: Optional[pd.DataFrame] = None,
326
+ groupby: Optional[str] = None,
327
+ network_name: Optional[str] = None,
328
+ attribute: Optional[str] = None,
329
+ figsize=(12, 8),
330
+ dot_size: float = 3,
331
+ ) -> plt.Figure:
332
+ """
333
+ Plot firing rate vs node attribute for each group in separate subplots.
334
+
335
+ Parameters
336
+ ----------
337
+ individual_stats : pd.DataFrame, optional
338
+ DataFrame containing individual cell firing rates from compute_firing_rate_stats
339
+ config : str, optional
340
+ Path to configuration file for loading node data
341
+ nodes : pd.DataFrame, optional
342
+ Pre-loaded node data as alternative to loading from config
343
+ groupby : str, optional
344
+ Column name in individual_stats to group plots by
345
+ network_name : str, optional
346
+ Name of network to load from config file
347
+ attribute : str, optional
348
+ Node attribute column name to plot against firing rate
349
+ figsize : tuple[int, int], optional
350
+ Figure dimensions (width, height) in inches
351
+ dot_size : float, optional
352
+ Size of scatter plot points
353
+
354
+ Returns
355
+ -------
356
+ matplotlib.figure.Figure
357
+ Figure containing the subplots
358
+
359
+ Raises
360
+ ------
361
+ ValueError
362
+ If neither config nor nodes is provided
363
+ If network_name is missing when using config
364
+ If attribute is not found in nodes DataFrame
365
+ If node_ids column is missing
366
+ If nodes index is not unique
367
+ """
368
+ # Input validation
369
+ if config is None and nodes is None:
370
+ raise ValueError("Must provide either config or nodes")
371
+ if config is not None and nodes is None:
372
+ if network_name is None:
373
+ raise ValueError("network_name required when using config")
374
+ nodes = load_nodes_from_config(config)
375
+ if attribute not in nodes.columns:
376
+ raise ValueError(f"Attribute '{attribute}' not found in nodes DataFrame")
377
+
378
+ # Extract node attribute data
379
+ node_attribute = nodes[attribute]
380
+
381
+ # Validate data structure
382
+ if "node_ids" not in individual_stats.columns:
383
+ raise ValueError("individual_stats missing required 'node_ids' column")
384
+ if not nodes.index.is_unique:
385
+ raise ValueError("nodes DataFrame must have unique index for merging")
386
+
387
+ # Merge firing rate data with node attributes
388
+ merged_df = individual_stats.merge(
389
+ node_attribute, left_on="node_ids", right_index=True, how="left"
390
+ )
391
+
392
+ # Setup subplot layout
393
+ max_groups = 15 # Maximum number of subplots to avoid overcrowding
394
+ unique_groups = merged_df[groupby].unique()
395
+ n_groups = min(len(unique_groups), max_groups)
396
+
397
+ if len(unique_groups) > max_groups:
398
+ print(f"Warning: Limiting display to {max_groups} groups out of {len(unique_groups)}")
399
+ unique_groups = unique_groups[:max_groups]
400
+
401
+ n_cols = min(3, n_groups)
402
+ n_rows = (n_groups + n_cols - 1) // n_cols
403
+
404
+ # Create subplots
405
+ fig, axes = plt.subplots(n_rows, n_cols, figsize=figsize)
406
+ if n_groups == 1:
407
+ axes = np.array([axes])
408
+ axes = axes.flatten()
409
+
410
+ # Plot each group
411
+ for i, group in enumerate(unique_groups):
412
+ group_df = merged_df[merged_df[groupby] == group]
413
+ axes[i].scatter(group_df["firing_rate"], group_df[attribute], s=dot_size)
414
+ axes[i].set_xlabel("Firing Rate (Hz)")
415
+ axes[i].set_ylabel(attribute)
416
+ axes[i].set_title(f"{groupby}: {group}")
417
+
418
+ # Hide unused subplots
419
+ for j in range(i + 1, len(axes)):
420
+ axes[j].set_visible(False)
421
+
422
+ plt.tight_layout()
423
+ plt.show()
bmtool/synapses.py CHANGED
@@ -18,15 +18,18 @@ from scipy.optimize import curve_fit, minimize, minimize_scalar
18
18
  from scipy.signal import find_peaks
19
19
  from tqdm.notebook import tqdm
20
20
 
21
+ from bmtool.util.util import load_templates_from_config
22
+
21
23
 
22
24
  class SynapseTuner:
23
25
  def __init__(
24
26
  self,
25
- mechanisms_dir: str,
26
- templates_dir: str,
27
- conn_type_settings: dict,
28
- connection: str,
29
- general_settings: dict,
27
+ mechanisms_dir: str = None,
28
+ templates_dir: str = None,
29
+ config: str = None,
30
+ conn_type_settings: dict = None,
31
+ connection: str = None,
32
+ general_settings: dict = None,
30
33
  json_folder_path: str = None,
31
34
  current_name: str = "i",
32
35
  other_vars_to_record: list = None,
@@ -57,8 +60,18 @@ class SynapseTuner:
57
60
  List of synaptic variables you would like sliders set up for the STP sliders method by default will use all parameters in spec_syn_param.
58
61
 
59
62
  """
60
- neuron.load_mechanisms(mechanisms_dir)
61
- h.load_file(templates_dir)
63
+ if config is None and (mechanisms_dir is None or templates_dir is None):
64
+ raise ValueError(
65
+ "Either a config file or both mechanisms_dir and templates_dir must be provided."
66
+ )
67
+
68
+ if config is None:
69
+ neuron.load_mechanisms(mechanisms_dir)
70
+ h.load_file(templates_dir)
71
+ else:
72
+ # loads both mech and templates
73
+ load_templates_from_config(config)
74
+
62
75
  self.conn_type_settings = conn_type_settings
63
76
  if json_folder_path:
64
77
  print(f"updating settings from json path {json_folder_path}")
@@ -939,10 +952,11 @@ class SynapseTuner:
939
952
  class GapJunctionTuner:
940
953
  def __init__(
941
954
  self,
942
- mechanisms_dir: str,
943
- templates_dir: str,
944
- general_settings: dict,
945
- conn_type_settings: dict,
955
+ mechanisms_dir: str = None,
956
+ templates_dir: str = None,
957
+ config: str = None,
958
+ general_settings: dict = None,
959
+ conn_type_settings: dict = None,
946
960
  ):
947
961
  """
948
962
  Initialize the GapJunctionTuner class.
@@ -953,13 +967,24 @@ class GapJunctionTuner:
953
967
  Directory path containing the compiled mod files needed for NEURON mechanisms.
954
968
  templates_dir : str
955
969
  Directory path containing cell template files (.hoc or .py) loaded into NEURON.
970
+ config : str
971
+ Path to a BMTK config.json file. Can be used to load mechanisms, templates, and other settings.
956
972
  general_settings : dict
957
973
  General settings dictionary including parameters like simulation time step, duration, and temperature.
958
974
  conn_type_settings : dict
959
975
  A dictionary containing connection-specific settings for gap junctions.
960
976
  """
961
- neuron.load_mechanisms(mechanisms_dir)
962
- h.load_file(templates_dir)
977
+ if config is None and (mechanisms_dir is None or templates_dir is None):
978
+ raise ValueError(
979
+ "Either a config file or both mechanisms_dir and templates_dir must be provided."
980
+ )
981
+
982
+ if config is None:
983
+ neuron.load_mechanisms(mechanisms_dir)
984
+ h.load_file(templates_dir)
985
+ else:
986
+ # this will load both mechs and templates
987
+ load_templates_from_config(config)
963
988
 
964
989
  self.general_settings = general_settings
965
990
  self.conn_type_settings = conn_type_settings
@@ -1049,7 +1074,6 @@ class GapJunctionTuner:
1049
1074
  plt.xlabel("Time (ms)")
1050
1075
  plt.ylabel("Membrane Voltage (mV)")
1051
1076
  plt.legend()
1052
- plt.show()
1053
1077
 
1054
1078
  def coupling_coefficient(self, t, v1, v2, t_start, t_end, dt=h.dt):
1055
1079
  """
@@ -1085,28 +1109,55 @@ class GapJunctionTuner:
1085
1109
 
1086
1110
  def InteractiveTuner(self):
1087
1111
  w_run = widgets.Button(description="Run", icon="history", button_style="primary")
1088
- values = [i * 10**-4 for i in range(1, 101)] # From 1e-4 to 1e-2
1112
+ values = [i * 10**-4 for i in range(1, 1001)] # From 1e-4 to 1e-1
1089
1113
 
1090
1114
  # Create the SelectionSlider widget with appropriate formatting
1091
- resistance = widgets.SelectionSlider(
1092
- options=[("%g" % i, i) for i in values], # Use scientific notation for display
1093
- value=10**-3, # Default value
1115
+ resistance = widgets.FloatLogSlider(
1116
+ value=0.001,
1117
+ base=10,
1118
+ min=-4, # max exponent of base
1119
+ max=-1, # min exponent of base
1120
+ step=0.1, # exponent step
1094
1121
  description="Resistance: ",
1095
1122
  continuous_update=True,
1096
1123
  )
1097
1124
 
1098
1125
  ui = VBox([w_run, resistance])
1126
+
1127
+ # Create an output widget to control what gets cleared
1128
+ output = widgets.Output()
1129
+
1099
1130
  display(ui)
1131
+ display(output)
1100
1132
 
1101
1133
  def on_button(*args):
1102
- clear_output()
1103
- display(ui)
1104
- resistance_for_gap = resistance.value
1105
- self.model(resistance_for_gap)
1106
- self.plot_model()
1107
- cc = self.coupling_coefficient(self.t_vec, self.soma_v_1, self.soma_v_2, 500, 1000)
1108
- print(f"coupling_coefficient is {cc:0.4f}")
1134
+ with output:
1135
+ # Clear only the output widget, not the entire cell
1136
+ output.clear_output(wait=True)
1137
+
1138
+ resistance_for_gap = resistance.value
1139
+ print(f"Running simulation with resistance: {resistance_for_gap}")
1140
+
1141
+ try:
1142
+ self.model(resistance_for_gap)
1143
+ self.plot_model()
1144
+
1145
+ # Convert NEURON vectors to numpy arrays
1146
+ t_array = np.array(self.t_vec)
1147
+ v1_array = np.array(self.soma_v_1)
1148
+ v2_array = np.array(self.soma_v_2)
1149
+
1150
+ cc = self.coupling_coefficient(t_array, v1_array, v2_array, 500, 1000)
1151
+ print(f"coupling_coefficient is {cc:0.4f}")
1152
+ plt.show()
1153
+
1154
+ except Exception as e:
1155
+ print(f"Error during simulation or analysis: {e}")
1156
+ import traceback
1157
+
1158
+ traceback.print_exc()
1109
1159
 
1160
+ # Run once initially
1110
1161
  on_button()
1111
1162
  w_run.on_click(on_button)
1112
1163
 
bmtool/util/util.py CHANGED
@@ -447,6 +447,9 @@ def load_mechanisms_from_config(config=None):
447
447
 
448
448
 
449
449
  def load_templates_from_config(config=None):
450
+ """
451
+ loads the neuron mechanisms and templates provided from BMTK config
452
+ """
450
453
  if config is None:
451
454
  config = "simulation_config.json"
452
455
  config = load_config(config)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bmtool
3
- Version: 0.7.1.6
3
+ Version: 0.7.2
4
4
  Summary: BMTool
5
5
  Home-page: https://github.com/cyneuro/bmtool
6
6
  Download-URL:
@@ -6,29 +6,29 @@ bmtool/graphs.py,sha256=gBTzI6c2BBK49dWGcfWh9c56TAooyn-KaiEy0Im1HcI,6717
6
6
  bmtool/manage.py,sha256=lsgRejp02P-x6QpA7SXcyXdalPhRmypoviIA2uAitQs,608
7
7
  bmtool/plot_commands.py,sha256=Dxm_RaT4CtHnfsltTtUopJ4KVbfhxtktEB_b7bFEXII,12716
8
8
  bmtool/singlecell.py,sha256=I2yolbAnNC8qpnRkNdnDCLidNW7CktmBuRrcowMZJ3A,45041
9
- bmtool/synapses.py,sha256=wlRY7IixefPzafqG6k2sPIK4s6PLG9Kct-oCaVR29wA,64269
9
+ bmtool/synapses.py,sha256=y8UJAqO1jpZY-mY9gVVMN8Dj1r9jD2fI1nAaNQeQfz4,66148
10
10
  bmtool/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- bmtool/analysis/entrainment.py,sha256=PM4Do8Cl248Y2kIXLRFLPmUB_mH38Yhl8CUDDcunGq0,28241
11
+ bmtool/analysis/entrainment.py,sha256=NQloQtVpEWjDzmkZwMWVcm3hSjErHBZfQl1mrBVoIE8,25321
12
12
  bmtool/analysis/lfp.py,sha256=S2JvxkjcK3-EH93wCrhqNSFY6cX7fOq74pz64ibHKrc,26556
13
13
  bmtool/analysis/netcon_reports.py,sha256=VnPZNKPaQA7oh1q9cIatsqQudm4cOtzNtbGPXoiDCD0,2909
14
- bmtool/analysis/spikes.py,sha256=iJfoVKl2k1X9s6C3PYz-18zlfahuRM_35wN5H9xDCIg,22715
14
+ bmtool/analysis/spikes.py,sha256=3n-xmyEZ7w6CKEND7-aKOAvdDg0lwDuPI5sMdOuPwa0,24637
15
15
  bmtool/bmplot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- bmtool/bmplot/connections.py,sha256=P1JBG4xCbLVq4sfQuUE6c3dO949qajrjdQcrazdmDS4,53861
17
- bmtool/bmplot/entrainment.py,sha256=VSlZvcSeXLr5OxGvmWcGU4s7JS7vOL38lq1XC69O_AE,6926
16
+ bmtool/bmplot/connections.py,sha256=KSORZ43v1B5xfiBN6AnAD7tJySVTkLIY3j_zb2r-YPA,55696
17
+ bmtool/bmplot/entrainment.py,sha256=BrBMerqyiG2YWAO_OEFv7OJf3yeFz3l9jUt4NamluLc,32837
18
18
  bmtool/bmplot/lfp.py,sha256=SNpbWGOUnYEgnkeBw5S--aPN5mIGD22Gw2Pwus0_lvY,2034
19
19
  bmtool/bmplot/netcon_reports.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- bmtool/bmplot/spikes.py,sha256=RJOOtmgWhTvyVi1CghoKTtxvt7MF9cJCrJVm5hV5wA4,11210
20
+ bmtool/bmplot/spikes.py,sha256=odzCSMbFRHp9qthSGQ0WzMWUwNQ7R1Z6gLT6VPF_o5Q,15326
21
21
  bmtool/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  bmtool/debug/commands.py,sha256=VV00f6q5gzZI503vUPeG40ABLLen0bw_k4-EX-H5WZE,580
23
23
  bmtool/debug/debug.py,sha256=9yUFvA4_Bl-x9s29quIEG3pY-S8hNJF3RKBfRBHCl28,208
24
24
  bmtool/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  bmtool/util/commands.py,sha256=Nn-R-4e9g8ZhSPZvTkr38xeKRPfEMANB9Lugppj82UI,68564
26
- bmtool/util/util.py,sha256=owce5BEusZO_8T5x05N2_B583G26vWAy7QX29V0Pj0Y,62818
26
+ bmtool/util/util.py,sha256=S8sAXwDiISGAqnSXRIgFqxqCRzL5YcxAqP1UGxGA5Z4,62906
27
27
  bmtool/util/neuron/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  bmtool/util/neuron/celltuner.py,sha256=lokRLUM1rsdSYBYrNbLBBo39j14mm8TBNVNRnSlhHCk,94868
29
- bmtool-0.7.1.6.dist-info/licenses/LICENSE,sha256=qrXg2jj6kz5d0EnN11hllcQt2fcWVNumx0xNbV05nyM,1068
30
- bmtool-0.7.1.6.dist-info/METADATA,sha256=_jtey-F9b0kjpQ2CELf9SxupFMMv1c-RtgmthDweFJw,3577
31
- bmtool-0.7.1.6.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
32
- bmtool-0.7.1.6.dist-info/entry_points.txt,sha256=0-BHZ6nUnh0twWw9SXNTiRmKjDnb1VO2DfG_-oprhAc,45
33
- bmtool-0.7.1.6.dist-info/top_level.txt,sha256=gpd2Sj-L9tWbuJEd5E8C8S8XkNm5yUE76klUYcM-eWM,7
34
- bmtool-0.7.1.6.dist-info/RECORD,,
29
+ bmtool-0.7.2.dist-info/licenses/LICENSE,sha256=qrXg2jj6kz5d0EnN11hllcQt2fcWVNumx0xNbV05nyM,1068
30
+ bmtool-0.7.2.dist-info/METADATA,sha256=JacxbP2RvvbSuuveedTUJjl8KeqOCKf4FlW-UrRmfCk,3575
31
+ bmtool-0.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
+ bmtool-0.7.2.dist-info/entry_points.txt,sha256=0-BHZ6nUnh0twWw9SXNTiRmKjDnb1VO2DfG_-oprhAc,45
33
+ bmtool-0.7.2.dist-info/top_level.txt,sha256=gpd2Sj-L9tWbuJEd5E8C8S8XkNm5yUE76klUYcM-eWM,7
34
+ bmtool-0.7.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5