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/BVH.py +1 -1
- topologicpy/Cell.py +2 -2
- topologicpy/CellComplex.py +2 -2
- topologicpy/Edge.py +23 -23
- topologicpy/Face.py +61 -6
- topologicpy/Graph.py +221 -222
- topologicpy/Helper.py +55 -0
- topologicpy/Matrix.py +211 -5
- topologicpy/Plotly.py +8 -9
- topologicpy/Polyskel.py +3 -3
- topologicpy/Shell.py +4 -4
- topologicpy/Topology.py +350 -406
- topologicpy/Vector.py +84 -35
- topologicpy/Vertex.py +63 -6
- topologicpy/Wire.py +26 -145
- topologicpy/version.py +1 -1
- {topologicpy-0.8.9.dist-info → topologicpy-0.8.11.dist-info}/METADATA +1 -1
- topologicpy-0.8.11.dist-info/RECORD +36 -0
- topologicpy-0.8.9.dist-info/RECORD +0 -36
- {topologicpy-0.8.9.dist-info → topologicpy-0.8.11.dist-info}/LICENSE +0 -0
- {topologicpy-0.8.9.dist-info → topologicpy-0.8.11.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.9.dist-info → topologicpy-0.8.11.dist-info}/top_level.txt +0 -0
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)
|
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",
|
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
|
-
|
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
|
-
|
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
|
1462
|
-
if
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
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
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
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,
|
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
|
-
|
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
|
-
|
4784
|
-
|
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
|
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
|
-
|
4802
|
-
|
4803
|
-
|
4804
|
-
|
4805
|
-
|
4806
|
-
|
4807
|
-
|
4808
|
-
|
4809
|
-
|
4810
|
-
|
4811
|
-
|
4812
|
-
|
4813
|
-
|
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, "
|
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
|
-
|
4845
|
-
|
4846
|
-
|
4847
|
-
|
4848
|
-
|
4849
|
-
|
4850
|
-
|
4851
|
-
|
4852
|
-
|
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
|
-
|
4855
|
-
|
4856
|
-
|
4857
|
-
|
4858
|
-
|
4859
|
-
|
4860
|
-
|
4861
|
-
|
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)
|
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)
|
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)
|
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)
|
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])
|
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)
|
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)
|
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
|
"""
|