topologicpy 0.8.10__py3-none-any.whl → 0.8.12__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
@@ -522,7 +522,7 @@ class Graph:
522
522
  return adjDict
523
523
 
524
524
  @staticmethod
525
- def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, tolerance=0.0001):
525
+ def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, mantissa: int = 6, tolerance=0.0001):
526
526
  """
527
527
  Returns the adjacency matrix of the input Graph. See https://en.wikipedia.org/wiki/Adjacency_matrix.
528
528
 
@@ -542,10 +542,12 @@ class Graph:
542
542
  If set to True or False, this key in the connecting edge will be used to determine is the edge is supposed to be bidirectional or not. If set to None, the input variable bidrectional will be used instead. The default is None
543
543
  bidirectional : bool , optional
544
544
  If set to True, the edges in the graph that do not have a bidireKey in their dictionaries will be treated as being bidirectional. Otherwise, the start vertex and end vertex of the connecting edge will determine the direction. The default is True.
545
- useEdgeIndex : bool , False
545
+ useEdgeIndex : bool , optional
546
546
  If set to True, the adjacency matrix values will the index of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
547
- useEdgeLength : bool , False
547
+ useEdgeLength : bool , optional
548
548
  If set to True, the adjacency matrix values will the length of the edge in Graph.Edges(graph). The default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
549
+ mantissa : int , optional
550
+ The desired length of the mantissa. The default is 6.
549
551
  tolerance : float , optional
550
552
  The desired tolerance. The default is 0.0001.
551
553
 
@@ -599,12 +601,16 @@ class Graph:
599
601
  bidir = bidirectional
600
602
  if edgeKeyFwd == None:
601
603
  valueFwd = 1
604
+ elif "len" in edgeKeyFwd.lower() or "dis" in edgeKeyFwd.lower():
605
+ valueFwd = Edge.Length(edge, mantissa=mantissa)
602
606
  else:
603
607
  valueFwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyFwd)
604
608
  if valueFwd == None:
605
609
  valueFwd = 1
606
610
  if edgeKeyBwd == None:
607
611
  valueBwd = 1
612
+ elif "len" in edgeKeyBwd.lower() or "dis" in edgeKeyBwd.lower():
613
+ valueBwd = Edge.Length(edge, mantissa=mantissa)
608
614
  else:
609
615
  valueBwd = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKeyBwd)
610
616
  if valueBwd == None:
@@ -613,8 +619,8 @@ class Graph:
613
619
  valueFwd = i+1
614
620
  valueBwd = i+1
615
621
  if useEdgeLength:
616
- valueFwd = Edge.Length(edge)
617
- valueBwd = Edge.Length(edge)
622
+ valueFwd = Edge.Length(edge, mantissa=mantissa)
623
+ valueBwd = Edge.Length(edge, mantissa=mantissa)
618
624
  if not svi == None and not evi == None:
619
625
  matrix[svi][evi] = valueFwd
620
626
  if bidir:
@@ -1352,7 +1358,7 @@ class Graph:
1352
1358
  return bot_graph.serialize(format=format)
1353
1359
 
1354
1360
  @staticmethod
1355
- def BetweennessCentrality(graph, key: str = "betweenness_centrality", method="vertex", mantissa: int = 6, tolerance: float = 0.001, silent: bool = False):
1361
+ 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
1362
  """
1357
1363
  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
1364
 
@@ -1360,10 +1366,23 @@ class Graph:
1360
1366
  ----------
1361
1367
  graph : topologic_core.Graph
1362
1368
  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
1369
  method : str , optional
1366
1370
  The method of computing the betweenness centrality. The options are "vertex" or "edge". The default is "vertex".
1371
+ weightKey : str , optional
1372
+ If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
1373
+ the shortest path. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
1374
+ This is used in weighted graphs. if weightKey is set to "Length" or "Distance", the length of the edge will be used as its weight.
1375
+ normalize : bool , optional
1376
+ If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
1377
+ nx : bool , optional
1378
+ 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.
1379
+ key : str , optional
1380
+ The desired dictionary key under which to store the betweenness centrality score. The default is "betweenness_centrality".
1381
+ colorKey : str , optional
1382
+ The desired dictionary key under which to store the betweenness centrality color. The default is "betweenness_centrality".
1383
+ colorScale : str , optional
1384
+ 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/.
1385
+ In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
1367
1386
  mantissa : int , optional
1368
1387
  The desired length of the mantissa. The default is 6.
1369
1388
  tolerance : float , optional
@@ -1375,163 +1394,58 @@ class Graph:
1375
1394
  The betweenness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
1376
1395
 
1377
1396
  """
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
1397
+ import warnings
1455
1398
 
1456
- return edge_betweenness_scores
1399
+ try:
1400
+ import networkx as nx
1401
+ except:
1402
+ print("Graph.BetwennessCentrality - Information: Installing required networkx library.")
1403
+ try:
1404
+ os.system("pip install networkx")
1405
+ except:
1406
+ os.system("pip install networkx --user")
1407
+ try:
1408
+ import networkx as nx
1409
+ print("Graph.BetwennessCentrality - Infromation: networkx library installed correctly.")
1410
+ except:
1411
+ warnings.warn("Graph.BetwennessCentrality - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
1412
+ return None
1457
1413
 
1458
- from topologicpy.Topology import Topology
1459
1414
  from topologicpy.Dictionary import Dictionary
1415
+ from topologicpy.Color import Color
1416
+ from topologicpy.Topology import Topology
1417
+ from topologicpy.Helper import Helper
1460
1418
 
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
1419
+ if weightKey:
1420
+ if "len" in weightKey.lower() or "dis" in wireightKey.lower():
1421
+ weightKey = "length"
1422
+ nx_graph = Graph.NetworkXGraph(graph)
1423
+ if "vert" in method.lower():
1424
+ elements = Graph.Vertices(graph)
1425
+ elements_dict = nx.betweenness_centrality(nx_graph, normalized=normalize, weight=weightKey)
1426
+ values = list(elements_dict.values())
1427
+ else:
1428
+ elements = Graph.Edges(graph)
1429
+ elements_dict = nx.edge_betweenness_centrality(nx_graph, normalized=normalize, weight=weightKey)
1430
+ values = [round(value, mantissa) for value in list(elements_dict.values())]
1431
+ if nx == False:
1432
+ if mantissa > 0: # We cannot have values in the range 0 to 1 with a mantissa < 1
1433
+ values = [round(v, mantissa) for v in Helper.Normalize(values)]
1434
+ else:
1435
+ values = Helper.Normalize(values)
1436
+ min_value = 0
1437
+ max_value = 1
1500
1438
  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
1439
  min_value = min(values)
1519
1440
  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
1441
+
1442
+ for i, value in enumerate(values):
1443
+ d = Topology.Dictionary(elements[i])
1444
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
1445
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
1446
+ elements[i] = Topology.SetDictionary(elements[i], d)
1447
+
1448
+ return values
1535
1449
 
1536
1450
  @staticmethod
1537
1451
  def BetweennessPartition(graph, n=2, m=10, key="partition", tolerance=0.0001, silent=False):
@@ -4772,93 +4686,93 @@ class Graph:
4772
4686
  return graph
4773
4687
 
4774
4688
  @staticmethod
4775
- def ClosenessCentrality(graph, vertices=None, key: str = "closeness_centrality", mantissa: int = 6, tolerance = 0.0001, silent = False):
4689
+ 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
4690
  """
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.
4691
+ 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
4692
 
4779
4693
  Parameters
4780
4694
  ----------
4781
4695
  graph : topologic_core.Graph
4782
4696
  The input graph.
4783
- vertices : list , optional
4784
- The input list of vertices. The default is None.
4697
+ weightKey : str , optional
4698
+ If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
4699
+ the shortest path. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
4700
+ This is used in weighted graphs. if weightKey is set to "Length" or "Distance", the length of the edge will be used as its weight.
4701
+ normalize : bool , optional
4702
+ If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
4703
+ nx : bool , optional
4704
+ If set to True, use networkX to scale by the fraction of nodes reachable. This gives the Wasserman and Faust improved formula.
4705
+ For single component graphs it is the same as the original formula.
4785
4706
  key : str , optional
4786
- The dictionary key under which to store the closeness centrality score. The default is "closeness_centrality".
4707
+ The desired dictionary key under which to store the closeness centrality score. The default is "closeness_centrality".
4708
+ colorKey : str , optional
4709
+ The desired dictionary key under which to store the closeness centrality color. The default is "cc_color".
4710
+ colorScale : str , optional
4711
+ 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/.
4712
+ 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
4713
  mantissa : int , optional
4788
4714
  The desired length of the mantissa. The default is 6.
4789
4715
  tolerance : float , optional
4790
4716
  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
4717
 
4794
4718
  Returns
4795
4719
  -------
4796
4720
  list
4797
- The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
4721
+ The betweenness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
4798
4722
 
4799
4723
  """
4724
+ import warnings
4800
4725
 
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
4726
+ try:
4727
+ import networkx as nx
4728
+ except:
4729
+ print("Graph.ClosenessCentrality - Information: Installing required networkx library.")
4730
+ try:
4731
+ os.system("pip install networkx")
4732
+ except:
4733
+ os.system("pip install networkx --user")
4734
+ try:
4735
+ import networkx as nx
4736
+ print("Graph.ClosenessCentrality - Infromation: networkx library installed correctly.")
4737
+ except:
4738
+ warnings.warn("Graph.ClosenessCentrality - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
4739
+ return None
4834
4740
 
4835
- from topologicpy.Vertex import Vertex
4836
- from topologicpy.Topology import Topology
4837
4741
  from topologicpy.Dictionary import Dictionary
4742
+ from topologicpy.Color import Color
4743
+ from topologicpy.Topology import Topology
4838
4744
  from topologicpy.Helper import Helper
4839
4745
 
4840
- if not Topology.IsInstance(graph, "Graph"):
4746
+ if not Topology.IsInstance(graph, "graph"):
4841
4747
  if not silent:
4842
4748
  print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.")
4843
4749
  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
4750
+
4751
+ if weightKey:
4752
+ if "len" in weightKey.lower() or "dis" in weightKey.lower():
4753
+ weightKey = "length"
4754
+ nx_graph = Graph.NetworkXGraph(graph)
4755
+ elements = Graph.Vertices(graph)
4756
+ elements_dict = nx.closeness_centrality(nx_graph, distance=weightKey, wf_improved=nx)
4757
+ values = [round(v, mantissa) for v in list(elements_dict.values())]
4758
+ if normalize == True:
4759
+ if mantissa > 0: # We cannot round numbers from 0 to 1 with a mantissa = 0.
4760
+ values = [round(v, mantissa) for v in Helper.Normalize(values)]
4761
+ else:
4762
+ values = Helper.Normalize(values)
4763
+ min_value = 0
4764
+ max_value = 1
4853
4765
  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
4766
+ min_value = min(values)
4767
+ max_value = max(values)
4768
+
4769
+ for i, value in enumerate(values):
4770
+ d = Topology.Dictionary(elements[i])
4771
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
4772
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
4773
+ elements[i] = Topology.SetDictionary(elements[i], d)
4774
+
4775
+ return values
4862
4776
 
4863
4777
  @staticmethod
4864
4778
  def Community(graph, key: str = "partition", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
@@ -5026,7 +4940,7 @@ class Graph:
5026
4940
  return graph
5027
4941
 
5028
4942
  @staticmethod
5029
- def Connectivity(graph, vertices=None, key: str = "connectivity", edgeKey: str = None, tolerance = 0.0001, silent = False):
4943
+ def Connectivity(graph, vertices=None, weightKey: str = None, normalize: bool = False, key: str = "connectivity", colorKey: str = "cn_color", colorScale="Viridis", mantissa: int = 6, tolerance = 0.0001, silent = False):
5030
4944
  """
5031
4945
  Return the connectivity 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 connectivity of all the vertices in the input graph is computed. See https://www.spacesyntax.online/term/connectivity/.
5032
4946
 
@@ -5035,15 +4949,22 @@ class Graph:
5035
4949
  graph : topologic_core.Graph
5036
4950
  The input graph.
5037
4951
  vertices : list , optional
5038
- The input list of vertices. The default is None.
4952
+ The input list of vertices. The default is None which means all graph vertices are computed.
4953
+ normalize : bool , optional
4954
+ If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
4955
+ weightKey : str , optional
4956
+ If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
4957
+ the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
4958
+ This is used in weighted graphs. if weightKey is set to "Length" or "Distance", the length of the edge will be used as its weight.
5039
4959
  key : str , optional
5040
4960
  The dictionary key under which to store the connectivity score. The default is "connectivity".
5041
- edgeKey : str , optional
5042
- If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate
5043
- the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs.
5044
- tolerance : float , optional
5045
- The desired tolerance. The default is 0.0001.
5046
-
4961
+ colorKey : str , optional
4962
+ The desired dictionary key under which to store the connectivity color. The default is "cn_color".
4963
+ colorScale : str , optional
4964
+ 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/.
4965
+ In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
4966
+ mantissa : int , optional
4967
+ The desired length of the mantissa. The default is 6.
5047
4968
  tolerance : float , optional
5048
4969
  The desired tolerance. The default is 0.0001.
5049
4970
  silent : bool , optional
@@ -5052,25 +4973,40 @@ class Graph:
5052
4973
  Returns
5053
4974
  -------
5054
4975
  list
5055
- The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
4976
+ The connectivity score of the input list of vertices within the input graph. The values are in the range 0 to 1 if normalized.
5056
4977
 
5057
4978
  """
5058
4979
 
5059
4980
  from topologicpy.Topology import Topology
5060
4981
  from topologicpy.Dictionary import Dictionary
4982
+ from topologicpy.Helper import Helper
4983
+ from topologicpy.Color import Color
5061
4984
 
5062
4985
  if not Topology.IsInstance(graph, "Graph"):
5063
4986
  if not silent:
5064
- print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.")
4987
+ print("Graph.Connectivity - Error: The input graph is not a valid graph. Returning None.")
5065
4988
  return None
5066
4989
  if vertices == None:
5067
4990
  vertices = Graph.Vertices(graph)
5068
- connectivities = [Graph.VertexDegree(graph, v, edgeKey=edgeKey, tolerance=tolerance, silent=silent) for v in vertices]
5069
- for i, v in enumerate(vertices):
5070
- d = Topology.Dictionary(v)
5071
- d = Dictionary.SetValueAtKey(d, key, connectivities[i])
5072
- v = Topology.SetDictionary(v, d)
5073
- return connectivities
4991
+ values = [Graph.VertexDegree(graph, v, weightKey=weightKey, mantissa=mantissa, tolerance=tolerance, silent=silent) for v in vertices]
4992
+ values = [round(v, mantissa) for v in values]
4993
+ if normalize == True:
4994
+ if mantissa > 0:
4995
+ values = [round(v, mantissa) for v in Helper.Normalize(values)]
4996
+ else:
4997
+ values = Helper.Normalize(values)
4998
+ min_value = 0
4999
+ max_value = 1
5000
+ else:
5001
+ min_value = min(values)
5002
+ max_value = max(values)
5003
+
5004
+ for i, value in enumerate(values):
5005
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
5006
+ d = Topology.Dictionary(vertices[i])
5007
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
5008
+ v = Topology.SetDictionary(vertices[i], d)
5009
+ return values
5074
5010
 
5075
5011
  @staticmethod
5076
5012
  def ContainsEdge(graph, edge, tolerance=0.0001):
@@ -5193,23 +5129,38 @@ class Graph:
5193
5129
  vertex = Topology.SetDictionary(vertex, d)
5194
5130
 
5195
5131
  return cut_vertices
5196
-
5132
+
5197
5133
  @staticmethod
5198
- def Degree(graph, vertices=None, key: str = "degree", edgeKey: str = None, mantissa: int = 6, tolerance = 0.0001):
5199
- """
5200
- Return the degree 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/Degree_(graph_theory).
5134
+ def DegreeCentrality(graph,
5135
+ vertices: list = None,
5136
+ weightKey: str= None,
5137
+ normalize: bool = False,
5138
+ key: str = "degree_centrality",
5139
+ colorKey="dc_color",
5140
+ colorScale="viridis",
5141
+ mantissa: int = 6,
5142
+ tolerance: float = 0.001,
5143
+ silent: bool = False):
5144
+ """
5145
+ Returns the degree centrality of the input graph. The order of the returned list is the same as the order of vertices. See https://en.wikipedia.org/wiki/Degree_centrality.
5201
5146
 
5202
5147
  Parameters
5203
5148
  ----------
5204
5149
  graph : topologic_core.Graph
5205
5150
  The input graph.
5206
- vertices : list , optional
5207
- The input list of vertices. The default is None.
5151
+ weightKey : str , optional
5152
+ If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
5153
+ the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
5154
+ This is used in weighted graphs. if weightKey is set to "Length" or "Distance", the length of the edge will be used as its weight.
5155
+ normalize : bool , optional
5156
+ If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
5208
5157
  key : str , optional
5209
- The dictionary key under which to store the closeness centrality score. The default is "degree".
5210
- edgeKey : str , optional
5211
- If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate
5212
- the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs.
5158
+ The desired dictionary key under which to store the degree centrality score. The default is "degree_centrality".
5159
+ colorKey : str , optional
5160
+ The desired dictionary key under which to store the degree centrality color. The default is "dc_color".
5161
+ colorScale : str , optional
5162
+ 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/.
5163
+ In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
5213
5164
  mantissa : int , optional
5214
5165
  The desired length of the mantissa. The default is 6.
5215
5166
  tolerance : float , optional
@@ -5218,35 +5169,30 @@ class Graph:
5218
5169
  Returns
5219
5170
  -------
5220
5171
  list
5221
- The degree of the input list of vertices within the input graph.
5172
+ The betweenness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
5222
5173
 
5223
5174
  """
5224
- from topologicpy.Topology import Topology
5175
+
5225
5176
  from topologicpy.Dictionary import Dictionary
5177
+ from topologicpy.Color import Color
5178
+ from topologicpy.Topology import Topology
5179
+ from topologicpy.Helper import Helper
5226
5180
 
5227
- if not Topology.IsInstance(graph, "Graph"):
5228
- print("Graph.ClosenessCentrality - Error: The input graph is not a valid graph. Returning None.")
5229
- return None
5230
- graphVertices = Graph.Vertices(graph)
5231
- if not isinstance(vertices, list):
5232
- vertices = graphVertices
5233
- else:
5234
- vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
5235
- if len(vertices) < 1:
5236
- print("Graph.Degree - Error: The input list of vertices does not contain any valid vertices. Returning None.")
5237
- return None
5238
- n = len(graphVertices)
5239
-
5240
- scores = []
5241
- for i, v in enumerate(vertices):
5242
- degree = Graph.VertexDegree(graph, v, edgeKey= edgeKey, tolerance = tolerance)
5243
- if isinstance(degree, float):
5244
- degree = round(degree, mantissa)
5245
- d = Topology.Dictionary(v)
5246
- d = Dictionary.SetValueAtKey(d, key, degree)
5247
- v = Topology.SetDictionary(v, d)
5248
- scores.append(degree)
5249
- return scores
5181
+ if not Topology.IsInstance(graph, "graph"):
5182
+ if not silent:
5183
+ print("Graph.DegreeCentrality - Error: The input graph is not a valid graph. Returning None.")
5184
+ return None
5185
+
5186
+ return Graph.Connectivity(graph,
5187
+ vertices=vertices,
5188
+ weightKey=weightKey,
5189
+ normalize=normalize,
5190
+ key=key,
5191
+ colorKey=colorKey,
5192
+ colorScale=colorScale,
5193
+ mantissa=mantissa,
5194
+ tolerance=tolerance,
5195
+ silent=silent)
5250
5196
 
5251
5197
  @staticmethod
5252
5198
  def DegreeMatrix(graph):
@@ -8468,7 +8414,7 @@ class Graph:
8468
8414
  return laplacian.tolist()
8469
8415
 
8470
8416
  @staticmethod
8471
- def Leaves(graph, edgeKey: str = None, tolerance: float = 0.0001, silent: bool = False):
8417
+ def Leaves(graph, weightKey: str = None, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
8472
8418
  """
8473
8419
  Returns a list of all vertices that have a degree of 1, also called leaf nodes.
8474
8420
 
@@ -8476,9 +8422,12 @@ class Graph:
8476
8422
  ----------
8477
8423
  graph : topologic_core.Graph
8478
8424
  The input graph.
8479
- edgeKey : str , optional
8480
- If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate
8481
- the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs.
8425
+ weightKey : str , optional
8426
+ If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
8427
+ the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
8428
+ This is used in weighted graphs. if weightKey is set to "Length" or "Distance", the length of the edge will be used as its weight.
8429
+ mantissa : int , optional
8430
+ The desired length of the mantissa. The default is 6.
8482
8431
  tolerance : float , optional
8483
8432
  The desired tolerance. The default is 0.0001.
8484
8433
  silent : bool , optional
@@ -8496,7 +8445,7 @@ class Graph:
8496
8445
  if not silent:
8497
8446
  print("Graph.Leaves - Error: The input graph parameter is not a valid graph. Returning None.")
8498
8447
  return None
8499
- return [v for v in Graph.Vertices(graph) if Graph.VertexDegree(graph, v, edgeKey=edgeKey, tolerance=tolerance, silent=silent) == 1]
8448
+ return [v for v in Graph.Vertices(graph) if Graph.VertexDegree(graph, v, weightKey=weightKey, mantissa=mantissa, tolerance=tolerance, silent=silent) == 1]
8500
8449
 
8501
8450
  @staticmethod
8502
8451
  def LineGraph(graph, transferVertexDictionaries=False, transferEdgeDictionaries=False, tolerance=0.0001, silent=False):
@@ -9264,6 +9213,7 @@ class Graph:
9264
9213
  from topologicpy.Vertex import Vertex
9265
9214
  from topologicpy.Topology import Topology
9266
9215
  from topologicpy.Dictionary import Dictionary
9216
+ import warnings
9267
9217
 
9268
9218
  try:
9269
9219
  import networkx as nx
@@ -9275,11 +9225,11 @@ class Graph:
9275
9225
  os.system("pip install networkx --user")
9276
9226
  try:
9277
9227
  import networkx as nx
9278
- print("Graph.NetworkXGraph - Infromation: networkx library installed correctly.")
9228
+ print("Graph.NetworkXGraph - Information: networkx library installed correctly.")
9279
9229
  except:
9280
9230
  warnings.warn("Graph - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
9281
9231
  return None
9282
-
9232
+
9283
9233
  if not Topology.IsInstance(graph, "Graph"):
9284
9234
  if not silent:
9285
9235
  print("Graph.NetworkXGraph - Error: The input graph is not a valid graph. Returning None.")
@@ -9287,6 +9237,8 @@ class Graph:
9287
9237
 
9288
9238
  nxGraph = nx.Graph()
9289
9239
  vertices = Graph.Vertices(graph)
9240
+ edges = Graph.Edges(graph)
9241
+ mesh_data = Graph.MeshData(graph)
9290
9242
  order = len(vertices)
9291
9243
  nodes = []
9292
9244
  for i in range(order):
@@ -9299,23 +9251,28 @@ class Graph:
9299
9251
  values = Dictionary.Values(d)
9300
9252
  if not values:
9301
9253
  values = []
9302
- keys += [xKey,yKey,zKey]
9254
+ keys += [xKey, yKey, zKey]
9303
9255
  values += [Vertex.X(v, mantissa=mantissa), Vertex.Y(v, mantissa=mantissa), Vertex.Z(v, mantissa=mantissa)]
9304
- d = Dictionary.ByKeysValues(keys,values)
9256
+ d = Dictionary.ByKeysValues(keys, values)
9305
9257
  pythonD = Dictionary.PythonDictionary(d)
9306
9258
  nodes.append((i, pythonD))
9307
9259
  else:
9308
9260
  nodes.append((i, {"name": str(i)}))
9309
9261
  nxGraph.add_nodes_from(nodes)
9310
- for i in range(order):
9311
- v = vertices[i]
9312
- adjVertices = Graph.AdjacentVertices(graph, vertices[i])
9313
- for adjVertex in adjVertices:
9314
- adjIndex = Vertex.Index(vertex=adjVertex, vertices=vertices, strict=True, tolerance=tolerance)
9315
- if not adjIndex == None:
9316
- nxGraph.add_edge(i,adjIndex, length=(Vertex.Distance(v, adjVertex, mantissa=mantissa)))
9317
9262
 
9318
- pos=nx.spring_layout(nxGraph, k=0.2)
9263
+ mesh_edges = mesh_data['edges']
9264
+ for i, mesh_edge in enumerate(mesh_edges):
9265
+ sv_i = mesh_edge[0]
9266
+ ev_i = mesh_edge[1]
9267
+ sv = vertices[sv_i]
9268
+ ev = vertices[ev_i]
9269
+ edge_length = Vertex.Distance(sv, ev, mantissa=mantissa)
9270
+ edge_dict = Topology.Dictionary(edges[i])
9271
+ edge_attributes = Dictionary.PythonDictionary(edge_dict) if edge_dict else {}
9272
+ edge_attributes['length'] = edge_length
9273
+ nxGraph.add_edge(sv_i, ev_i, **edge_attributes)
9274
+
9275
+ pos = nx.spring_layout(nxGraph, k=0.2)
9319
9276
  nx.set_node_attributes(nxGraph, pos, "pos")
9320
9277
  return nxGraph
9321
9278
 
@@ -10515,7 +10472,7 @@ class Graph:
10515
10472
  return Graph.ByVerticesEdges(dictionary['vertices'], dictionary['edges'])
10516
10473
 
10517
10474
  @staticmethod
10518
- def VertexDegree(graph, vertex, edgeKey: str = None, tolerance: float = 0.0001, silent: bool = False):
10475
+ def VertexDegree(graph, vertex, weightKey: str = None, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
10519
10476
  """
10520
10477
  Returns the degree of the input vertex. See https://en.wikipedia.org/wiki/Degree_(graph_theory).
10521
10478
 
@@ -10525,9 +10482,12 @@ class Graph:
10525
10482
  The input graph.
10526
10483
  vertex : topologic_core.Vertex
10527
10484
  The input vertex.
10528
- edgeKey : str , optional
10529
- If specified, the value in the connected edges' dictionary specified by the edgeKey string will be aggregated to calculate
10530
- the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead. This is used in weighted graphs.
10485
+ weightKey : str , optional
10486
+ If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
10487
+ the vertex degree. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
10488
+ This is used in weighted graphs. if weightKey is set to "Length" or "Distance", the length of the edge will be used as its weight.
10489
+ mantissa : int , optional
10490
+ The desired length of the mantissa. The default is 6.
10531
10491
  tolerance : float , optional
10532
10492
  The desired tolerance. The default is 0.0001.
10533
10493
  silent : bool , optional
@@ -10539,6 +10499,7 @@ class Graph:
10539
10499
  The degree of the input vertex.
10540
10500
 
10541
10501
  """
10502
+ from topologicpy.Edge import Edge
10542
10503
  from topologicpy.Topology import Topology
10543
10504
  from topologicpy.Dictionary import Dictionary
10544
10505
  import numbers
@@ -10551,18 +10512,20 @@ class Graph:
10551
10512
  if not silent:
10552
10513
  print("Graph.VertexDegree - Error: The input vertex is not a valid vertex. Returning None.")
10553
10514
  return None
10554
- if not isinstance(edgeKey, str):
10555
- edgeKey = ""
10556
10515
  edges = Graph.Edges(graph, [vertex], tolerance=tolerance)
10557
10516
  degree = 0
10558
10517
  for edge in edges:
10559
- d = Topology.Dictionary(edge)
10560
- value = Dictionary.ValueAtKey(d, edgeKey)
10561
- if isinstance(value, numbers.Number):
10562
- degree += value
10518
+ if weightKey == None:
10519
+ value = 1
10520
+ elif "len" in weightKey.lower() or "dis" in weightKey.lower():
10521
+ value = Edge.Length(edge, mantissa=mantissa)
10563
10522
  else:
10564
- degree += 1
10565
- return degree
10523
+ d = Topology.Dictionary(edge)
10524
+ value = Dictionary.ValueAtKey(d, weightKey)
10525
+ if not isinstance(value, numbers.Number):
10526
+ value = 1
10527
+ degree += value
10528
+ return round(degree, mantissa)
10566
10529
 
10567
10530
  @staticmethod
10568
10531
  def Vertices(graph, vertexKey=None, reverse=False):