bmtool 0.5.8__tar.gz → 0.5.9.5__tar.gz

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.
Files changed (29) hide show
  1. {bmtool-0.5.8 → bmtool-0.5.9.5}/PKG-INFO +1 -1
  2. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/bmplot.py +134 -28
  3. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/connectors.py +15 -8
  4. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool.egg-info/PKG-INFO +1 -1
  5. {bmtool-0.5.8 → bmtool-0.5.9.5}/setup.py +1 -1
  6. {bmtool-0.5.8 → bmtool-0.5.9.5}/LICENSE +0 -0
  7. {bmtool-0.5.8 → bmtool-0.5.9.5}/README.md +0 -0
  8. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/SLURM.py +0 -0
  9. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/__init__.py +0 -0
  10. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/__main__.py +0 -0
  11. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/debug/__init__.py +0 -0
  12. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/debug/commands.py +0 -0
  13. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/debug/debug.py +0 -0
  14. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/graphs.py +0 -0
  15. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/manage.py +0 -0
  16. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/plot_commands.py +0 -0
  17. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/singlecell.py +0 -0
  18. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/synapses.py +0 -0
  19. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/util/__init__.py +0 -0
  20. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/util/commands.py +0 -0
  21. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/util/neuron/__init__.py +0 -0
  22. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/util/neuron/celltuner.py +0 -0
  23. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool/util/util.py +0 -0
  24. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool.egg-info/SOURCES.txt +0 -0
  25. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool.egg-info/dependency_links.txt +0 -0
  26. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool.egg-info/entry_points.txt +0 -0
  27. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool.egg-info/requires.txt +0 -0
  28. {bmtool-0.5.8 → bmtool-0.5.9.5}/bmtool.egg-info/top_level.txt +0 -0
  29. {bmtool-0.5.8 → bmtool-0.5.9.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bmtool
3
- Version: 0.5.8
3
+ Version: 0.5.9.5
4
4
  Summary: BMTool
5
5
  Home-page: https://github.com/cyneuro/bmtool
6
6
  Download-URL:
@@ -405,6 +405,76 @@ def connection_histogram(config=None,nodes=None,edges=None,sources=[],targets=[]
405
405
  tids = []
406
406
  util.relation_matrix(config,nodes,edges,sources,targets,sids,tids,prepend_pop,relation_func=connection_pair_histogram,synaptic_info=synaptic_info)
407
407
 
408
+ def connection_distance(config: str,source: str,target: str,
409
+ source_cell_id: int,target_id_type: str) -> None:
410
+ """
411
+ Plots the 3D spatial distribution of target nodes relative to a source node
412
+ and a histogram of distances from the source node to each target node.
413
+
414
+ Parameters:
415
+ ----------
416
+ config: (str) A BMTK simulation config
417
+ sources: (str) network name(s) to plot
418
+ targets: (str) network name(s) to plot
419
+ source_cell_id : (int) ID of the source cell for calculating distances to target nodes.
420
+ target_id_type : (str) A string to filter target nodes based off the target_query.
421
+
422
+ """
423
+ if not config:
424
+ raise Exception("config not defined")
425
+ if not source or not target:
426
+ raise Exception("Sources or targets not defined")
427
+ #if source != target:
428
+ #raise Exception("Code is setup for source and target to be the same! Look at source code for function to add feature")
429
+
430
+ # Load nodes and edges based on config file
431
+ nodes, edges = util.load_nodes_edges_from_config(config)
432
+
433
+ edge_network = source + "_to_" + target
434
+ node_network = source
435
+
436
+ # Filter edges to obtain connections originating from the source node
437
+ edge = edges[edge_network]
438
+ edge = edge[edge['source_node_id'] == source_cell_id]
439
+ if target_id_type:
440
+ edge = edge[edge['target_query'].str.contains(target_id_type, na=False)]
441
+
442
+ target_node_ids = edge['target_node_id']
443
+
444
+ # Filter nodes to obtain only the target and source nodes
445
+ node = nodes[node_network]
446
+ target_nodes = node.loc[node.index.isin(target_node_ids)]
447
+ source_node = node.loc[node.index == source_cell_id]
448
+
449
+ # Calculate distances between source node and each target node
450
+ target_positions = target_nodes[['pos_x', 'pos_y', 'pos_z']].values
451
+ source_position = np.array([source_node['pos_x'], source_node['pos_y'], source_node['pos_z']]).ravel() # Ensure 1D shape
452
+ distances = np.linalg.norm(target_positions - source_position, axis=1)
453
+
454
+ # Plot positions of source and target nodes in 3D space
455
+ fig = plt.figure(figsize=(8, 6))
456
+ ax = fig.add_subplot(111, projection='3d')
457
+
458
+ ax.scatter(target_nodes['pos_x'], target_nodes['pos_y'], target_nodes['pos_z'], c='blue', label="target cells")
459
+ ax.scatter(source_node['pos_x'], source_node['pos_y'], source_node['pos_z'], c='red', label="source cell")
460
+
461
+ # Optional: Add text annotations for distances
462
+ # for i, distance in enumerate(distances):
463
+ # ax.text(target_nodes['pos_x'].iloc[i], target_nodes['pos_y'].iloc[i], target_nodes['pos_z'].iloc[i],
464
+ # f'{distance:.2f}', color='black', fontsize=8, ha='center')
465
+
466
+ plt.legend()
467
+ plt.show()
468
+
469
+ # Plot distances in a separate 2D plot
470
+ plt.figure(figsize=(8, 6))
471
+ plt.hist(distances, bins=20, color='blue', edgecolor='black')
472
+ plt.xlabel("Distance")
473
+ plt.ylabel("Count")
474
+ plt.title("Distance from Source Node to Each Target Node")
475
+ plt.grid(True)
476
+ plt.show()
477
+
408
478
  def edge_histogram_matrix(config=None,sources = None,targets=None,sids=None,tids=None,no_prepend_pop=None,edge_property = None,time = None,time_compare = None,report=None,title=None,save_file=None):
409
479
  """
410
480
  write about function here
@@ -480,8 +550,14 @@ def plot_connection_info(text, num, source_labels,target_labels, title, syn_info
480
550
  # Loop over data dimensions and create text annotations.
481
551
  for i in range(num_source):
482
552
  for j in range(num_target):
483
- edge_info = text[i,j]
484
- graph_dict[str(source_labels[i])+'2'+str(target_labels[j])] = edge_info
553
+ edge_info = text[i, j]
554
+
555
+ # Initialize the dictionary for the source node if not already done
556
+ if source_labels[i] not in graph_dict:
557
+ graph_dict[source_labels[i]] = {}
558
+
559
+ # Add edge info for the target node
560
+ graph_dict[source_labels[i]][target_labels[j]] = edge_info
485
561
  if syn_info =='2' or syn_info =='3':
486
562
  if num_source > 8 and num_source <20:
487
563
  fig_text = ax1.text(j, i, edge_info,
@@ -726,71 +802,101 @@ def plot_spikes(nodes, spikes_file,save_file=None):
726
802
 
727
803
  return
728
804
 
729
- def plot_3d_positions(config=None,populations_list=None,group_by=None,title=None,save_file=None):
805
+ def plot_3d_positions(config=None, populations_list=None, group_by=None, title=None, save_file=None, subset=None):
730
806
  """
731
- plots a 3D graph of all cells with x,y,z location
732
- config: A BMTK simulation config
733
- populations_list: Which network(s) to plot
734
- group_by: How to name cell groups
735
- title: plot title
736
- save_file: If plot should be saved
807
+ Plots a 3D graph of all cells with x, y, z location.
808
+
809
+ Parameters:
810
+ - config: A BMTK simulation config
811
+ - populations_list: Which network(s) to plot
812
+ - group_by: How to name cell groups
813
+ - title: Plot title
814
+ - save_file: If plot should be saved
815
+ - subset: Take every Nth row. This will make plotting large network graphs easier to see.
737
816
  """
738
817
 
739
818
  if not config:
740
819
  raise Exception("config not defined")
741
- if populations_list == None:
820
+
821
+ if populations_list is None:
742
822
  populations_list = "all"
823
+
824
+ # Set group keys (e.g., node types)
743
825
  group_keys = group_by
744
- if title == None:
826
+ if title is None:
745
827
  title = "3D positions"
746
828
 
829
+ # Load nodes from the configuration
747
830
  nodes = util.load_nodes_from_config(config)
748
831
 
832
+ # Get the list of populations to plot
749
833
  if 'all' in populations_list:
750
834
  populations = list(nodes)
751
835
  else:
752
836
  populations = populations_list.split(",")
753
-
837
+
838
+ # Split group_by into list
754
839
  group_keys = group_keys.split(",")
755
- group_keys += (len(populations)-len(group_keys)) * ["node_type_id"] #Extend the array to default values if not enough given
756
- fig = plt.figure(figsize=(10,10))
840
+ group_keys += (len(populations) - len(group_keys)) * ["node_type_id"] # Extend the array to default values if not enough given
841
+ if len(group_keys) > 1:
842
+ raise Exception("Only one group by is supported currently!")
843
+
844
+ fig = plt.figure(figsize=(10, 10))
757
845
  ax = fig.add_subplot(projection='3d')
758
846
  handles = []
759
- for nodes_key,group_key in zip(list(nodes),group_keys):
760
- if 'all' not in populations and nodes_key not in populations:
761
- continue
762
-
763
- nodes_df = nodes[nodes_key]
764
847
 
848
+ for pop in (list(nodes)):
849
+
850
+ if 'all' not in populations and pop not in populations:
851
+ continue
852
+
853
+ nodes_df = nodes[pop]
854
+ group_key = group_keys[0]
855
+
856
+ # If group_key is provided, ensure the column exists in the dataframe
765
857
  if group_key is not None:
766
858
  if group_key not in nodes_df:
767
- raise Exception('Could not find column {}'.format(group_key))
859
+ raise Exception(f"Could not find column '{group_key}' in {pop}")
860
+
768
861
  groupings = nodes_df.groupby(group_key)
769
-
770
862
  n_colors = nodes_df[group_key].nunique()
771
- color_norm = colors.Normalize(vmin=0, vmax=(n_colors-1))
863
+ color_norm = colors.Normalize(vmin=0, vmax=(n_colors - 1))
772
864
  scalar_map = cmx.ScalarMappable(norm=color_norm, cmap='hsv')
773
- color_map = [scalar_map.to_rgba(i) for i in range(0, n_colors)]
865
+ color_map = [scalar_map.to_rgba(i) for i in range(n_colors)]
774
866
  else:
775
867
  groupings = [(None, nodes_df)]
776
868
  color_map = ['blue']
777
869
 
870
+ # Loop over groupings and plot
778
871
  for color, (group_name, group_df) in zip(color_map, groupings):
779
- if "pos_x" not in group_df: #could also check model type == virtual
780
- continue #can't plot them if there isn't an xy coordinate (may be virtual)
781
- h = ax.scatter(group_df["pos_x"],group_df["pos_y"],group_df["pos_z"],color=color,label=group_name)
872
+ if "pos_x" not in group_df or "pos_y" not in group_df or "pos_z" not in group_df:
873
+ print(f"Warning: Missing position columns in group '{group_name}' for {pop}. Skipping this group.")
874
+ continue # Skip if position columns are missing
875
+
876
+ # Subset the dataframe by taking every Nth row if subset is provided
877
+ if subset is not None:
878
+ group_df = group_df.iloc[::subset]
879
+
880
+ h = ax.scatter(group_df["pos_x"], group_df["pos_y"], group_df["pos_z"], color=color, label=group_name)
782
881
  handles.append(h)
882
+
783
883
  if not handles:
884
+ print("No data to plot.")
784
885
  return
886
+
887
+ # Set plot title and legend
785
888
  plt.title(title)
786
889
  plt.legend(handles=handles)
787
890
 
891
+ # Draw the plot
788
892
  plt.draw()
789
893
 
894
+ # Save the plot if save_file is provided
790
895
  if save_file:
791
896
  plt.savefig(save_file)
792
- notebook = is_notebook
793
- if notebook == False:
897
+
898
+ # Show the plot if running outside of a notebook
899
+ if not is_notebook:
794
900
  plt.show()
795
901
 
796
902
  return
@@ -580,9 +580,11 @@ class ReciprocalConnector(AbstractConnector):
580
580
  self.source = source
581
581
  self.target = target
582
582
  if self.source is None or len(self.source) == 0:
583
- raise ValueError("Source nodes do not exists")
583
+ src_str, trg_str = self.get_nodes_info()
584
+ raise ValueError(f"{src_str} nodes do not exists")
584
585
  if self.target is None or len(self.target) == 0:
585
- raise ValueError("Target nodes do not exists")
586
+ src_str, trg_str = self.get_nodes_info()
587
+ raise ValueError(f"{trg_str} nodes do not exists")
586
588
 
587
589
  # Setup nodes
588
590
  self.recurrent = is_same_pop(self.source, self.target, quick=self.quick)
@@ -1130,9 +1132,11 @@ class UnidirectionConnector(AbstractConnector):
1130
1132
  self.source = source
1131
1133
  self.target = target
1132
1134
  if self.source is None or len(self.source) == 0:
1133
- raise ValueError("Source nodes do not exists")
1135
+ src_str, trg_str = self.get_nodes_info()
1136
+ raise ValueError(f"{src_str} nodes do not exists")
1134
1137
  if self.target is None or len(self.target) == 0:
1135
- raise ValueError("Target nodes do not exists")
1138
+ src_str, trg_str = self.get_nodes_info()
1139
+ raise ValueError(f"{trg_str} nodes do not exists")
1136
1140
  self.n_pair = len(self.source) * len(self.target)
1137
1141
 
1138
1142
  def edge_params(self):
@@ -1179,6 +1183,7 @@ class UnidirectionConnector(AbstractConnector):
1179
1183
  + src_str + "\n to " + trg_str,flush=True)
1180
1184
 
1181
1185
  # Make random connections
1186
+
1182
1187
  p_arg = self.p_arg(source, target)
1183
1188
  p = self.p(p_arg)
1184
1189
  possible = p > 0
@@ -1274,8 +1279,9 @@ class GapJunction(UnidirectionConnector):
1274
1279
  def setup_nodes(self, source=None, target=None):
1275
1280
  super().setup_nodes(source=source, target=target)
1276
1281
  if len(self.source) != len(self.target):
1277
- raise ValueError("Source and target must be the same for "
1278
- "gap junction.")
1282
+ src_str, trg_str = self.get_nodes_info()
1283
+ raise ValueError(f"Source and target must be the same for "
1284
+ f"gap junction. Nodes are {src_str} and {trg_str}")
1279
1285
  self.n_source = len(self.source)
1280
1286
 
1281
1287
  def make_connection(self, source, target, *args, **kwargs):
@@ -1500,8 +1506,9 @@ class OneToOneSequentialConnector(AbstractConnector):
1500
1506
  source, target = target, source
1501
1507
  if self.target_count == 0:
1502
1508
  if source is None or len(source) == 0:
1503
- raise ValueError(("Target" if self.partition_source else
1504
- "Source") + " nodes do not exists")
1509
+ src_str, trg_str = self.get_nodes_info()
1510
+ raise ValueError((f"{trg_str}" if self.partition_source else
1511
+ f"{src_str}") + " nodes do not exists")
1505
1512
  self.source = source
1506
1513
  self.n_source = len(source)
1507
1514
  if target is None or len(target) == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bmtool
3
- Version: 0.5.8
3
+ Version: 0.5.9.5
4
4
  Summary: BMTool
5
5
  Home-page: https://github.com/cyneuro/bmtool
6
6
  Download-URL:
@@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
6
6
 
7
7
  setup(
8
8
  name="bmtool",
9
- version='0.5.8',
9
+ version='0.5.9.5',
10
10
  author="Neural Engineering Laboratory at the University of Missouri",
11
11
  author_email="gregglickert@mail.missouri.edu",
12
12
  description="BMTool",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes