topologicpy 0.8.24__py3-none-any.whl → 0.8.25__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
@@ -278,6 +278,93 @@ class _DrawTree(object):
278
278
  return self.__str__()
279
279
 
280
280
  class Graph:
281
+
282
+ @staticmethod
283
+ def AccessibilityCentrality(graph, step: int = 2, normalize: bool = False, key: str = "accessibility_centrality", colorKey: str = "ac_color", colorScale: str = "viridis", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
284
+ """
285
+ Computes the accessibility centrality of each vertex in the graph using random walks of fixed step length.
286
+
287
+ Parameters
288
+ ----------
289
+ graph : topologic_core.Graph
290
+ The input graph.
291
+ step : int, optional
292
+ The length of the random walk (number of steps). Default is 2.
293
+ normalize : bool, optional
294
+ If True, normalize the output to the range 0 to 1. Default is False.
295
+ key : str, optional
296
+ Dictionary key to store the accessibility centrality value. Default is "accessibility_centrality".
297
+ colorKey : str, optional
298
+ Dictionary key to store the color value. Default is "ac_color".
299
+ colorScale : str, optional
300
+ Name of the Plotly color scale to use. Default is "viridis".
301
+ mantissa : int, optional
302
+ Decimal precision. Default is 6.
303
+ tolerance : float, optional
304
+ The desired Tolerance. Not used here but included for API compatibility. Default is 0.0001.
305
+ silent : bool, optional
306
+ If True, suppresses error/warning messages. Default is False.
307
+
308
+ Returns
309
+ -------
310
+ list
311
+ A list of accessibility centrality values for each vertex in the graph.
312
+ """
313
+ import numpy as np
314
+ from topologicpy.Graph import Graph
315
+ from topologicpy.Topology import Topology
316
+ from topologicpy.Dictionary import Dictionary
317
+ from topologicpy.Color import Color
318
+ from topologicpy.Helper import Helper
319
+
320
+ if not Topology.IsInstance(graph, "graph"):
321
+ if not silent:
322
+ print("Graph.AccessibilityCentrality - Error: The input graph paramter is not a valid Topologic Graph. Returning None.")
323
+ return None
324
+ vertices = Graph.Vertices(graph)
325
+ n = len(vertices)
326
+ if n == 0:
327
+ return []
328
+
329
+ # Step 1: get transition matrix (row-normalized adjacency matrix)
330
+ A = np.array(Graph.AdjacencyMatrix(graph), dtype=float)
331
+ row_sums = A.sum(axis=1, keepdims=True)
332
+ row_sums[row_sums == 0] = 1 # prevent division by zero
333
+ P = A / row_sums
334
+
335
+ # Step 2: walk matrix of length `step`
336
+ P_h = np.linalg.matrix_power(P, step)
337
+
338
+ # Step 3: compute entropy-based accessibility for each vertex
339
+ values = []
340
+ for i in range(n):
341
+ probs = P_h[i]
342
+ probs = probs[probs > 0]
343
+ entropy = -np.sum(probs * np.log(probs))
344
+ acc = np.exp(entropy)
345
+ values.append(float(acc))
346
+
347
+ # Optional normalization
348
+ if normalize == True:
349
+ if mantissa > 0: # We cannot round numbers from 0 to 1 with a mantissa = 0.
350
+ values = [round(v, mantissa) for v in Helper.Normalize(values)]
351
+ else:
352
+ values = Helper.Normalize(values)
353
+ min_value = 0
354
+ max_value = 1
355
+ else:
356
+ min_value = min(values)
357
+ max_value = max(values)
358
+
359
+ # Assign Values and Colors to Dictionaries
360
+ for i, value in enumerate(values):
361
+ d = Topology.Dictionary(vertices[i])
362
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
363
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
364
+ vertices[i] = Topology.SetDictionary(vertices[i], d)
365
+
366
+ return values
367
+
281
368
  @staticmethod
282
369
  def AddEdge(graph, edge, transferVertexDictionaries: bool = False, transferEdgeDictionaries: bool = False, tolerance: float = 0.0001, silent: bool = False):
283
370
  """
@@ -4393,12 +4480,14 @@ class Graph:
4393
4480
 
4394
4481
  @staticmethod
4395
4482
  def Compare(graphA, graphB,
4483
+ weightAccessibilityCentrality: float = 0.0,
4396
4484
  weightAttributes: float = 0.0,
4397
4485
  weightGeometry: float = 0.0,
4398
4486
  weightBetwennessCentrality: float = 0.0,
4399
4487
  weightClosenessCentrality: float = 0.0,
4400
4488
  weightDegreeCentrality: float = 0.0,
4401
4489
  weightDiameter: float = 0.0,
4490
+ weightEigenCentrality: float = 0.0,
4402
4491
  weightGlobalClusteringCoefficient: float = 0.0,
4403
4492
  weightPageRank: float = 0.0,
4404
4493
  weightStructure: float = 0.0,
@@ -4420,6 +4509,8 @@ class Graph:
4420
4509
  The first input graph.
4421
4510
  graphB : topologic Graph
4422
4511
  The second input graph.
4512
+ weightAccessibilityCentrality : float , optional
4513
+ The desired weight for degree accessibility similarity (graph-level and node-level). Default is 0.0.
4423
4514
  weightAttributes : float , optional
4424
4515
  The desired weight for attribute similarity (dictionary key overlap at vertices). Default is 0.0.
4425
4516
  weightBetwennessCentrality : float , optional
@@ -4430,6 +4521,8 @@ class Graph:
4430
4521
  The desired weight for degree centrality similarity (graph-level and node-level). Default is 0.0.
4431
4522
  weightDiameter : float , optional
4432
4523
  The desired weight for diameter similarity (graph-level and node-level). Default is 0.0.
4524
+ weightEigenCentrality : float , optional
4525
+ The desired weight for eigen centrality similarity (graph-level and node-level). Default is 0.0.
4433
4526
  weightGeometry : float , optional
4434
4527
  The desired weight for geometric similarity (vertex positions). Default is 0.0.
4435
4528
  weightGlobalClusteringCoefficient : float , optional
@@ -4461,10 +4554,12 @@ class Graph:
4461
4554
  dict
4462
4555
  A dictionary of similarity scores between 0 (completely dissimilar) and 1 (identical), based on weighted components.
4463
4556
  The keys in the dictionary are:
4557
+ "accessibility_centrality"
4464
4558
  "attribute"
4465
4559
  "betwenness_centrality"
4466
4560
  "closeness_centrality"
4467
4561
  "degree_centrality"
4562
+ "eigen_centrality"
4468
4563
  "geometry"
4469
4564
  "global_clustering_coefficient"
4470
4565
  "jaccard"
@@ -4587,6 +4682,14 @@ class Graph:
4587
4682
  def safe_mean(lst):
4588
4683
  return sum(lst)/len(lst) if lst else 0
4589
4684
 
4685
+ def accessibility_centrality_similarity(graphA, graphB, mantissa=6):
4686
+ v1 = safe_mean(Graph.AccessibilityCentrality(graphA))
4687
+ v2 = safe_mean(Graph.AccessibilityCentrality(graphB))
4688
+ if v1 == 0 and v2 == 0:
4689
+ return 1
4690
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4691
+ return round((1 - diff), mantissa)
4692
+
4590
4693
  def betweenness_centrality_similarity(graphA, graphB, mantissa=6):
4591
4694
  v1 = safe_mean(Graph.BetweennessCentrality(graphA))
4592
4695
  v2 = safe_mean(Graph.BetweennessCentrality(graphB))
@@ -4619,6 +4722,14 @@ class Graph:
4619
4722
  diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4620
4723
  return round((1 - diff), mantissa)
4621
4724
 
4725
+ def eigen_centrality_similarity(graphA, graphB, mantissa=6):
4726
+ v1 = safe_mean(Graph.EigenCentrality(graphA))
4727
+ v2 = safe_mean(Graph.EigenCentrality(graphB))
4728
+ if v1 == 0 and v2 == 0:
4729
+ return 1
4730
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4731
+ return round((1 - diff), mantissa)
4732
+
4622
4733
  def global_clustering_coefficient_similarity(graphA, graphB, mantissa=6):
4623
4734
  v1 = Graph.GlobalClusteringCoefficient(graphA)
4624
4735
  v2 = Graph.GlobalClusteringCoefficient(graphB)
@@ -4689,23 +4800,26 @@ class Graph:
4689
4800
  print("Graph.Compare - Error: The graphB input parameter is not a valid topologic graph. Returning None.")
4690
4801
  return
4691
4802
 
4692
- total_weight = sum([weightAttributes,
4803
+ total_weight = sum([weightAccessibilityCentrality,
4804
+ weightAttributes,
4693
4805
  weightGeometry,
4694
4806
  weightBetwennessCentrality,
4695
4807
  weightClosenessCentrality,
4696
4808
  weightDegreeCentrality,
4697
4809
  weightDiameter,
4810
+ weightEigenCentrality,
4698
4811
  weightGlobalClusteringCoefficient,
4699
4812
  weightPageRank,
4700
4813
  weightStructure,
4701
4814
  weightWeisfeilerLehman,
4702
4815
  weightJaccard])
4703
-
4816
+ accessibility_centrality_score = accessibility_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightAccessibilityCentrality else 0
4704
4817
  attribute_score = attribute_similarity(graphA, graphB, mantissa=mantissa) if weightAttributes else 0
4705
4818
  betweenness_centrality_score = betweenness_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightBetwennessCentrality else 0
4706
4819
  closeness_centrality_score = closeness_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightClosenessCentrality else 0
4707
4820
  degree_centrality_score = degree_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightDegreeCentrality else 0
4708
4821
  diameter_score = diameter_similarity(graphA, graphB, mantissa=mantissa) if weightDiameter else 0
4822
+ eigen_centrality_score = eigen_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightEigenCentrality else 0
4709
4823
  global_clustering_coefficient_score = global_clustering_coefficient_similarity(graphA, graphB, mantissa=mantissa) if weightGlobalClusteringCoefficient else 0
4710
4824
  geometry_score = geometry_similarity(graphA, graphB, mantissa=mantissa) if weightGeometry else 0
4711
4825
  jaccard_score = weighted_jaccard_similarity(graphA, graphB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa) if weightJaccard else 0
@@ -4714,11 +4828,13 @@ class Graph:
4714
4828
  weisfeiler_lehman_score = weisfeiler_lehman_similarity(graphA, graphB, iterations, mantissa=mantissa) if weightWeisfeilerLehman else 0
4715
4829
 
4716
4830
  weighted_sum = (
4831
+ accessibility_centrality_score * weightAccessibilityCentrality +
4717
4832
  attribute_score * weightAttributes +
4718
4833
  betweenness_centrality_score * weightBetwennessCentrality +
4719
4834
  closeness_centrality_score * weightClosenessCentrality +
4720
4835
  degree_centrality_score * weightDegreeCentrality +
4721
4836
  diameter_score * weightDiameter +
4837
+ eigen_centrality_score * weightEigenCentrality +
4722
4838
  geometry_score * weightGeometry +
4723
4839
  global_clustering_coefficient_score * weightGlobalClusteringCoefficient +
4724
4840
  jaccard_score * weightJaccard +
@@ -4733,10 +4849,12 @@ class Graph:
4733
4849
  overall_score = weighted_sum / total_weight
4734
4850
 
4735
4851
  return {
4852
+ "accessibility_centrality": round(accessibility_centrality_score, mantissa),
4736
4853
  "attribute": round(attribute_score, mantissa),
4737
4854
  "betwenness_centrality": round(betweenness_centrality_score, mantissa),
4738
4855
  "closeness_centrality": round(closeness_centrality_score, mantissa),
4739
4856
  "degree_centrality": round(degree_centrality_score, mantissa),
4857
+ "eigen_centrality": round(eigen_centrality_score, mantissa),
4740
4858
  "geometry": round(geometry_score, mantissa),
4741
4859
  "global_clustering_coefficient": round(global_clustering_coefficient_score, mantissa),
4742
4860
  "jaccard": round(jaccard_score, mantissa),
@@ -5087,7 +5205,7 @@ class Graph:
5087
5205
  Returns
5088
5206
  -------
5089
5207
  list
5090
- The betweenness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
5208
+ The closeness centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
5091
5209
 
5092
5210
  """
5093
5211
  import warnings
@@ -5932,6 +6050,87 @@ class Graph:
5932
6050
  _ = graph.Edges(vertices, tolerance, edges) # Hook to Core
5933
6051
  return list(dict.fromkeys(edges)) # remove duplicates
5934
6052
 
6053
+ @staticmethod
6054
+ def EigenCentrality(graph, normalize: bool = False, key: str = "eigen_centrality", colorKey: str = "ec_color", colorScale: str = "viridis", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
6055
+ """
6056
+ Returns the eigenvector centrality of the input graph. The order of the returned list is the same as the order of vertices.
6057
+
6058
+ Parameters
6059
+ ----------
6060
+ graph : topologic_core.Graph
6061
+ The input graph.
6062
+ weightKey : str, optional
6063
+ Ignored in this implementation. Reserved for future use if weighted adjacency matrix is desired.
6064
+ normalize : bool, optional
6065
+ If set to True, the centrality values are normalized to be in the range 0 to 1. The default is False.
6066
+ key : str, optional
6067
+ The desired dictionary key under which to store the eigenvector centrality score. The default is "eigen_centrality".
6068
+ colorKey : str, optional
6069
+ The desired dictionary key under which to store the eigenvector centrality color. The default is "ec_color".
6070
+ colorScale : str, optional
6071
+ The desired type of Plotly color scale to use (e.g., "viridis", "plasma"). Default is "viridis".
6072
+ For a full list of names, see https://plotly.com/python/builtin-colorscales/.
6073
+ Also supports color-blind friendly scales: "protanopia", "deuteranopia", "tritanopia".
6074
+ mantissa : int, optional
6075
+ The desired length of the mantissa. Default is 6.
6076
+ tolerance : float, optional
6077
+ The convergence tolerance for the power method. Default is 0.0001.
6078
+ silent : bool, optional
6079
+ If set to True, suppresses all messaging and warnings. Default is False.
6080
+
6081
+ Returns
6082
+ -------
6083
+ list
6084
+ A list of eigenvector centrality values corresponding to the vertices in the input graph.
6085
+ """
6086
+ import numpy as np
6087
+ from topologicpy.Graph import Graph
6088
+ from topologicpy.Topology import Topology
6089
+ from topologicpy.Dictionary import Dictionary
6090
+ from topologicpy.Color import Color
6091
+ from topologicpy.Helper import Helper
6092
+
6093
+ if not Topology.IsInstance(graph, "graph"):
6094
+ if not silent:
6095
+ print("Graph.EigenCentrality - Error: The input graph is not a valie Topologic Graph. Returning None.")
6096
+ return None
6097
+ adjacency_matrix = Graph.AdjacencyMatrix(graph)
6098
+ vertices = Graph.Vertices(graph)
6099
+ n = len(vertices)
6100
+ if n == 0:
6101
+ return []
6102
+
6103
+ values = np.ones(n)
6104
+ for _ in range(100):
6105
+ x_new = np.dot(adjacency_matrix, values)
6106
+ norm = np.linalg.norm(x_new)
6107
+ if norm == 0:
6108
+ break
6109
+ x_new = x_new / norm
6110
+ if np.linalg.norm(values - x_new) < tolerance:
6111
+ break
6112
+ values = x_new
6113
+ values = [float(x) for x in values]
6114
+ if normalize == True:
6115
+ if mantissa > 0: # We cannot round numbers from 0 to 1 with a mantissa = 0.
6116
+ values = [round(v, mantissa) for v in Helper.Normalize(values)]
6117
+ else:
6118
+ values = Helper.Normalize(values)
6119
+ min_value = 0
6120
+ max_value = 1
6121
+ else:
6122
+ values = [round(v, mantissa) for v in values]
6123
+ min_value = min(values)
6124
+ max_value = max(values)
6125
+
6126
+ for i, value in enumerate(values):
6127
+ d = Topology.Dictionary(vertices[i])
6128
+ color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
6129
+ d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
6130
+ vertices[i] = Topology.SetDictionary(vertices[i], d)
6131
+
6132
+ return values
6133
+
5935
6134
  @staticmethod
5936
6135
  def ExportToAdjacencyMatrixCSV(adjacencyMatrix, path):
5937
6136
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.24'
1
+ __version__ = '0.8.25'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.24
3
+ Version: 0.8.25
4
4
  Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
5
5
  Author-email: Wassim Jabi <wassim.jabi@gmail.com>
6
6
  License: AGPL v3 License
@@ -12,7 +12,7 @@ topologicpy/Dictionary.py,sha256=Lf24WHW8q_RCq0l8VpT3XJTn6UuStY66JI4Lb4W08jI,341
12
12
  topologicpy/Edge.py,sha256=pu4tZbRbK8qx2oqRbwHAeKuwU2X8JFGPSJjJMTJw8Q0,71418
13
13
  topologicpy/EnergyModel.py,sha256=Pyb28gDDwhzlQIH0xqAygqS0P3SJxWyyV7OWS_AAfRs,53856
14
14
  topologicpy/Face.py,sha256=7K46gB_UIKjKEKyzyY0JqGarqjwjH0ggS-JQTpDtWC4,184847
15
- topologicpy/Graph.py,sha256=BCd6Zv_4J_-WRIP6nyX5FiqnJG1kDynGYelpN5y5S3g,552547
15
+ topologicpy/Graph.py,sha256=v9iFyHVs-dlcH9Pdj4uT4ELgS2VIygfDKAMDoSnSAtI,562010
16
16
  topologicpy/Grid.py,sha256=qRnFUvs079zMOZ6COWzBX6408niI7HyNz-BM0VRguXY,18245
17
17
  topologicpy/Helper.py,sha256=JdvC30WMrla46mTj5TdwCV_bRv-6y8vK5Bkx0prluy4,29100
18
18
  topologicpy/Honeybee.py,sha256=yctkwfdupKnp7bAOjP1Z4YaYpRrWoMEb4gz9Z5zaWwE,21751
@@ -29,9 +29,9 @@ topologicpy/Vector.py,sha256=mx7fgABdioikPWM9HzXKzmqfx3u_XBcU_jlLD4qK2x8,42407
29
29
  topologicpy/Vertex.py,sha256=PIwfbA7_TxK_dSGlSeM5mson97TRr4dYrfZyOLgO150,80913
30
30
  topologicpy/Wire.py,sha256=eRs4PM7h4yU5v6umPh0oBJR4cN8BwsqlVroaFdnvK4w,228499
31
31
  topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
32
- topologicpy/version.py,sha256=c2VEZxOAKtWAyut3MMeHAIgcw2hpB1b9V0vR0y8Apb4,23
33
- topologicpy-0.8.24.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
34
- topologicpy-0.8.24.dist-info/METADATA,sha256=RrGSLhJbNeivQQeKBNJUDMaYmKToe__QsEBWtR5sFEg,10535
35
- topologicpy-0.8.24.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
36
- topologicpy-0.8.24.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
37
- topologicpy-0.8.24.dist-info/RECORD,,
32
+ topologicpy/version.py,sha256=RQuvvS5_gUL4CXqswj954OcBx7QfBRLpwl789EByiZc,23
33
+ topologicpy-0.8.25.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
34
+ topologicpy-0.8.25.dist-info/METADATA,sha256=AZ8ca3QxNZAkqzohr2VAyEi_30MSXsfnm1v_v523aJ8,10535
35
+ topologicpy-0.8.25.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
36
+ topologicpy-0.8.25.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
37
+ topologicpy-0.8.25.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5