bmtool 0.7.6__py3-none-any.whl → 0.7.7__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 +7 -2
- bmtool/connectors.py +386 -0
- bmtool/singlecell.py +429 -31
- bmtool/synapses.py +349 -20
- bmtool/util/util.py +40 -5
- {bmtool-0.7.6.dist-info → bmtool-0.7.7.dist-info}/METADATA +1 -1
- {bmtool-0.7.6.dist-info → bmtool-0.7.7.dist-info}/RECORD +11 -11
- {bmtool-0.7.6.dist-info → bmtool-0.7.7.dist-info}/WHEEL +0 -0
- {bmtool-0.7.6.dist-info → bmtool-0.7.7.dist-info}/entry_points.txt +0 -0
- {bmtool-0.7.6.dist-info → bmtool-0.7.7.dist-info}/licenses/LICENSE +0 -0
- {bmtool-0.7.6.dist-info → bmtool-0.7.7.dist-info}/top_level.txt +0 -0
bmtool/synapses.py
CHANGED
@@ -38,6 +38,7 @@ DEFAULT_GAP_JUNCTION_GENERAL_SETTINGS = {
|
|
38
38
|
"tdur": 500.0,
|
39
39
|
"dt": 0.025,
|
40
40
|
"celsius": 20,
|
41
|
+
"iclamp_amp": -0.01, # nA
|
41
42
|
}
|
42
43
|
|
43
44
|
|
@@ -1325,7 +1326,7 @@ class SynapseTuner:
|
|
1325
1326
|
vlamp_status = self.vclamp
|
1326
1327
|
|
1327
1328
|
# Connection dropdown
|
1328
|
-
connection_options = list(self.conn_type_settings.keys())
|
1329
|
+
connection_options = sorted(list(self.conn_type_settings.keys()))
|
1329
1330
|
w_connection = widgets.Dropdown(
|
1330
1331
|
options=connection_options,
|
1331
1332
|
value=self.current_connection,
|
@@ -1762,42 +1763,356 @@ class GapJunctionTuner:
|
|
1762
1763
|
self.general_settings = {**DEFAULT_GAP_JUNCTION_GENERAL_SETTINGS, **general_settings}
|
1763
1764
|
self.conn_type_settings = conn_type_settings
|
1764
1765
|
|
1766
|
+
self._syn_params_cache = {}
|
1767
|
+
self.config = config
|
1768
|
+
self.available_networks = []
|
1769
|
+
self.current_network = None
|
1770
|
+
if self.conn_type_settings is None and self.config is not None:
|
1771
|
+
self.conn_type_settings = self._build_conn_type_settings_from_config(self.config)
|
1772
|
+
if self.conn_type_settings is None or len(self.conn_type_settings) == 0:
|
1773
|
+
raise ValueError("conn_type_settings must be provided or config must be given to load gap junction connections from")
|
1774
|
+
self.current_connection = list(self.conn_type_settings.keys())[0]
|
1775
|
+
self.conn = self.conn_type_settings[self.current_connection]
|
1776
|
+
|
1765
1777
|
h.tstop = self.general_settings["tstart"] + self.general_settings["tdur"] + 100.0
|
1766
1778
|
h.dt = self.general_settings["dt"] # Time step (resolution) of the simulation in ms
|
1767
1779
|
h.steps_per_ms = 1 / h.dt
|
1768
1780
|
h.celsius = self.general_settings["celsius"]
|
1769
1781
|
|
1782
|
+
# Clean up any existing parallel context before setting up gap junctions
|
1783
|
+
try:
|
1784
|
+
pc_temp = h.ParallelContext()
|
1785
|
+
pc_temp.done() # Clean up any existing parallel context
|
1786
|
+
except:
|
1787
|
+
pass # Ignore errors if no existing context
|
1788
|
+
|
1789
|
+
# Force cleanup
|
1790
|
+
import gc
|
1791
|
+
gc.collect()
|
1792
|
+
|
1770
1793
|
# set up gap junctions
|
1771
|
-
pc = h.ParallelContext()
|
1794
|
+
self.pc = h.ParallelContext()
|
1772
1795
|
|
1773
1796
|
# Use provided hoc_cell or create new cells
|
1774
1797
|
if self.hoc_cell is not None:
|
1775
1798
|
self.cell1 = self.hoc_cell
|
1776
1799
|
# For gap junctions, we need two cells, so create a second one if using hoc_cell
|
1777
|
-
self.cell_name =
|
1800
|
+
self.cell_name = self.conn['cell']
|
1778
1801
|
self.cell2 = getattr(h, self.cell_name)()
|
1779
1802
|
else:
|
1780
|
-
self.
|
1803
|
+
print(self.conn)
|
1804
|
+
self.cell_name = self.conn['cell']
|
1781
1805
|
self.cell1 = getattr(h, self.cell_name)()
|
1782
1806
|
self.cell2 = getattr(h, self.cell_name)()
|
1783
1807
|
|
1784
1808
|
self.icl = h.IClamp(self.cell1.soma[0](0.5))
|
1785
1809
|
self.icl.delay = self.general_settings["tstart"]
|
1786
1810
|
self.icl.dur = self.general_settings["tdur"]
|
1787
|
-
self.icl.amp = self.
|
1811
|
+
self.icl.amp = self.general_settings["iclamp_amp"] # nA
|
1788
1812
|
|
1789
|
-
sec1 = list(self.cell1.all)[
|
1790
|
-
sec2 = list(self.cell2.all)[
|
1813
|
+
sec1 = list(self.cell1.all)[self.conn["sec_id"]]
|
1814
|
+
sec2 = list(self.cell2.all)[self.conn["sec_id"]]
|
1791
1815
|
|
1792
|
-
|
1816
|
+
# Use unique IDs to avoid conflicts with existing parallel context setups
|
1817
|
+
import time
|
1818
|
+
unique_id = int(time.time() * 1000) % 10000 # Use timestamp as unique base ID
|
1819
|
+
|
1820
|
+
self.pc.source_var(sec1(self.conn["sec_x"])._ref_v, unique_id, sec=sec1)
|
1793
1821
|
self.gap_junc_1 = h.Gap(sec1(0.5))
|
1794
|
-
pc.target_var(self.gap_junc_1._ref_vgap, 1)
|
1822
|
+
self.pc.target_var(self.gap_junc_1._ref_vgap, unique_id + 1)
|
1795
1823
|
|
1796
|
-
pc.source_var(sec2(
|
1824
|
+
self.pc.source_var(sec2(self.conn["sec_x"])._ref_v, unique_id + 1, sec=sec2)
|
1797
1825
|
self.gap_junc_2 = h.Gap(sec2(0.5))
|
1798
|
-
pc.target_var(self.gap_junc_2._ref_vgap,
|
1826
|
+
self.pc.target_var(self.gap_junc_2._ref_vgap, unique_id)
|
1827
|
+
|
1828
|
+
self.pc.setup_transfer()
|
1829
|
+
|
1830
|
+
# Now it's safe to initialize NEURON
|
1831
|
+
h.finitialize()
|
1832
|
+
|
1833
|
+
def _load_synaptic_params_from_config(self, config: dict, dynamics_params: str) -> dict:
|
1834
|
+
try:
|
1835
|
+
# Get the synaptic models directory from config
|
1836
|
+
synaptic_models_dir = config.get('components', {}).get('synaptic_models_dir', '')
|
1837
|
+
if synaptic_models_dir:
|
1838
|
+
# Handle path variables
|
1839
|
+
if synaptic_models_dir.startswith('$'):
|
1840
|
+
# This is a placeholder, try to resolve it
|
1841
|
+
config_dir = os.path.dirname(config.get('config_path', ''))
|
1842
|
+
synaptic_models_dir = synaptic_models_dir.replace('$COMPONENTS_DIR',
|
1843
|
+
os.path.join(config_dir, 'components'))
|
1844
|
+
synaptic_models_dir = synaptic_models_dir.replace('$BASE_DIR', config_dir)
|
1845
|
+
|
1846
|
+
dynamics_file = os.path.join(synaptic_models_dir, dynamics_params)
|
1847
|
+
|
1848
|
+
if os.path.exists(dynamics_file):
|
1849
|
+
with open(dynamics_file, 'r') as f:
|
1850
|
+
return json.load(f)
|
1851
|
+
else:
|
1852
|
+
print(f"Warning: Dynamics params file not found: {dynamics_file}")
|
1853
|
+
except Exception as e:
|
1854
|
+
print(f"Warning: Error loading synaptic parameters: {e}")
|
1855
|
+
|
1856
|
+
return {}
|
1857
|
+
|
1858
|
+
def _load_available_networks(self) -> None:
|
1859
|
+
"""
|
1860
|
+
Load available network names from the config file for the network dropdown feature.
|
1861
|
+
|
1862
|
+
This method is automatically called during initialization when a config file is provided.
|
1863
|
+
It populates the available_networks list which enables the network dropdown in
|
1864
|
+
InteractiveTuner when multiple networks are available.
|
1865
|
+
|
1866
|
+
Network Dropdown Behavior:
|
1867
|
+
-------------------------
|
1868
|
+
- If only one network exists: No network dropdown is shown
|
1869
|
+
- If multiple networks exist: Network dropdown appears next to connection dropdown
|
1870
|
+
- Networks are loaded from the edges data in the config file
|
1871
|
+
- Current network defaults to the first available if not specified during init
|
1872
|
+
"""
|
1873
|
+
if self.config is None:
|
1874
|
+
self.available_networks = []
|
1875
|
+
return
|
1876
|
+
|
1877
|
+
try:
|
1878
|
+
edges = load_edges_from_config(self.config)
|
1879
|
+
self.available_networks = list(edges.keys())
|
1880
|
+
|
1881
|
+
# Set current network to first available if not specified
|
1882
|
+
if self.current_network is None and self.available_networks:
|
1883
|
+
self.current_network = self.available_networks[0]
|
1884
|
+
except Exception as e:
|
1885
|
+
print(f"Warning: Could not load networks from config: {e}")
|
1886
|
+
self.available_networks = []
|
1887
|
+
|
1888
|
+
def _build_conn_type_settings_from_config(self, config_path: str) -> Dict[str, dict]:
|
1889
|
+
# Load configuration and get nodes and edges using util.py methods
|
1890
|
+
config = load_config(config_path)
|
1891
|
+
# Ensure the config dict knows its source path so path substitutions can be resolved
|
1892
|
+
try:
|
1893
|
+
config['config_path'] = config_path
|
1894
|
+
except Exception:
|
1895
|
+
pass
|
1896
|
+
nodes = load_nodes_from_config(config_path)
|
1897
|
+
edges = load_edges_from_config(config_path)
|
1898
|
+
|
1899
|
+
conn_type_settings = {}
|
1900
|
+
|
1901
|
+
# Process all edge datasets
|
1902
|
+
for edge_dataset_name, edge_df in edges.items():
|
1903
|
+
if edge_df.empty:
|
1904
|
+
continue
|
1905
|
+
|
1906
|
+
# Merging with node data to get model templates
|
1907
|
+
source_node_df = None
|
1908
|
+
target_node_df = None
|
1909
|
+
|
1910
|
+
# First, try to deterministically parse the edge_dataset_name for patterns like '<src>_to_<tgt>'
|
1911
|
+
if '_to_' in edge_dataset_name:
|
1912
|
+
parts = edge_dataset_name.split('_to_')
|
1913
|
+
if len(parts) == 2:
|
1914
|
+
src_name, tgt_name = parts
|
1915
|
+
if src_name in nodes:
|
1916
|
+
source_node_df = nodes[src_name].add_prefix('source_')
|
1917
|
+
if tgt_name in nodes:
|
1918
|
+
target_node_df = nodes[tgt_name].add_prefix('target_')
|
1919
|
+
|
1920
|
+
# If not found by parsing name, fall back to inspecting a sample edge row
|
1921
|
+
if source_node_df is None or target_node_df is None:
|
1922
|
+
sample_edge = edge_df.iloc[0] if len(edge_df) > 0 else None
|
1923
|
+
if sample_edge is not None:
|
1924
|
+
source_pop_name = sample_edge.get('source_population', '')
|
1925
|
+
target_pop_name = sample_edge.get('target_population', '')
|
1926
|
+
if source_pop_name in nodes:
|
1927
|
+
source_node_df = nodes[source_pop_name].add_prefix('source_')
|
1928
|
+
if target_pop_name in nodes:
|
1929
|
+
target_node_df = nodes[target_pop_name].add_prefix('target_')
|
1930
|
+
|
1931
|
+
# As a last resort, attempt to heuristically match
|
1932
|
+
if source_node_df is None or target_node_df is None:
|
1933
|
+
for pop_name, node_df in nodes.items():
|
1934
|
+
if source_node_df is None and (edge_dataset_name.startswith(pop_name) or edge_dataset_name.endswith(pop_name)):
|
1935
|
+
source_node_df = node_df.add_prefix('source_')
|
1936
|
+
if target_node_df is None and (edge_dataset_name.startswith(pop_name) or edge_dataset_name.endswith(pop_name)):
|
1937
|
+
target_node_df = node_df.add_prefix('target_')
|
1938
|
+
|
1939
|
+
if source_node_df is None or target_node_df is None:
|
1940
|
+
print(f"Warning: Could not find node data for edge dataset {edge_dataset_name}")
|
1941
|
+
continue
|
1942
|
+
|
1943
|
+
# Merge edge data with source node info
|
1944
|
+
edges_with_source = pd.merge(
|
1945
|
+
edge_df.reset_index(),
|
1946
|
+
source_node_df,
|
1947
|
+
how='left',
|
1948
|
+
left_on='source_node_id',
|
1949
|
+
right_index=True
|
1950
|
+
)
|
1951
|
+
|
1952
|
+
# Merge with target node info
|
1953
|
+
edges_with_nodes = pd.merge(
|
1954
|
+
edges_with_source,
|
1955
|
+
target_node_df,
|
1956
|
+
how='left',
|
1957
|
+
left_on='target_node_id',
|
1958
|
+
right_index=True
|
1959
|
+
)
|
1960
|
+
|
1961
|
+
# Skip edge datasets that don't have gap junction information
|
1962
|
+
if 'is_gap_junction' not in edges_with_nodes.columns:
|
1963
|
+
continue
|
1964
|
+
|
1965
|
+
# Filter to only gap junction edges
|
1966
|
+
# Handle NaN values in is_gap_junction column
|
1967
|
+
gap_junction_mask = edges_with_nodes['is_gap_junction'].fillna(False) == True
|
1968
|
+
gap_junction_edges = edges_with_nodes[gap_junction_mask]
|
1969
|
+
if gap_junction_edges.empty:
|
1970
|
+
continue
|
1971
|
+
|
1972
|
+
# Get unique edge types from the gap junction edges
|
1973
|
+
if 'edge_type_id' in gap_junction_edges.columns:
|
1974
|
+
edge_types = gap_junction_edges['edge_type_id'].unique()
|
1975
|
+
else:
|
1976
|
+
edge_types = [None] # Single edge type
|
1977
|
+
|
1978
|
+
# Process each edge type
|
1979
|
+
for edge_type_id in edge_types:
|
1980
|
+
# Filter edges for this type
|
1981
|
+
if edge_type_id is not None:
|
1982
|
+
edge_type_data = gap_junction_edges[gap_junction_edges['edge_type_id'] == edge_type_id]
|
1983
|
+
else:
|
1984
|
+
edge_type_data = gap_junction_edges
|
1985
|
+
|
1986
|
+
if len(edge_type_data) == 0:
|
1987
|
+
continue
|
1988
|
+
|
1989
|
+
# Get representative edge for this type
|
1990
|
+
edge_info = edge_type_data.iloc[0]
|
1991
|
+
|
1992
|
+
# Process gap junction
|
1993
|
+
source_model_template = edge_info.get('source_model_template', '')
|
1994
|
+
target_model_template = edge_info.get('target_model_template', '')
|
1995
|
+
|
1996
|
+
source_cell_type = source_model_template.replace('hoc:', '') if source_model_template.startswith('hoc:') else source_model_template
|
1997
|
+
target_cell_type = target_model_template.replace('hoc:', '') if target_model_template.startswith('hoc:') else target_model_template
|
1998
|
+
|
1999
|
+
if source_cell_type != target_cell_type:
|
2000
|
+
continue # Only process gap junctions between same cell types
|
2001
|
+
|
2002
|
+
source_pop = edge_info.get('source_pop_name', '')
|
2003
|
+
target_pop = edge_info.get('target_pop_name', '')
|
2004
|
+
|
2005
|
+
conn_name = f"{source_pop}2{target_pop}_gj"
|
2006
|
+
if edge_type_id is not None:
|
2007
|
+
conn_name += f"_type_{edge_type_id}"
|
2008
|
+
|
2009
|
+
conn_settings = {
|
2010
|
+
'cell': source_cell_type,
|
2011
|
+
'sec_id': 0,
|
2012
|
+
'sec_x': 0.5,
|
2013
|
+
'iclamp_amp': -0.01,
|
2014
|
+
'spec_syn_param': {}
|
2015
|
+
}
|
2016
|
+
|
2017
|
+
# Load dynamics params
|
2018
|
+
dynamics_file_name = edge_info.get('dynamics_params', '')
|
2019
|
+
if dynamics_file_name and dynamics_file_name.upper() != 'NULL':
|
2020
|
+
try:
|
2021
|
+
syn_params = self._load_synaptic_params_from_config(config, dynamics_file_name)
|
2022
|
+
conn_settings['spec_syn_param'] = syn_params
|
2023
|
+
except Exception as e:
|
2024
|
+
print(f"Warning: could not load dynamics_params file '{dynamics_file_name}': {e}")
|
2025
|
+
|
2026
|
+
conn_type_settings[conn_name] = conn_settings
|
2027
|
+
|
2028
|
+
return conn_type_settings
|
1799
2029
|
|
1800
|
-
|
2030
|
+
def _switch_connection(self, new_connection: str) -> None:
|
2031
|
+
"""
|
2032
|
+
Switch to a different gap junction connection and update all related properties.
|
2033
|
+
|
2034
|
+
Parameters:
|
2035
|
+
-----------
|
2036
|
+
new_connection : str
|
2037
|
+
Name of the new connection type to switch to.
|
2038
|
+
"""
|
2039
|
+
if new_connection not in self.conn_type_settings:
|
2040
|
+
raise ValueError(f"Connection '{new_connection}' not found in conn_type_settings")
|
2041
|
+
|
2042
|
+
# Update current connection
|
2043
|
+
self.current_connection = new_connection
|
2044
|
+
self.conn = self.conn_type_settings[new_connection]
|
2045
|
+
|
2046
|
+
# Check if cell type changed
|
2047
|
+
new_cell_name = self.conn['cell']
|
2048
|
+
if self.cell_name != new_cell_name:
|
2049
|
+
self.cell_name = new_cell_name
|
2050
|
+
|
2051
|
+
# Recreate cells
|
2052
|
+
if self.hoc_cell is None:
|
2053
|
+
self.cell1 = getattr(h, self.cell_name)()
|
2054
|
+
self.cell2 = getattr(h, self.cell_name)()
|
2055
|
+
else:
|
2056
|
+
# For hoc_cell, recreate the second cell
|
2057
|
+
self.cell2 = getattr(h, self.cell_name)()
|
2058
|
+
|
2059
|
+
# Recreate IClamp
|
2060
|
+
self.icl = h.IClamp(self.cell1.soma[0](0.5))
|
2061
|
+
self.icl.delay = self.general_settings["tstart"]
|
2062
|
+
self.icl.dur = self.general_settings["tdur"]
|
2063
|
+
self.icl.amp = self.general_settings["iclamp_amp"]
|
2064
|
+
else:
|
2065
|
+
# Update IClamp parameters even if same cell type
|
2066
|
+
self.icl.amp = self.general_settings["iclamp_amp"]
|
2067
|
+
|
2068
|
+
# Always recreate gap junctions when switching connections
|
2069
|
+
# (even for same cell type, sec_id or sec_x might differ)
|
2070
|
+
|
2071
|
+
# Clean up previous gap junctions and parallel context
|
2072
|
+
if hasattr(self, 'gap_junc_1'):
|
2073
|
+
del self.gap_junc_1
|
2074
|
+
if hasattr(self, 'gap_junc_2'):
|
2075
|
+
del self.gap_junc_2
|
2076
|
+
|
2077
|
+
# Properly clean up the existing parallel context
|
2078
|
+
if hasattr(self, 'pc'):
|
2079
|
+
self.pc.done() # Clean up existing parallel context
|
2080
|
+
|
2081
|
+
# Force garbage collection and reset NEURON state
|
2082
|
+
import gc
|
2083
|
+
gc.collect()
|
2084
|
+
h.finitialize()
|
2085
|
+
|
2086
|
+
# Create a fresh parallel context after cleanup
|
2087
|
+
self.pc = h.ParallelContext()
|
2088
|
+
|
2089
|
+
try:
|
2090
|
+
sec1 = list(self.cell1.all)[self.conn["sec_id"]]
|
2091
|
+
sec2 = list(self.cell2.all)[self.conn["sec_id"]]
|
2092
|
+
|
2093
|
+
# Use unique IDs to avoid conflicts with existing parallel context setups
|
2094
|
+
import time
|
2095
|
+
unique_id = int(time.time() * 1000) % 10000 # Use timestamp as unique base ID
|
2096
|
+
|
2097
|
+
self.pc.source_var(sec1(self.conn["sec_x"])._ref_v, unique_id, sec=sec1)
|
2098
|
+
self.gap_junc_1 = h.Gap(sec1(0.5))
|
2099
|
+
self.pc.target_var(self.gap_junc_1._ref_vgap, unique_id + 1)
|
2100
|
+
|
2101
|
+
self.pc.source_var(sec2(self.conn["sec_x"])._ref_v, unique_id + 1, sec=sec2)
|
2102
|
+
self.gap_junc_2 = h.Gap(sec2(0.5))
|
2103
|
+
self.pc.target_var(self.gap_junc_2._ref_vgap, unique_id)
|
2104
|
+
|
2105
|
+
self.pc.setup_transfer()
|
2106
|
+
except Exception as e:
|
2107
|
+
print(f"Error setting up gap junctions: {e}")
|
2108
|
+
# Try to continue with basic setup
|
2109
|
+
self.gap_junc_1 = h.Gap(list(self.cell1.all)[self.conn["sec_id"]](0.5))
|
2110
|
+
self.gap_junc_2 = h.Gap(list(self.cell2.all)[self.conn["sec_id"]](0.5))
|
2111
|
+
|
2112
|
+
# Reset NEURON state after complete setup
|
2113
|
+
h.finitialize()
|
2114
|
+
|
2115
|
+
print(f"Successfully switched to connection: {new_connection}")
|
1801
2116
|
|
1802
2117
|
def model(self, resistance):
|
1803
2118
|
"""
|
@@ -1901,13 +2216,9 @@ class GapJunctionTuner:
|
|
1901
2216
|
continuous_update=True,
|
1902
2217
|
)
|
1903
2218
|
|
1904
|
-
ui = VBox([w_run, resistance])
|
1905
|
-
|
1906
|
-
# Create an output widget to control what gets cleared
|
1907
2219
|
output = widgets.Output()
|
1908
2220
|
|
1909
|
-
|
1910
|
-
display(output)
|
2221
|
+
ui_widgets = [w_run, resistance]
|
1911
2222
|
|
1912
2223
|
def on_button(*args):
|
1913
2224
|
with output:
|
@@ -1915,7 +2226,7 @@ class GapJunctionTuner:
|
|
1915
2226
|
output.clear_output(wait=True)
|
1916
2227
|
|
1917
2228
|
resistance_for_gap = resistance.value
|
1918
|
-
print(f"Running simulation with resistance: {resistance_for_gap}")
|
2229
|
+
print(f"Running simulation with resistance: {resistance_for_gap:0.6f} and {self.general_settings['iclamp_amp']*1000}pA current clamps")
|
1919
2230
|
|
1920
2231
|
try:
|
1921
2232
|
self.model(resistance_for_gap)
|
@@ -1936,6 +2247,25 @@ class GapJunctionTuner:
|
|
1936
2247
|
|
1937
2248
|
traceback.print_exc()
|
1938
2249
|
|
2250
|
+
# Add connection dropdown if multiple connections exist
|
2251
|
+
if len(self.conn_type_settings) > 1:
|
2252
|
+
connection_dropdown = widgets.Dropdown(
|
2253
|
+
options=list(self.conn_type_settings.keys()),
|
2254
|
+
value=self.current_connection,
|
2255
|
+
description='Connection:',
|
2256
|
+
)
|
2257
|
+
def on_connection_change(change):
|
2258
|
+
if change['type'] == 'change' and change['name'] == 'value':
|
2259
|
+
self._switch_connection(change['new'])
|
2260
|
+
on_button() # Automatically rerun the simulation after switching
|
2261
|
+
connection_dropdown.observe(on_connection_change)
|
2262
|
+
ui_widgets.insert(0, connection_dropdown)
|
2263
|
+
|
2264
|
+
ui = VBox(ui_widgets)
|
2265
|
+
|
2266
|
+
display(ui)
|
2267
|
+
display(output)
|
2268
|
+
|
1939
2269
|
# Run once initially
|
1940
2270
|
on_button()
|
1941
2271
|
w_run.on_click(on_button)
|
@@ -2316,7 +2646,6 @@ class SynapseOptimizer:
|
|
2316
2646
|
self.tuner.ispk = None
|
2317
2647
|
self.tuner.SingleEvent(plot_and_print=True)
|
2318
2648
|
|
2319
|
-
|
2320
2649
|
# dataclass means just init the typehints as self.typehint. looks a bit cleaner
|
2321
2650
|
@dataclass
|
2322
2651
|
class GapOptimizationResult:
|
@@ -2531,4 +2860,4 @@ class GapJunctionOptimizer:
|
|
2531
2860
|
results["resistance"].append(resistance)
|
2532
2861
|
results["coupling_coefficient"].append(cc)
|
2533
2862
|
|
2534
|
-
return results
|
2863
|
+
return results
|
bmtool/util/util.py
CHANGED
@@ -1163,9 +1163,43 @@ def percent_connections(
|
|
1163
1163
|
num_sources = s_list[source_id_type].value_counts().sort_index().loc[source_id]
|
1164
1164
|
num_targets = t_list[target_id_type].value_counts().sort_index().loc[target_id]
|
1165
1165
|
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1166
|
+
# Check if this is a recurrent network (same source and target population)
|
1167
|
+
# For recurrent networks (e.g., FSI->FSI), we need special handling because:
|
1168
|
+
# - Each pair can have at most 2 directed connections (bidirectional)
|
1169
|
+
# - We want to count unique pairs, not directed connections
|
1170
|
+
# - The denominator should be n*(n-1)/2, not n*n
|
1171
|
+
is_recurrent = source_id == target_id
|
1172
|
+
|
1173
|
+
if is_recurrent:
|
1174
|
+
# For recurrent networks, calculate connectivity based on unique undirected pairs
|
1175
|
+
# This avoids double-counting reciprocal connections and uses correct denominator
|
1176
|
+
pair_counts = {}
|
1177
|
+
for _, row in cons.iterrows():
|
1178
|
+
sid = row['source_node_id']
|
1179
|
+
tid = row['target_node_id']
|
1180
|
+
if sid != tid: # Exclude self-connections
|
1181
|
+
# Use symmetric pair key to count connections per unique pair
|
1182
|
+
pair_key = (min(sid, tid), max(sid, tid))
|
1183
|
+
if pair_key not in pair_counts:
|
1184
|
+
pair_counts[pair_key] = 0
|
1185
|
+
pair_counts[pair_key] += 1
|
1186
|
+
|
1187
|
+
# Count pairs with exactly 1 connection (unidirectional) vs 2 connections (bidirectional)
|
1188
|
+
num_uni = sum(1 for count in pair_counts.values() if count == 1)
|
1189
|
+
num_bi = sum(1 for count in pair_counts.values() if count == 2)
|
1190
|
+
|
1191
|
+
# Total possible unique pairs (excluding self-connections)
|
1192
|
+
total_possible = num_sources * (num_sources - 1) / 2
|
1193
|
+
total = round((num_uni + num_bi) / total_possible * 100, 2)
|
1194
|
+
uni = round(num_uni / total_possible * 100, 2)
|
1195
|
+
bi = round(num_bi / total_possible * 100, 2)
|
1196
|
+
else:
|
1197
|
+
# For non-recurrent networks, use the original calculation
|
1198
|
+
# Each connection is unique (no double-counting issues)
|
1199
|
+
total = round(total_cons / (num_sources * num_targets) * 100, 2)
|
1200
|
+
uni = round(num_uni / (num_sources * num_targets) * 100, 2)
|
1201
|
+
bi = round(num_bi / (num_sources * num_targets) * 100, 2)
|
1202
|
+
|
1169
1203
|
if method == "total":
|
1170
1204
|
return total
|
1171
1205
|
if method == "uni":
|
@@ -1309,9 +1343,10 @@ def gap_junction_connections(
|
|
1309
1343
|
s_list = kwargs["source_nodes"]
|
1310
1344
|
|
1311
1345
|
cons = edges[(edges[source_id_type] == source_id) & (edges[target_id_type] == target_id)]
|
1312
|
-
#
|
1346
|
+
# print(cons)
|
1347
|
+
|
1313
1348
|
try:
|
1314
|
-
cons = cons[cons["is_gap_junction"]]
|
1349
|
+
cons = cons[cons["is_gap_junction"]==True]
|
1315
1350
|
except:
|
1316
1351
|
raise Exception("no gap junctions found to drop from connections")
|
1317
1352
|
|
@@ -1,19 +1,19 @@
|
|
1
1
|
bmtool/SLURM.py,sha256=UBfITY1MtYo95nyKglgGSqAC9Ds8PBvlHczsiNMFxvc,20573
|
2
2
|
bmtool/__init__.py,sha256=r_8fXc-2uj1DndCdhB4jME51r1pn6ESTD5zRc355BrU,134
|
3
3
|
bmtool/__main__.py,sha256=uCEqPwRxIuNRUASKhsvh4S8Nkp4dqnvfXTMUv-wWWRU,665
|
4
|
-
bmtool/connectors.py,sha256=
|
4
|
+
bmtool/connectors.py,sha256=tkCh9oVsIgaV5zzBzTFTG2Y6YVAoIHv2Pm-qxZAbca8,93124
|
5
5
|
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
|
-
bmtool/singlecell.py,sha256=
|
9
|
-
bmtool/synapses.py,sha256=
|
8
|
+
bmtool/singlecell.py,sha256=qcciWdn8RjJCp7z8G2dCdPTLS9Brho3lPFSIsYMw8Ls,65348
|
9
|
+
bmtool/synapses.py,sha256=2M0dUBXfdYrJA3FWLbqsZGqAkO0sS2gQ-zq6GHYaVE4,121393
|
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=xn9QaNN7fTTVNaalS8QHIp2TVo7nXgiT-nO4nKXOo6w,59612
|
17
17
|
bmtool/bmplot/entrainment.py,sha256=BrBMerqyiG2YWAO_OEFv7OJf3yeFz3l9jUt4NamluLc,32837
|
18
18
|
bmtool/bmplot/lfp.py,sha256=7JLozQQJ19ty0ZNyfhkuJAr_K8_pVP9C0flVJd_YXaY,2027
|
19
19
|
bmtool/bmplot/netcon_reports.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=oXrY-Cc4wxPvgJVg2TqaAPua_gAl1Kj1nqrLgsqsXS8,77815
|
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.7.dist-info/licenses/LICENSE,sha256=qrXg2jj6kz5d0EnN11hllcQt2fcWVNumx0xNbV05nyM,1068
|
30
|
+
bmtool-0.7.7.dist-info/METADATA,sha256=uHViQaXG-O6O_GqENKn1vOkXp6g_yN94M9jSRilSnJU,3595
|
31
|
+
bmtool-0.7.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
32
|
+
bmtool-0.7.7.dist-info/entry_points.txt,sha256=0-BHZ6nUnh0twWw9SXNTiRmKjDnb1VO2DfG_-oprhAc,45
|
33
|
+
bmtool-0.7.7.dist-info/top_level.txt,sha256=gpd2Sj-L9tWbuJEd5E8C8S8XkNm5yUE76klUYcM-eWM,7
|
34
|
+
bmtool-0.7.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|