topologicpy 0.8.9__py3-none-any.whl → 0.8.11__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.
topologicpy/Graph.py CHANGED
@@ -299,7 +299,7 @@ class Graph:
299
299
  unique = True
300
300
  returnVertex = vertex
301
301
  for gv in graph_vertices:
302
- if (Vertex.Distance(vertex, gv) < tolerance):
302
+ if (Vertex.Distance(vertex, gv) <= tolerance):
303
303
  if transferVertexDictionaries == True:
304
304
  gd = Topology.Dictionary(gv)
305
305
  vd = Topology.Dictionary(vertex)
@@ -1352,7 +1352,7 @@ class Graph:
1352
1352
  return bot_graph.serialize(format=format)
1353
1353
 
1354
1354
  @staticmethod
1355
- def BetweennessCentrality(graph, key: str = "betweenness_centrality", method="vertex", mantissa: int = 6, tolerance: float = 0.001, silent: bool = False):
1355
+ def BetweennessCentrality(graph, method: str = "vertex", weightKey="length", normalize: bool = False, nx: bool = False, key: str = "betweenness_centrality", colorKey="bc_color", colorScale="viridis", mantissa: int = 6, tolerance: float = 0.001, silent: bool = False):
1356
1356
  """
1357
1357
  Returns the betweenness centrality of the input graph. The order of the returned list is the same as the order of vertices/edges. See https://en.wikipedia.org/wiki/Betweenness_centrality.
1358
1358
 
@@ -1360,10 +1360,24 @@ class Graph:
1360
1360
  ----------
1361
1361
  graph : topologic_core.Graph
1362
1362
  The input graph.
1363
- key : str , optional
1364
- The dictionary key under which to store the betweeness centrality score. The default is "betweenness_centrality".
1365
1363
  method : str , optional
1366
1364
  The method of computing the betweenness centrality. The options are "vertex" or "edge". The default is "vertex".
1365
+ weightKey : str , optional
1366
+ If None, all edge weights are considered equal. Otherwise holds the name of the edge dictionary key to be used as weight.
1367
+ Weights are used to calculate weighted shortest paths, so they are interpreted as distances.
1368
+ If you wish to use the actual length of the edge, then use the "length" as key. the default is "length".
1369
+ normalize : bool , optional
1370
+ If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
1371
+ nx : bool , optional
1372
+ If set to True, and normalize input parameter is also set to True, the values are set to be identical to NetworkX values. Otherwise, they are normalized between 0 and 1. The default is False.
1373
+ key : str , optional
1374
+ The desired dictionary key under which to store the betweeness centrality score. The default is "betweenness_centrality".
1375
+ colorKey : str , optional
1376
+ The desired dictionary key under which to store the betweeness centrality color. The default is "betweenness_centrality".
1377
+ colorScale : str , optional
1378
+ The desired type of plotly color scales to use (e.g. "viridis", "plasma"). The default is "viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
1379
+ In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
1380
+
1367
1381
  mantissa : int , optional
1368
1382
  The desired length of the mantissa. The default is 6.
1369
1383
  tolerance : float , optional
@@ -1375,163 +1389,55 @@ class Graph:
1375
1389
  The betweenness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
1376
1390
 
1377
1391
  """
1378
- def bfs_paths(source):
1379
- queue = [(source, [source])]
1380
- while queue:
1381
- (vertex, path) = queue.pop(0)
1382
- for next in set(py_graph[vertex]) - set(path):
1383
- queue.append((next, path + [next]))
1384
- yield path + [next]
1385
-
1386
- def shortest_paths_count(source):
1387
- paths = list(bfs_paths(source))
1388
- shortest_paths = {v: [] for v in py_graph}
1389
- for path in paths:
1390
- shortest_paths[path[-1]].append(path)
1391
- return shortest_paths
1392
-
1393
- def calculate_vertex_betweenness():
1394
- betweenness = {v: 0.0 for v in py_graph}
1395
- for s in py_graph:
1396
- shortest_paths = shortest_paths_count(s)
1397
- dependency = {v: 0.0 for v in py_graph}
1398
- for t in py_graph:
1399
- if t != s:
1400
- for path in shortest_paths[t]:
1401
- for v in path[1:-1]:
1402
- dependency[v] += 1.0 / len(shortest_paths[t])
1403
- for v in py_graph:
1404
- if v != s:
1405
- betweenness[v] += dependency[v]
1406
- return betweenness
1407
-
1408
- def calculate_edge_betweenness(graph_adj_matrix):
1409
- n = len(graph_adj_matrix)
1410
- edge_betweenness_scores = {}
1411
-
1412
- # Iterate over all node pairs as source and target nodes
1413
- for source in range(n):
1414
- # Initialize the 'distance' and 'predecessors' for each node
1415
- distance = [-1] * n
1416
- predecessors = [[] for _ in range(n)]
1417
- distance[source] = 0
1418
- stack = []
1419
- queue = [source]
1420
-
1421
- # Breadth-first search to find shortest paths
1422
- while queue:
1423
- current_node = queue.pop(0)
1424
- stack.append(current_node)
1425
- for neighbor in range(n):
1426
- if graph_adj_matrix[current_node][neighbor] == 1:
1427
- if distance[neighbor] == -1: # First time visiting neighbor
1428
- distance[neighbor] = distance[current_node] + 1
1429
- queue.append(neighbor)
1430
- if distance[neighbor] == distance[current_node] + 1: # Shortest path
1431
- predecessors[neighbor].append(current_node)
1432
-
1433
- # Initialize the dependency values for each node
1434
- dependency = [0] * n
1435
-
1436
- # Process the nodes in reverse order of discovery
1437
- while stack:
1438
- current_node = stack.pop()
1439
- for pred in predecessors[current_node]:
1440
- dependency[pred] += (1 + dependency[current_node]) / len(predecessors[current_node])
1441
-
1442
- # Update edge betweenness scores
1443
- if pred < current_node:
1444
- edge = (pred, current_node)
1445
- else:
1446
- edge = (current_node, pred)
1447
-
1448
- if edge not in edge_betweenness_scores:
1449
- edge_betweenness_scores[edge] = 0
1450
- edge_betweenness_scores[edge] += dependency[current_node]
1451
-
1452
- # Normalize edge betweenness scores by dividing by 2 (since each edge is counted twice)
1453
- for edge in edge_betweenness_scores:
1454
- edge_betweenness_scores[edge] /= 2
1392
+ import warnings
1455
1393
 
1456
- return edge_betweenness_scores
1394
+ try:
1395
+ import networkx as nx
1396
+ except:
1397
+ print("Graph.BetwennessCentrality - Information: Installing required networkx library.")
1398
+ try:
1399
+ os.system("pip install networkx")
1400
+ except:
1401
+ os.system("pip install networkx --user")
1402
+ try:
1403
+ import networkx as nx
1404
+ print("Graph.BetwennessCentrality - Infromation: networkx library installed correctly.")
1405
+ except:
1406
+ warnings.warn("Graph.BetwennessCentrality - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
1407
+ return None
1457
1408
 
1458
- from topologicpy.Topology import Topology
1459
1409
  from topologicpy.Dictionary import Dictionary
1410
+ from topologicpy.Color import Color
1411
+ from topologicpy.Topology import Topology
1412
+ from topologicpy.Helper import Helper
1460
1413
 
1461
- if not Topology.IsInstance(graph, "Graph"):
1462
- if not silent:
1463
- print("Graph.BetweenessCentrality - Error: The input graph is not a valid graph. Returning None.")
1464
- return None
1465
-
1466
- if "v" in method.lower():
1467
- vertices = Graph.Vertices(graph)
1468
-
1469
- if len(vertices) < 1:
1470
- if not silent:
1471
- print("Graph.BetweenessCentrality - Error: The input graph does not contain valid vertices. Returning None.")
1472
- return None
1473
- if len(vertices) == 1:
1474
- d = Topology.Dictionary(vertices[0])
1475
- d = Dictionary.SetValueAtKey(d, key, 1.0)
1476
- vertices[0] = Topology.SetDictionary(vertices[0], d)
1477
- return [1.0]
1478
-
1479
- py_graph = Graph.AdjacencyDictionary(graph)
1480
- vertex_betweenness = calculate_vertex_betweenness()
1481
- for v in vertex_betweenness:
1482
- vertex_betweenness[v] /= 2.0 # Each shortest path is counted twice
1483
-
1484
- min_betweenness = min(vertex_betweenness.values())
1485
- max_betweenness = max(vertex_betweenness.values())
1486
- if (max_betweenness - min_betweenness) > 0:
1487
- for v in vertex_betweenness:
1488
- vertex_betweenness[v] = (vertex_betweenness[v] - min_betweenness)/ (max_betweenness - min_betweenness) # Normalize to [0, 1]
1489
-
1490
-
1491
- vertex_betweenness_scores = [0]*len(vertices)
1492
- for i, score in vertex_betweenness.items():
1493
- vertex = vertices[int(i)]
1494
- d = Topology.Dictionary(vertex)
1495
- d = Dictionary.SetValueAtKey(d, key, round(score, mantissa))
1496
- vertex = Topology.SetDictionary(vertex, d)
1497
- vertex_betweenness_scores[int(i)] = round(score, mantissa)
1498
-
1499
- return vertex_betweenness_scores
1414
+ if weightKey:
1415
+ if "len" in weightKey.lower():
1416
+ weightKey = "length"
1417
+ nx_graph = Graph.NetworkXGraph(graph)
1418
+ if "vert" in method.lower():
1419
+ elements = Graph.Vertices(graph)
1420
+ elements_dict = nx.betweenness_centrality(nx_graph, normalized=normalize, weight=weightKey)
1421
+ values = list(elements_dict.values())
1422
+ else:
1423
+ elements = Graph.Edges(graph)
1424
+ elements_dict = nx.edge_betweenness_centrality(nx_graph, normalized=normalize, weight=weightKey)
1425
+ values = list(elements_dict.values())
1426
+ if nx == False:
1427
+ values = Helper.Normalize(values)
1428
+ min_value = 0
1429
+ max_value = 1
1500
1430
  else:
1501
- graph_edges = Graph.Edges(graph)
1502
- adj_matrix = Graph.AdjacencyMatrix(graph)
1503
- meshData = Graph.MeshData(graph)
1504
- edges = meshData["edges"]
1505
- if len(graph_edges) < 1:
1506
- if not silent:
1507
- print("Graph.BetweenessCentrality - Error: The input graph does not contain any edges. Returning None.")
1508
- return None
1509
- if len(graph_edges) == 1:
1510
- d = Topology.Dictionary(graph_edges[0])
1511
- d = Dictionary.SetValueAtKey(d, key, 1.0)
1512
- graph_edges[0] = Topology.SetDictionary(graph_edges[0], d)
1513
- return [1.0]
1514
-
1515
- edge_betweenness = calculate_edge_betweenness(adj_matrix)
1516
- keys = list(edge_betweenness.keys())
1517
- values = list(edge_betweenness.values())
1518
1431
  min_value = min(values)
1519
1432
  max_value = max(values)
1520
- edge_betweenness_scores = []
1521
- for i, edge in enumerate(edges):
1522
- u,v = edge
1523
- if (u,v) in keys:
1524
- score = edge_betweenness[(u,v)]
1525
- elif (v,u) in keys:
1526
- score = edge_betweenness[(v,u)]
1527
- else:
1528
- score = 0
1529
- score = (score - min_value)/(max_value - min_value)
1530
- edge_betweenness_scores.append(round(score, mantissa))
1531
- d = Topology.Dictionary(graph_edges[i])
1532
- d = Dictionary.SetValueAtKey(d, key, round(score, mantissa))
1533
- graph_edges[i] = Topology.SetDictionary(graph_edges[i], d)
1534
- return edge_betweenness_scores
1433
+
1434
+ for i, value in enumerate(values):
1435
+ d = Topology.Dictionary(elements[i])
1436
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
1437
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
1438
+ elements[i] = Topology.SetDictionary(elements[i], d)
1439
+
1440
+ return values
1535
1441
 
1536
1442
  @staticmethod
1537
1443
  def BetweennessPartition(graph, n=2, m=10, key="partition", tolerance=0.0001, silent=False):
@@ -4771,94 +4677,92 @@ class Graph:
4771
4677
  graph = Graph.RemoveVertex(graph,ev, tolerance=tolerance)
4772
4678
  return graph
4773
4679
 
4680
+
4774
4681
  @staticmethod
4775
- def ClosenessCentrality(graph, vertices=None, key: str = "closeness_centrality", mantissa: int = 6, tolerance = 0.0001, silent = False):
4682
+ def ClosenessCentrality(graph, weightKey="length", normalize: bool = False, nx: bool = True, key: str = "closeness_centrality", colorKey="cc_color", colorScale="viridis", mantissa: int = 6, tolerance: float = 0.001, silent: bool = False):
4776
4683
  """
4777
- Return the closeness centrality measure of the input list of vertices within the input graph. The order of the returned list is the same as the order of the input list of vertices. If no vertices are specified, the closeness centrality of all the vertices in the input graph is computed. See https://en.wikipedia.org/wiki/Closeness_centrality.
4684
+ Returns the closeness centrality of the input graph. The order of the returned list is the same as the order of vertices/edges. See https://en.wikipedia.org/wiki/Betweenness_centrality.
4778
4685
 
4779
4686
  Parameters
4780
4687
  ----------
4781
4688
  graph : topologic_core.Graph
4782
4689
  The input graph.
4783
- vertices : list , optional
4784
- The input list of vertices. The default is None.
4690
+ weightKey : str , optional
4691
+ If None, all edge weights are considered equal. Otherwise holds the name of the edge dictionary key to be used as weight.
4692
+ Weights are used to calculate weighted shortest paths, so they are interpreted as distances.
4693
+ If you wish to use the actual length of the edge, then use the "length" as key. the default is "length".
4694
+ normalize : bool , optional
4695
+ If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
4696
+ nx : bool , optional
4697
+ If set to True, use networkX to scale by the fraction of nodes reachable. This gives the Wasserman and Faust improved formula.
4698
+ For single component graphs it is the same as the original formula.
4785
4699
  key : str , optional
4786
- The dictionary key under which to store the closeness centrality score. The default is "closeness_centrality".
4700
+ The desired dictionary key under which to store the closeness centrality score. The default is "closeness_centrality".
4701
+ colorKey : str , optional
4702
+ The desired dictionary key under which to store the closeness centrality color. The default is "cc_color".
4703
+ colorScale : str , optional
4704
+ The desired type of plotly color scales to use (e.g. "viridis", "plasma"). The default is "viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
4705
+ In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
4787
4706
  mantissa : int , optional
4788
4707
  The desired length of the mantissa. The default is 6.
4789
4708
  tolerance : float , optional
4790
4709
  The desired tolerance. The default is 0.0001.
4791
- silent : bool , optional
4792
- If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
4793
4710
 
4794
4711
  Returns
4795
4712
  -------
4796
4713
  list
4797
- The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
4714
+ The betweenness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
4798
4715
 
4799
4716
  """
4717
+ import warnings
4800
4718
 
4801
- def closeness_centrality(g):
4802
- """
4803
- Computes the closeness centrality for each vertex in the graph.
4804
-
4805
- Parameters:
4806
- graph (dict): A dictionary representing the graph where keys are vertices and
4807
- values are lists of neighboring vertices.
4808
-
4809
- Returns:
4810
- dict: A dictionary where keys are vertices and values are their closeness centrality.
4811
- """
4812
- keys = list(g.keys())
4813
- N = len(keys)
4814
-
4815
- centralities = []
4816
- for v in keys:
4817
- total_distance = 0
4818
- reachable_count = 0
4819
-
4820
- for u in keys:
4821
- if v != u:
4822
- distance = Graph._topological_distance(g, v, u)
4823
- if distance != None:
4824
- total_distance += distance
4825
- reachable_count += 1
4826
-
4827
- if reachable_count > 0: # Avoid division by zero
4828
- centrality = (reachable_count / total_distance)
4829
- else:
4830
- centrality = 0.0 # Isolated vertex
4831
-
4832
- centralities.append(centrality)
4833
- return centralities
4719
+ try:
4720
+ import networkx as nx
4721
+ except:
4722
+ print("Graph.BetwennessCentrality - Information: Installing required networkx library.")
4723
+ try:
4724
+ os.system("pip install networkx")
4725
+ except:
4726
+ os.system("pip install networkx --user")
4727
+ try:
4728
+ import networkx as nx
4729
+ print("Graph.ClosenessCentrality - Infromation: networkx library installed correctly.")
4730
+ except:
4731
+ warnings.warn("Graph.ClosenessCentrality - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
4732
+ return None
4834
4733
 
4835
- from topologicpy.Vertex import Vertex
4836
- from topologicpy.Topology import Topology
4837
4734
  from topologicpy.Dictionary import Dictionary
4735
+ from topologicpy.Color import Color
4736
+ from topologicpy.Topology import Topology
4838
4737
  from topologicpy.Helper import Helper
4839
4738
 
4840
- if not Topology.IsInstance(graph, "Graph"):
4739
+ if not Topology.IsInstance(graph, "graph"):
4841
4740
  if not silent:
4842
4741
  print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.")
4843
4742
  return None
4844
- g = Graph.AdjacencyDictionary(graph)
4845
- centralities = closeness_centrality(g)
4846
- graphVertices = Graph.Vertices(graph)
4847
- if vertices == None:
4848
- for i, v in enumerate(graphVertices):
4849
- d = Topology.Dictionary(v)
4850
- d = Dictionary.SetValueAtKey(d, key, centralities[i])
4851
- v = Topology.SetDictionary(v, d)
4852
- return centralities
4743
+
4744
+ if weightKey:
4745
+ if "len" in weightKey.lower():
4746
+ weightKey = "length"
4747
+ nx_graph = Graph.NetworkXGraph(graph)
4748
+ elements = Graph.Vertices(graph)
4749
+ elements_dict = nx.closeness_centrality(nx_graph, distance=weightKey, wf_improved=nx)
4750
+ values = list(elements_dict.values())
4751
+ if normalize == True:
4752
+ values = Helper.Normalize(values)
4753
+ min_value = 0
4754
+ max_value = 1
4853
4755
  else:
4854
- return_centralities = []
4855
- for v in vertices:
4856
- i = Vertex.Index(v, graphVertices)
4857
- d = Topology.Dictionary(v)
4858
- d = Dictionary.SetValueAtKey(d, key, centralities[i])
4859
- v = Topology.SetDictionary(v, d)
4860
- return_centralities.append(centralities[i])
4861
- return centralities
4756
+ min_value = min(values)
4757
+ max_value = max(values)
4758
+
4759
+ for i, value in enumerate(values):
4760
+ d = Topology.Dictionary(elements[i])
4761
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
4762
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
4763
+ elements[i] = Topology.SetDictionary(elements[i], d)
4764
+
4765
+ return values
4862
4766
 
4863
4767
  @staticmethod
4864
4768
  def Community(graph, key: str = "partition", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
@@ -7943,7 +7847,7 @@ class Graph:
7943
7847
  incoming_edges = []
7944
7848
  for edge in edges:
7945
7849
  ev = Edge.EndVertex(edge)
7946
- if Vertex.Distance(vertex, ev) < tolerance:
7850
+ if Vertex.Distance(vertex, ev) <= tolerance:
7947
7851
  incoming_edges.append(edge)
7948
7852
  return incoming_edges
7949
7853
 
@@ -8751,7 +8655,7 @@ class Graph:
8751
8655
  longest_path = Topology.SelfMerge(Cluster.ByTopologies(new_edges), tolerance=tolerance)
8752
8656
 
8753
8657
  sv = Topology.Vertices(longest_path)[0]
8754
- if Vertex.Distance(sv, vertexB) < tolerance: # Wire is reversed. Re-reverse it
8658
+ if Vertex.Distance(sv, vertexB) <= tolerance: # Wire is reversed. Re-reverse it
8755
8659
  if Topology.IsInstance(longest_path, "Edge"):
8756
8660
  longest_path = Edge.Reverse(longest_path)
8757
8661
  elif Topology.IsInstance(longest_path, "Wire"):
@@ -9048,7 +8952,7 @@ class Graph:
9048
8952
 
9049
8953
  def vertexInList(vertex, vertexList, tolerance=0.0001):
9050
8954
  for v in vertexList:
9051
- if Vertex.Distance(v, vertex) < tolerance:
8955
+ if Vertex.Distance(v, vertex) <= tolerance:
9052
8956
  return True
9053
8957
  return False
9054
8958
 
@@ -9264,6 +9168,101 @@ class Graph:
9264
9168
  from topologicpy.Vertex import Vertex
9265
9169
  from topologicpy.Topology import Topology
9266
9170
  from topologicpy.Dictionary import Dictionary
9171
+ import warnings
9172
+
9173
+ try:
9174
+ import networkx as nx
9175
+ except:
9176
+ print("Graph.NetworkXGraph - Information: Installing required networkx library.")
9177
+ try:
9178
+ os.system("pip install networkx")
9179
+ except:
9180
+ os.system("pip install networkx --user")
9181
+ try:
9182
+ import networkx as nx
9183
+ print("Graph.NetworkXGraph - Information: networkx library installed correctly.")
9184
+ except:
9185
+ warnings.warn("Graph - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
9186
+ return None
9187
+
9188
+ if not Topology.IsInstance(graph, "Graph"):
9189
+ if not silent:
9190
+ print("Graph.NetworkXGraph - Error: The input graph is not a valid graph. Returning None.")
9191
+ return None
9192
+
9193
+ nxGraph = nx.Graph()
9194
+ vertices = Graph.Vertices(graph)
9195
+ edges = Graph.Edges(graph)
9196
+ mesh_data = Graph.MeshData(graph)
9197
+ order = len(vertices)
9198
+ nodes = []
9199
+ for i in range(order):
9200
+ v = vertices[i]
9201
+ d = Topology.Dictionary(vertices[i])
9202
+ if d:
9203
+ keys = Dictionary.Keys(d)
9204
+ if not keys:
9205
+ keys = []
9206
+ values = Dictionary.Values(d)
9207
+ if not values:
9208
+ values = []
9209
+ keys += [xKey, yKey, zKey]
9210
+ values += [Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa), Vertex.Z(v, mantissa=mantissa)]
9211
+ d = Dictionary.ByKeysValues(keys, values)
9212
+ pythonD = Dictionary.PythonDictionary(d)
9213
+ nodes.append((i, pythonD))
9214
+ else:
9215
+ nodes.append((i, {"name": str(i)}))
9216
+ nxGraph.add_nodes_from(nodes)
9217
+
9218
+ mesh_edges = mesh_data['edges']
9219
+ for i, mesh_edge in enumerate(mesh_edges):
9220
+ sv_i = mesh_edge[0]
9221
+ ev_i = mesh_edge[1]
9222
+ sv = vertices[sv_i]
9223
+ ev = vertices[ev_i]
9224
+ edge_length = Vertex.Distance(sv, ev, mantissa=mantissa)
9225
+ edge_dict = Topology.Dictionary(edges[i])
9226
+ edge_attributes = Dictionary.PythonDictionary(edge_dict) if edge_dict else {}
9227
+ edge_attributes['length'] = edge_length
9228
+ nxGraph.add_edge(sv_i, ev_i, **edge_attributes)
9229
+
9230
+ pos = nx.spring_layout(nxGraph, k=0.2)
9231
+ nx.set_node_attributes(nxGraph, pos, "pos")
9232
+ return nxGraph
9233
+
9234
+ @staticmethod
9235
+ def NetworkXGraph_old(graph, xKey='x', yKey='y', zKey='z', mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
9236
+ """
9237
+ Converts the input graph into a NetworkX Graph. See http://networkx.org
9238
+
9239
+ Parameters
9240
+ ----------
9241
+ graph : topologic_core.Graph
9242
+ The input graph.
9243
+ xKey : str , optional
9244
+ The dictionary key under which to store the X-Coordinate of the vertex. The default is 'x'.
9245
+ yKey : str , optional
9246
+ The dictionary key under which to store the Y-Coordinate of the vertex. The default is 'y'.
9247
+ zKey : str , optional
9248
+ The dictionary key under which to store the Z-Coordinate of the vertex. The default is 'z'.
9249
+ mantissa : int , optional
9250
+ The desired length of the mantissa. The default is 6.
9251
+ tolerance : float , optional
9252
+ The desired tolerance. The default is 0.0001.
9253
+ silent : bool , optional
9254
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
9255
+
9256
+ Returns
9257
+ -------
9258
+ networkX Graph
9259
+ The created networkX Graph
9260
+
9261
+ """
9262
+ from topologicpy.Vertex import Vertex
9263
+ from topologicpy.Topology import Topology
9264
+ from topologicpy.Dictionary import Dictionary
9265
+ import warnings
9267
9266
 
9268
9267
  try:
9269
9268
  import networkx as nx
@@ -9382,7 +9381,7 @@ class Graph:
9382
9381
  outgoing_edges = []
9383
9382
  for edge in edges:
9384
9383
  sv = Edge.StartVertex(edge)
9385
- if Vertex.Distance(vertex, sv) < tolerance:
9384
+ if Vertex.Distance(vertex, sv) <= tolerance:
9386
9385
  outgoing_edges.append(edge)
9387
9386
  return outgoing_edges
9388
9387
 
@@ -9481,7 +9480,7 @@ class Graph:
9481
9480
  new_scores[i] = alpha * incoming_score + (1 - alpha) / num_vertices
9482
9481
 
9483
9482
  # Check for convergence
9484
- if all(abs(new_scores[i] - scores[i]) < tolerance for i in range(len(vertices))):
9483
+ if all(abs(new_scores[i] - scores[i]) <= tolerance for i in range(len(vertices))):
9485
9484
  break
9486
9485
 
9487
9486
  scores = new_scores
@@ -9938,7 +9937,7 @@ class Graph:
9938
9937
  if Topology.IsInstance(shortest_path, "Edge"):
9939
9938
  shortest_path = Wire.ByEdges([shortest_path])
9940
9939
  sv = Topology.Vertices(shortest_path)[0]
9941
- if Vertex.Distance(sv, gev) < tolerance: # Path is reversed. Correct it.
9940
+ if Vertex.Distance(sv, gev) <= tolerance: # Path is reversed. Correct it.
9942
9941
  if Topology.IsInstance(shortest_path, "Wire"):
9943
9942
  shortest_path = Wire.Reverse(shortest_path)
9944
9943
  shortest_path = Wire.OrientEdges(shortest_path, Wire.StartVertex(shortest_path), tolerance=tolerance)
@@ -10491,7 +10490,7 @@ class Graph:
10491
10490
  if parent:
10492
10491
  edge = Graph.Edge(graph, parent, vertex, tolerance=tolerance)
10493
10492
  ev = Edge.EndVertex(edge)
10494
- if Vertex.Distance(parent, ev) < tolerance:
10493
+ if Vertex.Distance(parent, ev) <= tolerance:
10495
10494
  edge = Edge.Reverse(edge)
10496
10495
  edges.append(edge)
10497
10496
  if parent == None:
topologicpy/Helper.py CHANGED
@@ -35,6 +35,61 @@ except:
35
35
  warnings.warn("Helper - Error: Could not import numpy.")
36
36
 
37
37
  class Helper:
38
+ @staticmethod
39
+ def BinAndAverage(listA, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
40
+ """
41
+ Groups numbers within a specified tolerance into bins, calculates the average
42
+ of each bin, and returns a sorted list of these averages.
43
+
44
+ Parameters
45
+ ----------
46
+ listA : list
47
+ The input list.
48
+ mantissa : int , optional
49
+ The desired length of the mantissa. The default is 6
50
+ tolerance : float , optional
51
+ The desired tolerance. The default is 0.0001.
52
+ silent : bool , optional
53
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
54
+
55
+ Returns
56
+ -------
57
+ list
58
+ The sorted list of bin averages.
59
+
60
+ """
61
+ if not isinstance(listA, list):
62
+ if not silent:
63
+ print("Helper.BinAndAverage = Error: The input listA parameter is not a valid list. Returning None.")
64
+ return None
65
+
66
+ if len(listA) < 1:
67
+ if not silent:
68
+ print("Helper.BinAndAverage = Error: The input listA parameter is not an empty list. Returning None.")
69
+ return None
70
+
71
+ if len(listA) == 1:
72
+ return listA
73
+
74
+ # Sort numbers to facilitate grouping
75
+ listA = sorted(listA)
76
+ bins = []
77
+ current_bin = [listA[0]]
78
+
79
+ # Group numbers into bins based on tolerance
80
+ for num in listA[1:]:
81
+ if abs(num - current_bin[-1]) <= tolerance:
82
+ current_bin.append(num)
83
+ else:
84
+ bins.append(current_bin)
85
+ current_bin = [num]
86
+ bins.append(current_bin) # Add the last bin
87
+
88
+ # Calculate averages of each bin
89
+ bin_averages = [round(sum(bin) / len(bin), mantissa) for bin in bins]
90
+
91
+ return sorted(bin_averages)
92
+
38
93
  @staticmethod
39
94
  def ClosestMatch(item, listA):
40
95
  """