bmtool 0.7.2.1__py3-none-any.whl → 0.7.4__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/connections.py +33 -13
- bmtool/bmplot/lfp.py +1 -2
- bmtool/synapses.py +149 -71
- bmtool/util/util.py +361 -87
- {bmtool-0.7.2.1.dist-info → bmtool-0.7.4.dist-info}/METADATA +2 -1
- {bmtool-0.7.2.1.dist-info → bmtool-0.7.4.dist-info}/RECORD +10 -10
- {bmtool-0.7.2.1.dist-info → bmtool-0.7.4.dist-info}/WHEEL +0 -0
- {bmtool-0.7.2.1.dist-info → bmtool-0.7.4.dist-info}/entry_points.txt +0 -0
- {bmtool-0.7.2.1.dist-info → bmtool-0.7.4.dist-info}/licenses/LICENSE +0 -0
- {bmtool-0.7.2.1.dist-info → bmtool-0.7.4.dist-info}/top_level.txt +0 -0
bmtool/bmplot/connections.py
CHANGED
@@ -13,6 +13,8 @@ import numpy as np
|
|
13
13
|
import pandas as pd
|
14
14
|
from IPython import get_ipython
|
15
15
|
|
16
|
+
from neuron import h
|
17
|
+
|
16
18
|
from ..util import util
|
17
19
|
|
18
20
|
use_description = """
|
@@ -132,6 +134,7 @@ def total_connection_matrix(
|
|
132
134
|
title = "All Synapse .mod Files Used"
|
133
135
|
if synaptic_info == "3":
|
134
136
|
title = "All Synapse .json Files Used"
|
137
|
+
|
135
138
|
plot_connection_info(
|
136
139
|
text, num, source_labels, target_labels, title, syn_info=synaptic_info, save_file=save_file
|
137
140
|
)
|
@@ -980,9 +983,10 @@ def distance_delay_plot(
|
|
980
983
|
plt.show()
|
981
984
|
|
982
985
|
|
983
|
-
def plot_synapse_location(config: str, source: str, target: str, sids: str, tids: str) -> tuple:
|
986
|
+
def plot_synapse_location(config: str, source: str, target: str, sids: str, tids: str, syn_feature: str = 'afferent_section_id') -> tuple:
|
984
987
|
"""
|
985
988
|
Generates a connectivity matrix showing synaptic distribution across different cell sections.
|
989
|
+
Note does exclude gap junctions since they dont have an afferent id stored in the h5 file!
|
986
990
|
|
987
991
|
Parameters
|
988
992
|
----------
|
@@ -996,6 +1000,8 @@ def plot_synapse_location(config: str, source: str, target: str, sids: str, tids
|
|
996
1000
|
Column name in nodes file containing source population identifiers
|
997
1001
|
tids : str
|
998
1002
|
Column name in nodes file containing target population identifiers
|
1003
|
+
syn_feature : str, default 'afferent_section_id'
|
1004
|
+
Synaptic feature to analyze ('afferent_section_id' or 'afferent_section_pos')
|
999
1005
|
|
1000
1006
|
Returns
|
1001
1007
|
-------
|
@@ -1009,37 +1015,50 @@ def plot_synapse_location(config: str, source: str, target: str, sids: str, tids
|
|
1009
1015
|
RuntimeError
|
1010
1016
|
If template loading or cell instantiation fails
|
1011
1017
|
"""
|
1012
|
-
import matplotlib.pyplot as plt
|
1013
|
-
import numpy as np
|
1014
|
-
from neuron import h
|
1015
|
-
|
1016
1018
|
# Validate inputs
|
1017
1019
|
if not all([config, source, target, sids, tids]):
|
1018
1020
|
raise ValueError(
|
1019
1021
|
"Missing required parameters: config, source, target, sids, and tids must be provided"
|
1020
1022
|
)
|
1021
1023
|
|
1024
|
+
# Fix the validation logic - it was using 'or' instead of 'and'
|
1025
|
+
if syn_feature not in ["afferent_section_id", "afferent_section_pos"]:
|
1026
|
+
raise ValueError("Currently only syn features supported are afferent_section_id or afferent_section_pos")
|
1027
|
+
|
1022
1028
|
try:
|
1023
1029
|
# Load mechanisms and template
|
1024
1030
|
util.load_templates_from_config(config)
|
1025
1031
|
except Exception as e:
|
1026
1032
|
raise RuntimeError(f"Failed to load templates from config: {str(e)}")
|
1027
|
-
|
1033
|
+
|
1028
1034
|
try:
|
1029
1035
|
# Load node and edge data
|
1030
1036
|
nodes, edges = util.load_nodes_edges_from_config(config)
|
1031
1037
|
if source not in nodes or f"{source}_to_{target}" not in edges:
|
1032
1038
|
raise ValueError(f"Source '{source}' or target '{target}' networks not found in data")
|
1033
1039
|
|
1034
|
-
|
1040
|
+
target_nodes = nodes[target]
|
1041
|
+
source_nodes = nodes[source]
|
1035
1042
|
edges = edges[f"{source}_to_{target}"]
|
1043
|
+
|
1044
|
+
# Find edges with NaN values in the specified feature
|
1045
|
+
nan_edges = edges[edges[syn_feature].isna()]
|
1046
|
+
# Print information about removed edges
|
1047
|
+
if not nan_edges.empty:
|
1048
|
+
unique_indices = sorted(list(set(nan_edges.index.tolist())))
|
1049
|
+
print(f"Removing {len(nan_edges)} edges with missing {syn_feature}")
|
1050
|
+
print(f"Unique indices removed: {unique_indices}")
|
1051
|
+
|
1052
|
+
# Filter out edges with NaN values in the specified feature
|
1053
|
+
edges = edges[edges[syn_feature].notna()]
|
1054
|
+
|
1036
1055
|
except Exception as e:
|
1037
1056
|
raise RuntimeError(f"Failed to load nodes and edges: {str(e)}")
|
1038
1057
|
|
1039
1058
|
# Map identifiers while checking for missing values
|
1040
|
-
edges["target_model_template"] = edges["target_node_id"].map(
|
1041
|
-
edges["target_pop_name"] = edges["target_node_id"].map(
|
1042
|
-
edges["source_pop_name"] = edges["source_node_id"].map(
|
1059
|
+
edges["target_model_template"] = edges["target_node_id"].map(target_nodes["model_template"])
|
1060
|
+
edges["target_pop_name"] = edges["target_node_id"].map(target_nodes[tids])
|
1061
|
+
edges["source_pop_name"] = edges["source_node_id"].map(source_nodes[sids])
|
1043
1062
|
|
1044
1063
|
if edges["target_model_template"].isnull().any():
|
1045
1064
|
print("Warning: Some target nodes missing model template")
|
@@ -1101,7 +1120,7 @@ def plot_synapse_location(config: str, source: str, target: str, sids: str, tids
|
|
1101
1120
|
section_mapping = section_mappings[target_model_template]
|
1102
1121
|
|
1103
1122
|
# Calculate section distribution
|
1104
|
-
section_counts = filtered_edges[
|
1123
|
+
section_counts = filtered_edges[syn_feature].value_counts()
|
1105
1124
|
section_percentages = (section_counts / total_connections * 100).round(1)
|
1106
1125
|
|
1107
1126
|
# Format section distribution text - show all sections
|
@@ -1110,16 +1129,17 @@ def plot_synapse_location(config: str, source: str, target: str, sids: str, tids
|
|
1110
1129
|
section_name = section_mapping.get(section_id, f"sec_{section_id}")
|
1111
1130
|
section_display.append(f"{section_name}:{percentage}%")
|
1112
1131
|
|
1132
|
+
|
1113
1133
|
num_connections[source_idx, target_idx] = total_connections
|
1114
1134
|
text_data[source_idx, target_idx] = "\n".join(section_display)
|
1115
1135
|
|
1116
1136
|
except Exception as e:
|
1117
1137
|
print(f"Warning: Error processing {target_model_template}: {str(e)}")
|
1118
1138
|
num_connections[source_idx, target_idx] = total_connections
|
1119
|
-
text_data[source_idx, target_idx] = "
|
1139
|
+
text_data[source_idx, target_idx] = "Feature info N/A"
|
1120
1140
|
|
1121
1141
|
# Create the plot
|
1122
|
-
title = f"Synaptic Distribution by
|
1142
|
+
title = f"Synaptic Distribution by {syn_feature.replace('_', ' ').title()}: {source} to {target}"
|
1123
1143
|
fig, ax = plot_connection_info(
|
1124
1144
|
text=text_data,
|
1125
1145
|
num=num_connections,
|
bmtool/bmplot/lfp.py
CHANGED
bmtool/synapses.py
CHANGED
@@ -20,68 +20,104 @@ from tqdm.notebook import tqdm
|
|
20
20
|
|
21
21
|
from bmtool.util.util import load_templates_from_config
|
22
22
|
|
23
|
+
DEFAULT_GENERAL_SETTINGS = {
|
24
|
+
"vclamp": True,
|
25
|
+
"rise_interval": (0.1, 0.9),
|
26
|
+
"tstart": 500.0,
|
27
|
+
"tdur": 100.0,
|
28
|
+
"threshold": -15.0,
|
29
|
+
"delay": 1.3,
|
30
|
+
"weight": 1.0,
|
31
|
+
"dt": 0.025,
|
32
|
+
"celsius": 20,
|
33
|
+
}
|
34
|
+
|
35
|
+
DEFAULT_GAP_JUNCTION_GENERAL_SETTINGS = {
|
36
|
+
"tstart": 500.0,
|
37
|
+
"tdur": 500.0,
|
38
|
+
"dt": 0.025,
|
39
|
+
"celsius": 20,
|
40
|
+
}
|
41
|
+
|
23
42
|
|
24
43
|
class SynapseTuner:
|
25
44
|
def __init__(
|
26
45
|
self,
|
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,
|
33
|
-
json_folder_path: str = None,
|
46
|
+
mechanisms_dir: Optional[str] = None,
|
47
|
+
templates_dir: Optional[str] = None,
|
48
|
+
config: Optional[str] = None,
|
49
|
+
conn_type_settings: Optional[dict] = None,
|
50
|
+
connection: Optional[str] = None,
|
51
|
+
general_settings: Optional[dict] = None,
|
52
|
+
json_folder_path: Optional[str] = None,
|
34
53
|
current_name: str = "i",
|
35
|
-
other_vars_to_record: list = None,
|
36
|
-
slider_vars: list = None,
|
54
|
+
other_vars_to_record: Optional[list] = None,
|
55
|
+
slider_vars: Optional[list] = None,
|
56
|
+
hoc_cell: Optional[object] = None,
|
37
57
|
) -> None:
|
38
58
|
"""
|
39
|
-
Initialize the
|
59
|
+
Initialize the SynapseTuner class with connection type settings, mechanisms, and template directories.
|
40
60
|
|
41
61
|
Parameters:
|
42
62
|
-----------
|
43
|
-
mechanisms_dir : str
|
63
|
+
mechanisms_dir : Optional[str]
|
44
64
|
Directory path containing the compiled mod files needed for NEURON mechanisms.
|
45
|
-
templates_dir : str
|
65
|
+
templates_dir : Optional[str]
|
46
66
|
Directory path containing cell template files (.hoc or .py) loaded into NEURON.
|
47
|
-
conn_type_settings : dict
|
67
|
+
conn_type_settings : Optional[dict]
|
48
68
|
A dictionary containing connection-specific settings, such as synaptic properties and details.
|
49
|
-
connection : str
|
69
|
+
connection : Optional[str]
|
50
70
|
Name of the connection type to be used from the conn_type_settings dictionary.
|
51
|
-
general_settings : dict
|
71
|
+
general_settings : Optional[dict]
|
52
72
|
General settings dictionary including parameters like simulation time step, duration, and temperature.
|
53
|
-
json_folder_path : str
|
73
|
+
json_folder_path : Optional[str]
|
54
74
|
Path to folder containing JSON files with additional synaptic properties to update settings.
|
55
75
|
current_name : str, optional
|
56
76
|
Name of the synaptic current variable to be recorded (default is 'i').
|
57
|
-
other_vars_to_record : list
|
77
|
+
other_vars_to_record : Optional[list]
|
58
78
|
List of additional synaptic variables to record during the simulation (e.g., 'Pr', 'Use').
|
59
|
-
slider_vars : list
|
79
|
+
slider_vars : Optional[list]
|
60
80
|
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.
|
61
|
-
|
81
|
+
hoc_cell : Optional[object]
|
82
|
+
An already loaded NEURON cell object. If provided, template loading and cell setup will be skipped.
|
62
83
|
"""
|
63
|
-
|
64
|
-
raise ValueError(
|
65
|
-
"Either a config file or both mechanisms_dir and templates_dir must be provided."
|
66
|
-
)
|
84
|
+
self.hoc_cell = hoc_cell
|
67
85
|
|
68
|
-
if
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
load_templates_from_config(config)
|
86
|
+
if hoc_cell is None:
|
87
|
+
if config is None and (mechanisms_dir is None or templates_dir is None):
|
88
|
+
raise ValueError(
|
89
|
+
"Either a config file, both mechanisms_dir and templates_dir, or a hoc_cell must be provided."
|
90
|
+
)
|
74
91
|
|
75
|
-
|
92
|
+
if config is None:
|
93
|
+
neuron.load_mechanisms(mechanisms_dir)
|
94
|
+
h.load_file(templates_dir)
|
95
|
+
else:
|
96
|
+
# loads both mech and templates
|
97
|
+
load_templates_from_config(config)
|
98
|
+
|
99
|
+
if conn_type_settings is None:
|
100
|
+
raise ValueError("conn_type_settings must be provided.")
|
101
|
+
if connection is None:
|
102
|
+
raise ValueError("connection must be provided.")
|
103
|
+
if connection not in conn_type_settings:
|
104
|
+
raise ValueError(f"connection '{connection}' not found in conn_type_settings.")
|
105
|
+
|
106
|
+
self.conn_type_settings: dict = conn_type_settings
|
76
107
|
if json_folder_path:
|
77
108
|
print(f"updating settings from json path {json_folder_path}")
|
78
109
|
self._update_spec_syn_param(json_folder_path)
|
79
|
-
|
110
|
+
# Use default general settings if not provided
|
111
|
+
if general_settings is None:
|
112
|
+
self.general_settings: dict = DEFAULT_GENERAL_SETTINGS.copy()
|
113
|
+
else:
|
114
|
+
# Merge defaults with user-provided
|
115
|
+
self.general_settings = {**DEFAULT_GENERAL_SETTINGS, **general_settings}
|
80
116
|
self.conn = self.conn_type_settings[connection]
|
81
117
|
self.synaptic_props = self.conn["spec_syn_param"]
|
82
|
-
self.vclamp = general_settings["vclamp"]
|
118
|
+
self.vclamp = self.general_settings["vclamp"]
|
83
119
|
self.current_name = current_name
|
84
|
-
self.other_vars_to_record = other_vars_to_record
|
120
|
+
self.other_vars_to_record = other_vars_to_record or []
|
85
121
|
self.ispk = None
|
86
122
|
|
87
123
|
if slider_vars:
|
@@ -94,25 +130,26 @@ class SynapseTuner:
|
|
94
130
|
# If the key is missing from synaptic_props, get the value using getattr
|
95
131
|
if key not in self.synaptic_props:
|
96
132
|
try:
|
97
|
-
# Get the alternative value from getattr dynamically
|
98
133
|
self._set_up_cell()
|
99
134
|
self._set_up_synapse()
|
100
135
|
value = getattr(self.syn, key)
|
101
|
-
# print(value)
|
102
136
|
self.slider_vars[key] = value
|
103
137
|
except AttributeError as e:
|
104
138
|
print(f"Error accessing '{key}' in syn {self.syn}: {e}")
|
105
|
-
|
106
139
|
else:
|
107
140
|
self.slider_vars = self.synaptic_props
|
108
141
|
|
109
|
-
h.tstop = general_settings["tstart"] + general_settings["tdur"]
|
110
|
-
h.dt = general_settings["dt"] # Time step (resolution) of the simulation in ms
|
142
|
+
h.tstop = self.general_settings["tstart"] + self.general_settings["tdur"]
|
143
|
+
h.dt = self.general_settings["dt"] # Time step (resolution) of the simulation in ms
|
111
144
|
h.steps_per_ms = 1 / h.dt
|
112
|
-
h.celsius = general_settings["celsius"]
|
145
|
+
h.celsius = self.general_settings["celsius"]
|
113
146
|
|
114
147
|
# get some stuff set up we need for both SingleEvent and Interactive Tuner
|
115
|
-
|
148
|
+
# Only set up cell if hoc_cell was not provided
|
149
|
+
if self.hoc_cell is None:
|
150
|
+
self._set_up_cell()
|
151
|
+
else:
|
152
|
+
self.cell = self.hoc_cell
|
116
153
|
self._set_up_synapse()
|
117
154
|
|
118
155
|
self.nstim = h.NetStim()
|
@@ -137,7 +174,7 @@ class SynapseTuner:
|
|
137
174
|
|
138
175
|
self._set_up_recorders()
|
139
176
|
|
140
|
-
def _update_spec_syn_param(self, json_folder_path):
|
177
|
+
def _update_spec_syn_param(self, json_folder_path: str) -> None:
|
141
178
|
"""
|
142
179
|
Update specific synaptic parameters using JSON files located in the specified folder.
|
143
180
|
|
@@ -146,6 +183,8 @@ class SynapseTuner:
|
|
146
183
|
json_folder_path : str
|
147
184
|
Path to folder containing JSON files, where each JSON file corresponds to a connection type.
|
148
185
|
"""
|
186
|
+
if not self.conn_type_settings:
|
187
|
+
return
|
149
188
|
for conn_type, settings in self.conn_type_settings.items():
|
150
189
|
json_file_path = os.path.join(json_folder_path, f"{conn_type}.json")
|
151
190
|
if os.path.exists(json_file_path):
|
@@ -155,13 +194,17 @@ class SynapseTuner:
|
|
155
194
|
else:
|
156
195
|
print(f"JSON file for {conn_type} not found.")
|
157
196
|
|
158
|
-
def _set_up_cell(self):
|
197
|
+
def _set_up_cell(self) -> None:
|
159
198
|
"""
|
160
199
|
Set up the neuron cell based on the specified connection settings.
|
200
|
+
This method is only called when hoc_cell is not provided.
|
161
201
|
"""
|
162
|
-
self.
|
202
|
+
if self.hoc_cell is None:
|
203
|
+
self.cell = getattr(h, self.conn["spec_settings"]["post_cell"])()
|
204
|
+
else:
|
205
|
+
self.cell = self.hoc_cell
|
163
206
|
|
164
|
-
def _set_up_synapse(self):
|
207
|
+
def _set_up_synapse(self) -> None:
|
165
208
|
"""
|
166
209
|
Set up the synapse on the target cell according to the synaptic parameters in `conn_type_settings`.
|
167
210
|
|
@@ -176,7 +219,7 @@ class SynapseTuner:
|
|
176
219
|
)
|
177
220
|
)
|
178
221
|
for key, value in self.conn["spec_syn_param"].items():
|
179
|
-
if isinstance(value, (int, float)):
|
222
|
+
if isinstance(value, (int, float)):
|
180
223
|
if hasattr(self.syn, key):
|
181
224
|
setattr(self.syn, key, value)
|
182
225
|
else:
|
@@ -184,7 +227,7 @@ class SynapseTuner:
|
|
184
227
|
f"Warning: {key} cannot be assigned as it does not exist in the synapse. Check your mod file or spec_syn_param."
|
185
228
|
)
|
186
229
|
|
187
|
-
def _set_up_recorders(self):
|
230
|
+
def _set_up_recorders(self) -> None:
|
188
231
|
"""
|
189
232
|
Set up recording vectors to capture simulation data.
|
190
233
|
|
@@ -952,11 +995,12 @@ class SynapseTuner:
|
|
952
995
|
class GapJunctionTuner:
|
953
996
|
def __init__(
|
954
997
|
self,
|
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,
|
998
|
+
mechanisms_dir: Optional[str] = None,
|
999
|
+
templates_dir: Optional[str] = None,
|
1000
|
+
config: Optional[str] = None,
|
1001
|
+
general_settings: Optional[dict] = None,
|
1002
|
+
conn_type_settings: Optional[dict] = None,
|
1003
|
+
hoc_cell: Optional[object] = None,
|
960
1004
|
):
|
961
1005
|
"""
|
962
1006
|
Initialize the GapJunctionTuner class.
|
@@ -973,34 +1017,49 @@ class GapJunctionTuner:
|
|
973
1017
|
General settings dictionary including parameters like simulation time step, duration, and temperature.
|
974
1018
|
conn_type_settings : dict
|
975
1019
|
A dictionary containing connection-specific settings for gap junctions.
|
1020
|
+
hoc_cell : object, optional
|
1021
|
+
An already loaded NEURON cell object. If provided, template loading and cell creation will be skipped.
|
976
1022
|
"""
|
977
|
-
|
978
|
-
raise ValueError(
|
979
|
-
"Either a config file or both mechanisms_dir and templates_dir must be provided."
|
980
|
-
)
|
1023
|
+
self.hoc_cell = hoc_cell
|
981
1024
|
|
982
|
-
if
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
1025
|
+
if hoc_cell is None:
|
1026
|
+
if config is None and (mechanisms_dir is None or templates_dir is None):
|
1027
|
+
raise ValueError(
|
1028
|
+
"Either a config file, both mechanisms_dir and templates_dir, or a hoc_cell must be provided."
|
1029
|
+
)
|
1030
|
+
|
1031
|
+
if config is None:
|
1032
|
+
neuron.load_mechanisms(mechanisms_dir)
|
1033
|
+
h.load_file(templates_dir)
|
1034
|
+
else:
|
1035
|
+
# this will load both mechs and templates
|
1036
|
+
load_templates_from_config(config)
|
988
1037
|
|
989
|
-
|
1038
|
+
# Use default general settings if not provided, merge with user-provided
|
1039
|
+
if general_settings is None:
|
1040
|
+
self.general_settings: dict = DEFAULT_GAP_JUNCTION_GENERAL_SETTINGS.copy()
|
1041
|
+
else:
|
1042
|
+
self.general_settings = {**DEFAULT_GAP_JUNCTION_GENERAL_SETTINGS, **general_settings}
|
990
1043
|
self.conn_type_settings = conn_type_settings
|
991
1044
|
|
992
|
-
h.tstop = general_settings["tstart"] + general_settings["tdur"] + 100.0
|
993
|
-
h.dt = general_settings["dt"] # Time step (resolution) of the simulation in ms
|
1045
|
+
h.tstop = self.general_settings["tstart"] + self.general_settings["tdur"] + 100.0
|
1046
|
+
h.dt = self.general_settings["dt"] # Time step (resolution) of the simulation in ms
|
994
1047
|
h.steps_per_ms = 1 / h.dt
|
995
|
-
h.celsius = general_settings["celsius"]
|
996
|
-
|
997
|
-
self.cell_name = conn_type_settings["cell"]
|
1048
|
+
h.celsius = self.general_settings["celsius"]
|
998
1049
|
|
999
1050
|
# set up gap junctions
|
1000
1051
|
pc = h.ParallelContext()
|
1001
1052
|
|
1002
|
-
|
1003
|
-
self.
|
1053
|
+
# Use provided hoc_cell or create new cells
|
1054
|
+
if self.hoc_cell is not None:
|
1055
|
+
self.cell1 = self.hoc_cell
|
1056
|
+
# For gap junctions, we need two cells, so create a second one if using hoc_cell
|
1057
|
+
self.cell_name = conn_type_settings["cell"]
|
1058
|
+
self.cell2 = getattr(h, self.cell_name)()
|
1059
|
+
else:
|
1060
|
+
self.cell_name = conn_type_settings["cell"]
|
1061
|
+
self.cell1 = getattr(h, self.cell_name)()
|
1062
|
+
self.cell2 = getattr(h, self.cell_name)()
|
1004
1063
|
|
1005
1064
|
self.icl = h.IClamp(self.cell1.soma[0](0.5))
|
1006
1065
|
self.icl.delay = self.general_settings["tstart"]
|
@@ -1247,6 +1306,10 @@ class SynapseOptimizer:
|
|
1247
1306
|
- max_amplitude: maximum synaptic response amplitude
|
1248
1307
|
- rise_time: time for synaptic response to rise from 20% to 80% of peak
|
1249
1308
|
- decay_time: time constant of synaptic response decay
|
1309
|
+
- latency: synaptic response latency
|
1310
|
+
- half_width: synaptic response half-width
|
1311
|
+
- baseline: baseline current
|
1312
|
+
- amp: peak amplitude from syn_props
|
1250
1313
|
"""
|
1251
1314
|
# Set these to 0 for when we return the dict
|
1252
1315
|
induction = 0
|
@@ -1255,11 +1318,22 @@ class SynapseOptimizer:
|
|
1255
1318
|
amp = 0
|
1256
1319
|
rise_time = 0
|
1257
1320
|
decay_time = 0
|
1321
|
+
latency = 0
|
1322
|
+
half_width = 0
|
1323
|
+
baseline = 0
|
1324
|
+
syn_amp = 0
|
1258
1325
|
|
1259
1326
|
if self.run_single_event:
|
1260
1327
|
self.tuner.SingleEvent(plot_and_print=False)
|
1261
|
-
|
1262
|
-
|
1328
|
+
# Use the attributes set by SingleEvent method
|
1329
|
+
rise_time = getattr(self.tuner, "rise_time", 0)
|
1330
|
+
decay_time = getattr(self.tuner, "decay_time", 0)
|
1331
|
+
# Get additional syn_props directly
|
1332
|
+
syn_props = self.tuner._get_syn_prop()
|
1333
|
+
latency = syn_props.get("latency", 0)
|
1334
|
+
half_width = syn_props.get("half_width", 0)
|
1335
|
+
baseline = syn_props.get("baseline", 0)
|
1336
|
+
syn_amp = syn_props.get("amp", 0)
|
1263
1337
|
|
1264
1338
|
if self.run_train_input:
|
1265
1339
|
self.tuner._simulate_model(self.train_frequency, self.train_delay)
|
@@ -1276,6 +1350,10 @@ class SynapseOptimizer:
|
|
1276
1350
|
"max_amplitude": float(amp),
|
1277
1351
|
"rise_time": float(rise_time),
|
1278
1352
|
"decay_time": float(decay_time),
|
1353
|
+
"latency": float(latency),
|
1354
|
+
"half_width": float(half_width),
|
1355
|
+
"baseline": float(baseline),
|
1356
|
+
"amp": float(syn_amp),
|
1279
1357
|
}
|
1280
1358
|
|
1281
1359
|
def _default_cost_function(
|
bmtool/util/util.py
CHANGED
@@ -16,6 +16,9 @@ import numpy as np
|
|
16
16
|
import pandas as pd
|
17
17
|
from neuron import h
|
18
18
|
|
19
|
+
from typing import Dict, List, Optional, Union, Any
|
20
|
+
from pathlib import Path
|
21
|
+
|
19
22
|
# from bmtk.utils.io.cell_vars import CellVarsFile
|
20
23
|
# from bmtk.analyzer.cell_vars import _get_cell_report
|
21
24
|
# from bmtk.analyzer.io_tools import load_config
|
@@ -345,95 +348,366 @@ def load_edges(edges_file, edge_types_file):
|
|
345
348
|
return edges # return (population, edges_df)
|
346
349
|
|
347
350
|
|
348
|
-
def load_edges_from_paths(
|
351
|
+
def load_edges_from_paths(
|
352
|
+
file_paths: str,
|
353
|
+
verbose: bool = False
|
354
|
+
) -> Dict[str, pd.DataFrame]:
|
349
355
|
"""
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
356
|
+
Load multiple SONATA edge files into a dictionary of DataFrames.
|
357
|
+
|
358
|
+
This function reads a configuration file containing multiple edge file pairs
|
359
|
+
(CSV + H5) and loads each pair into a separate DataFrame. Each DataFrame
|
360
|
+
contains merged edge connectivity and metadata information.
|
361
|
+
|
362
|
+
Parameters:
|
363
|
+
-----------
|
364
|
+
file_paths str
|
365
|
+
Expected structure: [{'edge_types_file': ..., 'edges_file': ...}, ...]
|
366
|
+
verbose : bool, default False
|
367
|
+
If True, print detailed information about the loading process
|
368
|
+
|
369
|
+
Returns:
|
370
|
+
--------
|
371
|
+
Dict[str, pd.DataFrame]
|
372
|
+
Dictionary mapping edge dataset names to DataFrames.
|
373
|
+
Keys are derived from CSV filenames (without '_edge_types.csv' suffix).
|
374
|
+
Values are DataFrames containing merged edge data.
|
375
|
+
|
376
|
+
Notes:
|
377
|
+
------
|
378
|
+
- If loading fails for any dataset, an empty DataFrame is stored for that key
|
379
|
+
- Dataset names are extracted from CSV filenames for readability
|
380
|
+
- All edge data follows SONATA format specifications
|
360
381
|
"""
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
with h5py.File(
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
382
|
+
|
383
|
+
# Load configuration and extract edge file information
|
384
|
+
|
385
|
+
edge_files_list = file_paths
|
386
|
+
|
387
|
+
if verbose:
|
388
|
+
print(f"Loading {len(edge_files_list)} edge datasets from config: {config}")
|
389
|
+
|
390
|
+
edges_dict: Dict[str, pd.DataFrame] = {}
|
391
|
+
|
392
|
+
# Process each edge file pair
|
393
|
+
for i, file_info in enumerate(edge_files_list):
|
394
|
+
csv_path = file_info['edge_types_file']
|
395
|
+
h5_path = file_info['edges_file']
|
396
|
+
|
397
|
+
# Generate meaningful key from h5 file
|
398
|
+
with h5py.File(h5_path, "r") as connections_h5:
|
399
|
+
key = list(connections_h5["/edges"])[0]
|
400
|
+
|
401
|
+
if verbose:
|
402
|
+
print(f"\n{'='*60}")
|
403
|
+
print(f"Loading edge set {i+1}/{len(edge_files_list)}: {key}")
|
404
|
+
print(f"{'='*60}")
|
405
|
+
|
406
|
+
try:
|
407
|
+
# Load individual edge dataset
|
408
|
+
df = load_sonata_edges_to_dataframe(csv_path, h5_path, verbose=verbose)
|
409
|
+
edges_dict[key] = df
|
410
|
+
|
411
|
+
if verbose:
|
412
|
+
print(f"\nSuccessfully loaded {key}: {df.shape[0]} edges, {df.shape[1]} columns")
|
413
|
+
|
414
|
+
# Show important columns for verification
|
415
|
+
important_cols = ['edge_type_id', 'source_node_id', 'target_node_id',
|
416
|
+
'model_template', 'afferent_section_id']
|
417
|
+
available_important = [col for col in important_cols if col in df.columns]
|
418
|
+
print(f"Key columns available: {available_important}")
|
419
|
+
|
420
|
+
# Show model template information if available
|
421
|
+
if 'model_template' in df.columns:
|
422
|
+
unique_templates = df['model_template'].dropna().unique()
|
423
|
+
print(f"Model templates: {unique_templates}")
|
424
|
+
|
425
|
+
except Exception as e:
|
426
|
+
if verbose:
|
427
|
+
print(f"Error loading {key}: {str(e)}")
|
428
|
+
edges_dict[key] = pd.DataFrame() # Store empty DataFrame on error
|
429
|
+
|
430
|
+
if verbose:
|
431
|
+
print(f"\n{'='*60}")
|
432
|
+
print("LOADING SUMMARY")
|
433
|
+
print(f"{'='*60}")
|
434
|
+
for key, df in edges_dict.items():
|
435
|
+
if not df.empty:
|
436
|
+
print(f"{key}: {df.shape[0]} edges, {df.shape[1]} columns")
|
437
|
+
if 'model_template' in df.columns:
|
438
|
+
templates = df['model_template'].dropna().unique()
|
439
|
+
print(f" Model templates: {list(templates)}")
|
440
|
+
if 'afferent_section_id' in df.columns:
|
441
|
+
print(f" afferent_section_id available")
|
442
|
+
else:
|
443
|
+
print(f"{key}: No data loaded (error occurred)")
|
444
|
+
|
445
|
+
return edges_dict
|
391
446
|
|
392
|
-
c_df.reset_index(inplace=True)
|
393
|
-
c_df.rename(columns={"index": "edge_id"}, inplace=True)
|
394
|
-
c_df.set_index("edge_type_id", inplace=True)
|
395
447
|
|
396
|
-
|
397
|
-
|
398
|
-
|
448
|
+
def load_sonata_edges_to_dataframe(
|
449
|
+
csv_path: Union[str, Path],
|
450
|
+
h5_path: Union[str, Path],
|
451
|
+
verbose: bool = False
|
452
|
+
) -> pd.DataFrame:
|
453
|
+
"""
|
454
|
+
Load SONATA format edge data into a pandas DataFrame.
|
455
|
+
|
456
|
+
This function combines edge connectivity data from HDF5 files with edge type
|
457
|
+
metadata from CSV files according to the SONATA specification. H5 attributes
|
458
|
+
take precedence over CSV attributes when both exist for the same column.
|
459
|
+
|
460
|
+
Parameters:
|
461
|
+
-----------
|
462
|
+
csv_path : Union[str, Path]
|
463
|
+
Path to the edge types CSV file (space-delimited)
|
464
|
+
h5_path : Union[str, Path]
|
465
|
+
Path to the edges HDF5 file containing connectivity data
|
466
|
+
verbose : bool, default False
|
467
|
+
If True, print detailed information about the loading process
|
468
|
+
|
469
|
+
Returns:
|
470
|
+
--------
|
471
|
+
pd.DataFrame
|
472
|
+
Combined edge data with columns from both H5 and CSV sources.
|
473
|
+
Columns include:
|
474
|
+
- edge_id: Sequential edge identifier
|
475
|
+
- population: Edge population name
|
476
|
+
- edge_type_id: Edge type identifier for merging with CSV
|
477
|
+
- source_node_id, target_node_id: Connected node IDs
|
478
|
+
- source_population, target_population: Node population names
|
479
|
+
- edge_group_id, edge_group_index: Edge grouping information
|
480
|
+
- Additional attributes from H5 edge groups
|
481
|
+
- Edge type metadata from CSV (model_template, afferent_section_id, etc.)
|
482
|
+
|
483
|
+
Notes:
|
484
|
+
------
|
485
|
+
- CSV file must be space-delimited and contain 'edge_type_id' column
|
486
|
+
- H5 file must follow SONATA edge format with required datasets
|
487
|
+
- When both H5 and CSV contain the same attribute, H5 version is kept
|
488
|
+
- Dynamics parameters are flattened with 'dynamics_params/' prefix
|
489
|
+
"""
|
490
|
+
|
491
|
+
# Load edge types CSV (space-delimited)
|
492
|
+
edge_types_df = pd.read_csv(csv_path, sep=' ')
|
493
|
+
|
494
|
+
if verbose:
|
495
|
+
print(f"Loaded edge types from: {csv_path}")
|
496
|
+
print(f" Columns: {edge_types_df.columns.tolist()}")
|
497
|
+
print(f" Shape: {edge_types_df.shape}")
|
498
|
+
print(f" Unique edge_type_ids: {sorted(edge_types_df['edge_type_id'].unique())}")
|
499
|
+
|
500
|
+
# Open HDF5 file and process edge data
|
501
|
+
with h5py.File(h5_path, 'r') as h5f:
|
502
|
+
|
503
|
+
# Navigate to edges group
|
504
|
+
edges_group = h5f['edges']
|
505
|
+
populations = list(edges_group.keys())
|
506
|
+
|
507
|
+
if verbose:
|
508
|
+
print(f"\nProcessing H5 file: {h5_path}")
|
509
|
+
print(f" Edge populations: {populations}")
|
510
|
+
|
511
|
+
# Process each population (typically one per file)
|
512
|
+
all_edges_data: List[pd.DataFrame] = []
|
513
|
+
|
514
|
+
for pop_name in populations:
|
515
|
+
pop_group = edges_group[pop_name]
|
516
|
+
|
517
|
+
if verbose:
|
518
|
+
print(f"\n Processing population: {pop_name}")
|
519
|
+
|
520
|
+
# Read required SONATA edge datasets
|
521
|
+
edge_type_ids = pop_group['edge_type_id'][:]
|
522
|
+
source_node_ids = pop_group['source_node_id'][:]
|
523
|
+
target_node_ids = pop_group['target_node_id'][:]
|
524
|
+
edge_group_ids = pop_group['edge_group_id'][:]
|
525
|
+
edge_group_indices = pop_group['edge_group_index'][:]
|
526
|
+
|
527
|
+
if verbose:
|
528
|
+
print(f" Unique edge_type_ids in H5: {sorted(np.unique(edge_type_ids))}")
|
529
|
+
print(f" Number of edges: {len(edge_type_ids)}")
|
530
|
+
|
531
|
+
# Extract node population attributes with proper string handling
|
532
|
+
source_pop = pop_group['source_node_id'].attrs.get('node_population', '')
|
533
|
+
target_pop = pop_group['target_node_id'].attrs.get('node_population', '')
|
534
|
+
|
535
|
+
# Handle both string and bytes cases for population names
|
536
|
+
if isinstance(source_pop, bytes):
|
537
|
+
source_pop = source_pop.decode()
|
538
|
+
if isinstance(target_pop, bytes):
|
539
|
+
target_pop = target_pop.decode()
|
540
|
+
|
541
|
+
n_edges = len(edge_type_ids)
|
542
|
+
|
543
|
+
# Create base DataFrame with core edge attributes
|
544
|
+
edges_df = pd.DataFrame({
|
545
|
+
'edge_id': np.arange(n_edges), # Sequential edge identifiers
|
546
|
+
'population': pop_name,
|
547
|
+
'edge_type_id': edge_type_ids,
|
548
|
+
'source_node_id': source_node_ids,
|
549
|
+
'target_node_id': target_node_ids,
|
550
|
+
'source_population': source_pop,
|
551
|
+
'target_population': target_pop,
|
552
|
+
'edge_group_id': edge_group_ids,
|
553
|
+
'edge_group_index': edge_group_indices
|
554
|
+
})
|
555
|
+
|
556
|
+
# Process edge groups to extract additional attributes
|
557
|
+
edge_group_names = [
|
558
|
+
key for key in pop_group.keys()
|
559
|
+
if key not in ['edge_type_id', 'source_node_id', 'target_node_id',
|
560
|
+
'edge_group_id', 'edge_group_index', 'indices']
|
561
|
+
]
|
562
|
+
|
563
|
+
# Track which columns come from H5 for precedence handling
|
564
|
+
edge_attributes: Dict[str, np.ndarray] = {}
|
565
|
+
h5_column_names: set = set()
|
566
|
+
|
567
|
+
# Process each edge group to extract group-specific attributes
|
568
|
+
for group_name in edge_group_names:
|
569
|
+
group = pop_group[group_name]
|
570
|
+
group_id = int(group_name)
|
571
|
+
|
572
|
+
# Identify edges belonging to this group
|
573
|
+
group_mask = edge_group_ids == group_id
|
574
|
+
group_indices = edge_group_indices[group_mask]
|
575
|
+
|
576
|
+
# Extract all attributes from this edge group
|
577
|
+
for attr_name in group.keys():
|
578
|
+
if attr_name == 'dynamics_params': # sonata says this exist but i have yet to see it
|
579
|
+
# Handle nested dynamics parameters
|
580
|
+
dynamics_group = group[attr_name]
|
581
|
+
for param_name in dynamics_group.keys():
|
582
|
+
param_data = dynamics_group[param_name][:]
|
583
|
+
full_attr_name = f"dynamics_params/{param_name}"
|
584
|
+
|
585
|
+
# Initialize attribute array if not exists
|
586
|
+
if full_attr_name not in edge_attributes:
|
587
|
+
edge_attributes[full_attr_name] = np.full(n_edges, np.nan, dtype=object)
|
588
|
+
|
589
|
+
# Assign data to appropriate edges
|
590
|
+
edge_attributes[full_attr_name][group_mask] = param_data[group_indices]
|
591
|
+
h5_column_names.add(full_attr_name)
|
592
|
+
else:
|
593
|
+
# Handle regular attributes with proper string decoding
|
594
|
+
attr_data = group[attr_name][:]
|
595
|
+
|
596
|
+
# Handle string attributes properly
|
597
|
+
if attr_data.dtype.kind in ['S', 'U']: # String or Unicode
|
598
|
+
# Decode bytes to strings if necessary
|
599
|
+
if attr_data.dtype.kind == 'S':
|
600
|
+
attr_data = np.array([
|
601
|
+
s.decode() if isinstance(s, bytes) else s
|
602
|
+
for s in attr_data
|
603
|
+
])
|
604
|
+
|
605
|
+
# Initialize attribute array with appropriate default
|
606
|
+
if attr_name not in edge_attributes:
|
607
|
+
if attr_data.dtype.kind in ['S', 'U']:
|
608
|
+
edge_attributes[attr_name] = np.full(n_edges, '', dtype=object)
|
609
|
+
else:
|
610
|
+
edge_attributes[attr_name] = np.full(n_edges, np.nan, dtype=object)
|
611
|
+
|
612
|
+
# Assign data to appropriate edges
|
613
|
+
edge_attributes[attr_name][group_mask] = attr_data[group_indices]
|
614
|
+
h5_column_names.add(attr_name)
|
615
|
+
|
616
|
+
# Add all H5 attributes to the DataFrame
|
617
|
+
for attr_name, attr_values in edge_attributes.items():
|
618
|
+
edges_df[attr_name] = attr_values
|
619
|
+
|
620
|
+
all_edges_data.append(edges_df)
|
621
|
+
|
622
|
+
# Combine all populations into single DataFrame
|
623
|
+
if all_edges_data:
|
624
|
+
combined_edges_df = pd.concat(all_edges_data, ignore_index=True)
|
625
|
+
else:
|
626
|
+
combined_edges_df = pd.DataFrame()
|
627
|
+
|
628
|
+
if verbose:
|
629
|
+
print(f"\n H5 columns: {sorted(h5_column_names)}")
|
630
|
+
print(f" CSV columns: {edge_types_df.columns.tolist()}")
|
631
|
+
print(f" Combined edges before merge: {combined_edges_df.shape}")
|
632
|
+
|
633
|
+
# Merge H5 data with CSV edge type metadata
|
634
|
+
if not combined_edges_df.empty and not edge_types_df.empty:
|
635
|
+
# Determine merge columns (typically just edge_type_id)
|
636
|
+
merge_cols = []
|
637
|
+
if 'edge_type_id' in combined_edges_df.columns and 'edge_type_id' in edge_types_df.columns:
|
638
|
+
merge_cols.append('edge_type_id')
|
639
|
+
|
640
|
+
if 'population' in edge_types_df.columns and 'population' in combined_edges_df.columns:
|
641
|
+
merge_cols.append('population')
|
642
|
+
|
643
|
+
if merge_cols:
|
644
|
+
if verbose:
|
645
|
+
# Debug merge compatibility
|
646
|
+
h5_edge_types = set(combined_edges_df['edge_type_id'].unique())
|
647
|
+
csv_edge_types = set(edge_types_df['edge_type_id'].unique())
|
648
|
+
overlap = h5_edge_types.intersection(csv_edge_types)
|
649
|
+
|
650
|
+
print(f"\n Merging on columns: {merge_cols}")
|
651
|
+
print(f" Edge types in H5: {sorted(h5_edge_types)}")
|
652
|
+
print(f" Edge types in CSV: {sorted(csv_edge_types)}")
|
653
|
+
print(f" Overlap: {sorted(overlap)}")
|
654
|
+
|
655
|
+
# Perform left join to preserve all H5 edges
|
656
|
+
final_df = combined_edges_df.merge(
|
657
|
+
edge_types_df,
|
658
|
+
on=merge_cols,
|
659
|
+
how='left',
|
660
|
+
suffixes=('', '_csv')
|
399
661
|
)
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
662
|
+
|
663
|
+
if verbose:
|
664
|
+
print(f" After merge: {final_df.shape}")
|
665
|
+
|
666
|
+
# Apply H5 precedence rule: H5 attributes override CSV attributes
|
667
|
+
for col in edge_types_df.columns:
|
668
|
+
if col in merge_cols:
|
669
|
+
continue # Skip merge columns
|
670
|
+
|
671
|
+
csv_col_name = f"{col}_csv" if f"{col}_csv" in final_df.columns else col
|
672
|
+
|
673
|
+
# If column exists in H5, handle row-by-row precedence
|
674
|
+
if col in h5_column_names:
|
675
|
+
if csv_col_name in final_df.columns and csv_col_name != col:
|
676
|
+
# Row-by-row logic: use CSV value only where H5 is NaN
|
677
|
+
mask_h5_nan = final_df[col].isna()
|
678
|
+
final_df.loc[mask_h5_nan, col] = final_df.loc[mask_h5_nan, csv_col_name]
|
679
|
+
|
680
|
+
if verbose and mask_h5_nan.any():
|
681
|
+
n_filled = mask_h5_nan.sum()
|
682
|
+
print(f" Column '{col}': filled {n_filled} NaN values from CSV")
|
683
|
+
|
684
|
+
# Remove CSV version after filling NaN values
|
685
|
+
final_df = final_df.drop(columns=[csv_col_name])
|
686
|
+
elif verbose:
|
687
|
+
print(f" Column '{col}' exists in H5 only")
|
688
|
+
# If column only in CSV, rename if needed
|
689
|
+
elif csv_col_name in final_df.columns and csv_col_name != col:
|
690
|
+
final_df = final_df.rename(columns={csv_col_name: col})
|
691
|
+
if verbose:
|
692
|
+
print(f" Added column '{col}' from CSV")
|
693
|
+
else:
|
694
|
+
if verbose:
|
695
|
+
print(" No common merge columns found. Using H5 data only.")
|
696
|
+
final_df = combined_edges_df
|
697
|
+
else:
|
698
|
+
final_df = combined_edges_df
|
699
|
+
|
700
|
+
if verbose:
|
701
|
+
print(f"\nFinal DataFrame: {final_df.shape}")
|
702
|
+
print(f"Final columns: {final_df.columns.tolist()}")
|
703
|
+
|
704
|
+
# Set edge_type_id as the index to match old function
|
705
|
+
if 'edge_type_id' in final_df.columns:
|
706
|
+
final_df = final_df.set_index('edge_type_id')
|
707
|
+
if verbose:
|
708
|
+
print(f"Set edge_type_id as index. Final shape: {final_df.shape}")
|
709
|
+
|
710
|
+
return final_df
|
437
711
|
|
438
712
|
|
439
713
|
def load_mechanisms_from_config(config=None):
|
@@ -604,7 +878,6 @@ def relation_matrix(
|
|
604
878
|
if relation_func:
|
605
879
|
source_nodes = nodes[source].add_prefix("source_")
|
606
880
|
target_nodes = nodes[target].add_prefix("target_")
|
607
|
-
|
608
881
|
c_edges = pd.merge(
|
609
882
|
left=edges[e_name],
|
610
883
|
right=source_nodes,
|
@@ -856,7 +1129,8 @@ def percent_connections(
|
|
856
1129
|
cons = edges[(edges[source_id_type] == source_id) & (edges[target_id_type] == target_id)]
|
857
1130
|
if not include_gap:
|
858
1131
|
try:
|
859
|
-
|
1132
|
+
gaps = cons["is_gap_junction"]==True
|
1133
|
+
cons = cons[~gaps]
|
860
1134
|
except:
|
861
1135
|
raise Exception("no gap junctions found to drop from connections")
|
862
1136
|
|
@@ -1003,7 +1277,7 @@ def gap_junction_connections(
|
|
1003
1277
|
# print(cons)
|
1004
1278
|
|
1005
1279
|
try:
|
1006
|
-
cons = cons[cons["is_gap_junction"]]
|
1280
|
+
cons = cons[cons["is_gap_junction"]==True]
|
1007
1281
|
except:
|
1008
1282
|
raise Exception("no gap junctions found to drop from connections")
|
1009
1283
|
mean = cons["target_node_id"].value_counts().mean()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bmtool
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.4
|
4
4
|
Summary: BMTool
|
5
5
|
Home-page: https://github.com/cyneuro/bmtool
|
6
6
|
Download-URL:
|
@@ -36,6 +36,7 @@ Requires-Dist: requests
|
|
36
36
|
Requires-Dist: pyyaml
|
37
37
|
Requires-Dist: PyWavelets
|
38
38
|
Requires-Dist: numba
|
39
|
+
Requires-Dist: tqdm
|
39
40
|
Provides-Extra: dev
|
40
41
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
41
42
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
@@ -6,16 +6,16 @@ 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=-kg_TJoqXStIgE5iHpJWpXU6VRKT0YyIVpTw8frNbxA,69653
|
10
10
|
bmtool/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
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
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=
|
16
|
+
bmtool/bmplot/connections.py,sha256=DwnnMp5cUwZC3OqsJqvgTPr4lBzc0sNWYRPmwij0WRk,59337
|
17
17
|
bmtool/bmplot/entrainment.py,sha256=BrBMerqyiG2YWAO_OEFv7OJf3yeFz3l9jUt4NamluLc,32837
|
18
|
-
bmtool/bmplot/lfp.py,sha256=
|
18
|
+
bmtool/bmplot/lfp.py,sha256=7JLozQQJ19ty0ZNyfhkuJAr_K8_pVP9C0flVJd_YXaY,2027
|
19
19
|
bmtool/bmplot/netcon_reports.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
20
|
bmtool/bmplot/spikes.py,sha256=odzCSMbFRHp9qthSGQ0WzMWUwNQ7R1Z6gLT6VPF_o5Q,15326
|
21
21
|
bmtool/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -23,12 +23,12 @@ 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=TAWdGd0tDuouS-JiusMs8WwP7kQpWHPr1nu0XG01TBQ,75056
|
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.4.dist-info/licenses/LICENSE,sha256=qrXg2jj6kz5d0EnN11hllcQt2fcWVNumx0xNbV05nyM,1068
|
30
|
+
bmtool-0.7.4.dist-info/METADATA,sha256=126Cn17YBt6twoBgMXYXK10kOWUXRhnLnGmPr7z7k_4,3595
|
31
|
+
bmtool-0.7.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
32
|
+
bmtool-0.7.4.dist-info/entry_points.txt,sha256=0-BHZ6nUnh0twWw9SXNTiRmKjDnb1VO2DfG_-oprhAc,45
|
33
|
+
bmtool-0.7.4.dist-info/top_level.txt,sha256=gpd2Sj-L9tWbuJEd5E8C8S8XkNm5yUE76klUYcM-eWM,7
|
34
|
+
bmtool-0.7.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|