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/analysis/entrainment.py +1 -81
- bmtool/analysis/spikes.py +55 -4
- bmtool/bmplot/connections.py +116 -67
- bmtool/bmplot/entrainment.py +693 -30
- bmtool/bmplot/spikes.py +118 -5
- bmtool/synapses.py +76 -25
- bmtool/util/util.py +3 -0
- {bmtool-0.7.1.6.dist-info → bmtool-0.7.2.dist-info}/METADATA +1 -1
- {bmtool-0.7.1.6.dist-info → bmtool-0.7.2.dist-info}/RECORD +13 -13
- {bmtool-0.7.1.6.dist-info → bmtool-0.7.2.dist-info}/WHEEL +1 -1
- {bmtool-0.7.1.6.dist-info → bmtool-0.7.2.dist-info}/entry_points.txt +0 -0
- {bmtool-0.7.1.6.dist-info → bmtool-0.7.2.dist-info}/licenses/LICENSE +0 -0
- {bmtool-0.7.1.6.dist-info → bmtool-0.7.2.dist-info}/top_level.txt +0 -0
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
|
-
|
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
|
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="
|
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
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
61
|
-
|
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
|
-
|
945
|
-
|
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
|
-
|
962
|
-
|
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,
|
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.
|
1092
|
-
|
1093
|
-
|
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
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
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)
|
@@ -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=
|
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=
|
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=
|
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=
|
17
|
-
bmtool/bmplot/entrainment.py,sha256=
|
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=
|
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=
|
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.
|
30
|
-
bmtool-0.7.
|
31
|
-
bmtool-0.7.
|
32
|
-
bmtool-0.7.
|
33
|
-
bmtool-0.7.
|
34
|
-
bmtool-0.7.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|