topologicpy 0.8.44__py3-none-any.whl → 0.8.46__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 +440 -132
- topologicpy/version.py +1 -1
- {topologicpy-0.8.44.dist-info → topologicpy-0.8.46.dist-info}/METADATA +1 -1
- {topologicpy-0.8.44.dist-info → topologicpy-0.8.46.dist-info}/RECORD +7 -7
- {topologicpy-0.8.44.dist-info → topologicpy-0.8.46.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.44.dist-info → topologicpy-0.8.46.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.44.dist-info → topologicpy-0.8.46.dist-info}/top_level.txt +0 -0
topologicpy/Graph.py
CHANGED
@@ -4575,6 +4575,56 @@ class Graph:
|
|
4575
4575
|
edges = [e for e in edges if Topology.IsInstance(e, "Edge")]
|
4576
4576
|
return topologic.Graph.ByVerticesEdges(vertices, edges) # Hook to Core
|
4577
4577
|
|
4578
|
+
@staticmethod
|
4579
|
+
def Choice(graph, method: str = "vertex", weightKey="length", normalize: bool = False, nxCompatible: bool = False, key: str = "choice", colorKey="ch_color", colorScale="viridis", mantissa: int = 6, tolerance: float = 0.001, silent: bool = False):
|
4580
|
+
"""
|
4581
|
+
This is an alias method for Graph.BetweenessCentrality. Returns the choice (Betweeness 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.
|
4582
|
+
|
4583
|
+
Parameters
|
4584
|
+
----------
|
4585
|
+
graph : topologic_core.Graph
|
4586
|
+
The input graph.
|
4587
|
+
method : str , optional
|
4588
|
+
The method of computing the betweenness centrality. The options are "vertex" or "edge". The default is "vertex".
|
4589
|
+
weightKey : str , optional
|
4590
|
+
If specified, the value in the connected edges' dictionary specified by the weightKey string will be aggregated to calculate
|
4591
|
+
the shortest path. If a numeric value cannot be retrieved from an edge, a value of 1 is used instead.
|
4592
|
+
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.
|
4593
|
+
normalize : bool , optional
|
4594
|
+
If set to True, the values are normalized to be in the range 0 to 1. Otherwise they are not. The default is False.
|
4595
|
+
nxCompatible : bool , optional
|
4596
|
+
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.
|
4597
|
+
key : str , optional
|
4598
|
+
The desired dictionary key under which to store the betweenness centrality score. The default is "betweenness_centrality".
|
4599
|
+
colorKey : str , optional
|
4600
|
+
The desired dictionary key under which to store the betweenness centrality color. The default is "betweenness_centrality".
|
4601
|
+
colorScale : str , optional
|
4602
|
+
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/.
|
4603
|
+
In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
|
4604
|
+
mantissa : int , optional
|
4605
|
+
The desired length of the mantissa. The default is 6.
|
4606
|
+
tolerance : float , optional
|
4607
|
+
The desired tolerance. The default is 0.0001.
|
4608
|
+
|
4609
|
+
Returns
|
4610
|
+
-------
|
4611
|
+
list
|
4612
|
+
The choice (betweenness centrality) of the input list of vertices within the input graph. The values are in the range 0 to 1.
|
4613
|
+
|
4614
|
+
"""
|
4615
|
+
return Graph.BetweennessCentrality(graph,
|
4616
|
+
method=method,
|
4617
|
+
weightKey=weightKey,
|
4618
|
+
normalize=normalize,
|
4619
|
+
nxCompatible=nxCompatible,
|
4620
|
+
key=key,
|
4621
|
+
colorKey=colorKey,
|
4622
|
+
colorScale=colorScale,
|
4623
|
+
mantissa=mantissa,
|
4624
|
+
tolerance=tolerance,
|
4625
|
+
silent=silent)
|
4626
|
+
|
4627
|
+
|
4578
4628
|
@staticmethod
|
4579
4629
|
def ChromaticNumber(graph, maxColors: int = 3, silent: bool = False):
|
4580
4630
|
"""
|
@@ -4750,7 +4800,8 @@ class Graph:
|
|
4750
4800
|
weightJaccard: float = 0.0,
|
4751
4801
|
vertexIDKey: str = "id",
|
4752
4802
|
edgeWeightKey: str = None,
|
4753
|
-
|
4803
|
+
vertexKey: str = None,
|
4804
|
+
iterations: int = 2,
|
4754
4805
|
mantissa: int = 6,
|
4755
4806
|
silent: bool = False):
|
4756
4807
|
"""
|
@@ -4797,8 +4848,10 @@ class Graph:
|
|
4797
4848
|
The dictionary key under which to find the weight of the edge for weighted graphs.
|
4798
4849
|
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
4799
4850
|
The default is None which means all edges are treated as if they have a weight of 1.
|
4851
|
+
vertexKey: str , optional
|
4852
|
+
The vertex key to use for the Weifeiler-Lehman initial labels. The default is None which means it will use vertex degree as an initial label.
|
4800
4853
|
iterations : int , optional
|
4801
|
-
The desired number of Weisfeiler-Lehman iterations. Default is
|
4854
|
+
The desired number of Weisfeiler-Lehman kernel iterations. Default is 2.
|
4802
4855
|
mantissa : int , optional
|
4803
4856
|
The desired length of the mantissa. The default is 6.
|
4804
4857
|
silent : bool , optional
|
@@ -4885,7 +4938,7 @@ class Graph:
|
|
4885
4938
|
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
4886
4939
|
The default is None which means all edges are treated as if they have a weight of 1.
|
4887
4940
|
iterations : int , optional
|
4888
|
-
The desired number of Weisfeiler-Lehman iterations. Default is
|
4941
|
+
The desired number of Weisfeiler-Lehman iterations. Default is 2.
|
4889
4942
|
mantissa : int , optional
|
4890
4943
|
The desired length of the mantissa. The default is 6.
|
4891
4944
|
|
@@ -5012,39 +5065,9 @@ class Graph:
|
|
5012
5065
|
|
5013
5066
|
return round((vertex_score + edge_score) / 2, mantissa)
|
5014
5067
|
|
5015
|
-
def
|
5016
|
-
|
5017
|
-
|
5018
|
-
|
5019
|
-
for v in vertices:
|
5020
|
-
d = Topology.Dictionary(v)
|
5021
|
-
label = str(Dictionary.ValueAtKey(d, "label")) if d and Dictionary.ValueAtKey(d, "label") else "0"
|
5022
|
-
labels[v] = label
|
5023
|
-
|
5024
|
-
all_label_counts = Counter()
|
5025
|
-
|
5026
|
-
for _ in range(iterations):
|
5027
|
-
new_labels = {}
|
5028
|
-
for v in vertices:
|
5029
|
-
neighbors = Graph.AdjacentVertices(graph, v)
|
5030
|
-
neighbor_labels = sorted(labels.get(n, "0") for n in neighbors)
|
5031
|
-
long_label = labels[v] + "_" + "_".join(neighbor_labels)
|
5032
|
-
hashed_label = hashlib.md5(long_label.encode()).hexdigest()
|
5033
|
-
new_labels[v] = hashed_label
|
5034
|
-
all_label_counts[hashed_label] += 1
|
5035
|
-
labels = new_labels
|
5036
|
-
|
5037
|
-
return all_label_counts
|
5038
|
-
|
5039
|
-
def weisfeiler_lehman_similarity(graphA, graphB, iterations=3, mantissa=6):
|
5040
|
-
f1 = weisfeiler_lehman_fingerprint(graphA, iterations)
|
5041
|
-
f2 = weisfeiler_lehman_fingerprint(graphB, iterations)
|
5042
|
-
|
5043
|
-
common_labels = set(f1.keys()) & set(f2.keys())
|
5044
|
-
score = sum(min(f1[label], f2[label]) for label in common_labels)
|
5045
|
-
norm = max(sum(f1.values()), sum(f2.values()), 1)
|
5046
|
-
|
5047
|
-
return round(score / norm, mantissa)
|
5068
|
+
def weisfeiler_lehman_similarity(graphA, graphB, key=None, iterations=3, mantissa=6):
|
5069
|
+
score = Graph.WLKernel(graphA, graphB, key=key, iterations=iterations, normalize=True, mantissa=mantissa)
|
5070
|
+
return score
|
5048
5071
|
|
5049
5072
|
if not Topology.IsInstance(graphA, "graph"):
|
5050
5073
|
if not silent:
|
@@ -5080,7 +5103,7 @@ class Graph:
|
|
5080
5103
|
jaccard_score = weighted_jaccard_similarity(graphA, graphB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa) if weightJaccard else 0
|
5081
5104
|
pagerank_score = pagerank_similarity(graphA, graphB, mantissa=mantissa) if weightPageRank else 0
|
5082
5105
|
structure_score = structure_similarity(graphA, graphB, mantissa=mantissa) if weightStructure else 0
|
5083
|
-
weisfeiler_lehman_score = weisfeiler_lehman_similarity(graphA, graphB, iterations, mantissa=mantissa) if weightWeisfeilerLehman else 0
|
5106
|
+
weisfeiler_lehman_score = weisfeiler_lehman_similarity(graphA, graphB, key=vertexKey, iterations=iterations, mantissa=mantissa) if weightWeisfeilerLehman else 0
|
5084
5107
|
|
5085
5108
|
weighted_sum = (
|
5086
5109
|
accessibility_centrality_score * weightAccessibilityCentrality +
|
@@ -5427,94 +5450,159 @@ class Graph:
|
|
5427
5450
|
graph = Graph.RemoveVertex(graph,ev, tolerance=tolerance)
|
5428
5451
|
return graph
|
5429
5452
|
|
5453
|
+
|
5430
5454
|
@staticmethod
|
5431
|
-
def ClosenessCentrality(
|
5455
|
+
def ClosenessCentrality(
|
5456
|
+
graph,
|
5457
|
+
weightKey: str = "length",
|
5458
|
+
normalize: bool = False,
|
5459
|
+
nxCompatible: bool = True,
|
5460
|
+
key: str = "closeness_centrality",
|
5461
|
+
colorKey: str = "cc_color",
|
5462
|
+
colorScale: str = "viridis",
|
5463
|
+
mantissa: int = 6,
|
5464
|
+
tolerance: float = 0.0001,
|
5465
|
+
silent: bool = False
|
5466
|
+
):
|
5432
5467
|
"""
|
5433
|
-
|
5468
|
+
Returns the closeness centrality of the input graph. The order of the returned
|
5469
|
+
list matches the order of Graph.Vertices(graph).
|
5470
|
+
See: https://en.wikipedia.org/wiki/Closeness_centrality
|
5434
5471
|
|
5435
5472
|
Parameters
|
5436
5473
|
----------
|
5437
5474
|
graph : topologic_core.Graph
|
5438
5475
|
The input graph.
|
5439
5476
|
weightKey : str , optional
|
5440
|
-
If specified,
|
5441
|
-
|
5442
|
-
|
5477
|
+
If specified, this edge attribute will be used as the distance weight when
|
5478
|
+
computing shortest paths. If set to a name containing "Length" or "Distance",
|
5479
|
+
it will be mapped to "length".
|
5480
|
+
Note: Graph.NetworkXGraph automatically provides a "length" attribute on all edges.
|
5443
5481
|
normalize : bool , optional
|
5444
|
-
If
|
5482
|
+
If True, the returned values are rescaled to [0, 1]. Otherwise raw values
|
5483
|
+
from NetworkX (optionally using the improved formula) are returned.
|
5445
5484
|
nxCompatible : bool , optional
|
5446
|
-
If
|
5447
|
-
For single
|
5485
|
+
If True, use NetworkX's wf_improved scaling (Wasserman and Faust).
|
5486
|
+
For single-component graphs it matches the original formula.
|
5448
5487
|
key : str , optional
|
5449
|
-
The
|
5488
|
+
The dictionary key under which to store the closeness centrality score.
|
5450
5489
|
colorKey : str , optional
|
5451
|
-
The
|
5490
|
+
The dictionary key under which to store a color derived from the score.
|
5452
5491
|
colorScale : str , optional
|
5453
|
-
|
5454
|
-
In addition to these, three color-blind friendly scales are included. These are "protanopia", "deuteranopia", and "tritanopia" for red, green, and blue colorblindness respectively.
|
5492
|
+
Plotly color scale name (e.g., "viridis", "plasma").
|
5455
5493
|
mantissa : int , optional
|
5456
5494
|
The desired length of the mantissa. The default is 6.
|
5457
5495
|
tolerance : float , optional
|
5458
5496
|
The desired tolerance. The default is 0.0001.
|
5497
|
+
silent : bool , optional
|
5498
|
+
If set to True, error and warning messages are suppressed. The default is False.
|
5459
5499
|
|
5460
5500
|
Returns
|
5461
5501
|
-------
|
5462
|
-
list
|
5463
|
-
|
5464
|
-
|
5502
|
+
list[float]
|
5503
|
+
Closeness centrality values for vertices in the same order as Graph.Vertices(graph).
|
5465
5504
|
"""
|
5466
5505
|
import warnings
|
5467
|
-
|
5468
5506
|
try:
|
5469
5507
|
import networkx as nx
|
5470
|
-
except:
|
5471
|
-
|
5472
|
-
|
5473
|
-
|
5474
|
-
|
5475
|
-
|
5476
|
-
try:
|
5477
|
-
import networkx as nx
|
5478
|
-
print("Graph.ClosenessCentrality - Infromation: networkx library installed correctly.")
|
5479
|
-
except:
|
5480
|
-
warnings.warn("Graph.ClosenessCentrality - Error: Could not import networkx. Please try to install networkx manually. Returning None.")
|
5481
|
-
return None
|
5482
|
-
|
5508
|
+
except Exception as e:
|
5509
|
+
warnings.warn(
|
5510
|
+
f"Graph.ClosenessCentrality - Error: networkx is required but not installed ({e}). Returning None."
|
5511
|
+
)
|
5512
|
+
return None
|
5513
|
+
|
5483
5514
|
from topologicpy.Dictionary import Dictionary
|
5484
5515
|
from topologicpy.Color import Color
|
5485
5516
|
from topologicpy.Topology import Topology
|
5486
5517
|
from topologicpy.Helper import Helper
|
5487
5518
|
|
5519
|
+
# Topology.IsInstance is case-insensitive, so a single call is sufficient.
|
5488
5520
|
if not Topology.IsInstance(graph, "graph"):
|
5489
5521
|
if not silent:
|
5490
|
-
print("Graph.ClosenessCentrality - Error: The input
|
5522
|
+
print("Graph.ClosenessCentrality - Error: The input is not a valid Graph. Returning None.")
|
5491
5523
|
return None
|
5492
|
-
|
5493
|
-
if
|
5494
|
-
if
|
5524
|
+
vertices = Graph.Vertices(graph)
|
5525
|
+
if len(vertices) == 0:
|
5526
|
+
if not silent:
|
5527
|
+
print("Graph.ClosenessCentrality - Warning: Graph has no vertices. Returning [].")
|
5528
|
+
return []
|
5529
|
+
|
5530
|
+
# Normalize the weight key semantics
|
5531
|
+
distance_attr = None
|
5532
|
+
if isinstance(weightKey, str) and weightKey:
|
5533
|
+
if ("len" in weightKey.lower()) or ("dis" in weightKey.lower()):
|
5495
5534
|
weightKey = "length"
|
5535
|
+
distance_attr = weightKey
|
5536
|
+
|
5537
|
+
# Build the NX graph
|
5496
5538
|
nx_graph = Graph.NetworkXGraph(graph)
|
5497
|
-
|
5498
|
-
|
5499
|
-
|
5500
|
-
if
|
5501
|
-
|
5502
|
-
|
5539
|
+
|
5540
|
+
# Graph.NetworkXGraph automatically adds "length" to all edges.
|
5541
|
+
# So if distance_attr == "length", we trust it and skip per-edge checks.
|
5542
|
+
if distance_attr and distance_attr != "length":
|
5543
|
+
# For any non-"length" custom attribute, verify presence; else fall back unweighted.
|
5544
|
+
attr_missing = any(
|
5545
|
+
(distance_attr not in data) or (data[distance_attr] is None)
|
5546
|
+
for _, _, data in nx_graph.edges(data=True)
|
5547
|
+
)
|
5548
|
+
if attr_missing:
|
5549
|
+
if not silent:
|
5550
|
+
print("Graph.ClosenessCentrality - Warning: The specified edge attribute was not found on all edges. Falling back to unweighted closeness.")
|
5551
|
+
distance_arg = None
|
5503
5552
|
else:
|
5504
|
-
|
5505
|
-
min_value = 0
|
5506
|
-
max_value = 1
|
5553
|
+
distance_arg = distance_attr
|
5507
5554
|
else:
|
5508
|
-
|
5509
|
-
|
5555
|
+
# Use "length" directly or unweighted if distance_attr is falsy.
|
5556
|
+
distance_arg = distance_attr if distance_attr else None
|
5510
5557
|
|
5511
|
-
|
5558
|
+
# Compute centrality (dict keyed by NetworkX nodes)
|
5559
|
+
try:
|
5560
|
+
cc_dict = nx.closeness_centrality(nx_graph, distance=distance_arg, wf_improved=nxCompatible)
|
5561
|
+
except Exception as e:
|
5562
|
+
if not silent:
|
5563
|
+
print(f"Graph.ClosenessCentrality - Error: NetworkX failed to compute centrality ({e}). Returning None.")
|
5564
|
+
return None
|
5565
|
+
|
5566
|
+
# NetworkX vertex ids are in the same numerice order as the list of vertices starting from 0.
|
5567
|
+
raw_values = []
|
5568
|
+
for i, v in enumerate(vertices):
|
5569
|
+
try:
|
5570
|
+
raw_values.append(float(cc_dict.get(i, 0.0)))
|
5571
|
+
except Exception:
|
5572
|
+
if not silent:
|
5573
|
+
print(f,"Graph.ClosenessCentrality - Warning: Could not retrieve score for vertex {i}. Assigning a Zero (0).")
|
5574
|
+
raw_values.append(0.0)
|
5575
|
+
|
5576
|
+
# Optional normalization ONLY once, then rounding once at the end
|
5577
|
+
values_for_return = Helper.Normalize(raw_values) if normalize else raw_values
|
5578
|
+
|
5579
|
+
# Values for color scaling should reflect the displayed numbers
|
5580
|
+
color_values = values_for_return
|
5581
|
+
|
5582
|
+
# Single rounding at the end for return values
|
5583
|
+
if mantissa is not None and mantissa >= 0:
|
5584
|
+
values_for_return = [round(v, mantissa) for v in values_for_return]
|
5585
|
+
|
5586
|
+
# Prepare color mapping range, guarding equal-range case
|
5587
|
+
if color_values:
|
5588
|
+
min_value = min(color_values)
|
5589
|
+
max_value = max(color_values)
|
5590
|
+
else:
|
5591
|
+
min_value, max_value = 0.0, 1.0
|
5592
|
+
|
5593
|
+
if abs(max_value - min_value) < tolerance:
|
5594
|
+
max_value = min_value + tolerance
|
5595
|
+
|
5596
|
+
# Annotate vertices with score and color
|
5597
|
+
for i, value in enumerate(color_values):
|
5512
5598
|
d = Topology.Dictionary(vertices[i])
|
5513
|
-
|
5514
|
-
|
5599
|
+
color_hex = Color.AnyToHex(
|
5600
|
+
Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale)
|
5601
|
+
)
|
5602
|
+
d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [values_for_return[i], color_hex])
|
5515
5603
|
vertices[i] = Topology.SetDictionary(vertices[i], d)
|
5516
5604
|
|
5517
|
-
return
|
5605
|
+
return values_for_return
|
5518
5606
|
|
5519
5607
|
@staticmethod
|
5520
5608
|
def Community(graph, key: str = "partition", mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
|
@@ -5684,7 +5772,7 @@ class Graph:
|
|
5684
5772
|
@staticmethod
|
5685
5773
|
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):
|
5686
5774
|
"""
|
5687
|
-
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/.
|
5775
|
+
This is an alias method for Graph.DegreeCentrality. 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/.
|
5688
5776
|
|
5689
5777
|
Parameters
|
5690
5778
|
----------
|
@@ -5718,38 +5806,17 @@ class Graph:
|
|
5718
5806
|
The connectivity score of the input list of vertices within the input graph. The values are in the range 0 to 1 if normalized.
|
5719
5807
|
|
5720
5808
|
"""
|
5721
|
-
|
5722
|
-
|
5723
|
-
|
5724
|
-
|
5725
|
-
|
5726
|
-
|
5727
|
-
|
5728
|
-
|
5729
|
-
|
5730
|
-
|
5731
|
-
if vertices == None:
|
5732
|
-
vertices = Graph.Vertices(graph)
|
5733
|
-
values = [Graph.VertexDegree(graph, v, weightKey=weightKey, mantissa=mantissa, tolerance=tolerance, silent=silent) for v in vertices]
|
5734
|
-
values = [round(v, mantissa) for v in values]
|
5735
|
-
if normalize == True:
|
5736
|
-
if mantissa > 0:
|
5737
|
-
values = [round(v, mantissa) for v in Helper.Normalize(values)]
|
5738
|
-
else:
|
5739
|
-
values = Helper.Normalize(values)
|
5740
|
-
min_value = 0
|
5741
|
-
max_value = 1
|
5742
|
-
else:
|
5743
|
-
min_value = min(values)
|
5744
|
-
max_value = max(values)
|
5809
|
+
return Graph.DegreeCentrality(graph=graph,
|
5810
|
+
vertices=vertices,
|
5811
|
+
weightKey=weightKey,
|
5812
|
+
normalize=normalize,
|
5813
|
+
key="connectivity",
|
5814
|
+
colorKey=colorKey,
|
5815
|
+
colorScale=colorScale,
|
5816
|
+
mantissa=mantissa,
|
5817
|
+
tolerance=tolerance,
|
5818
|
+
silent=silent)
|
5745
5819
|
|
5746
|
-
for i, value in enumerate(values):
|
5747
|
-
color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
|
5748
|
-
d = Topology.Dictionary(vertices[i])
|
5749
|
-
d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
|
5750
|
-
v = Topology.SetDictionary(vertices[i], d)
|
5751
|
-
return values
|
5752
|
-
|
5753
5820
|
@staticmethod
|
5754
5821
|
def ContainsEdge(graph, edge, tolerance=0.0001):
|
5755
5822
|
"""
|
@@ -5884,7 +5951,7 @@ class Graph:
|
|
5884
5951
|
tolerance: float = 0.001,
|
5885
5952
|
silent: bool = False):
|
5886
5953
|
"""
|
5887
|
-
|
5954
|
+
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.
|
5888
5955
|
|
5889
5956
|
Parameters
|
5890
5957
|
----------
|
@@ -5911,30 +5978,42 @@ class Graph:
|
|
5911
5978
|
Returns
|
5912
5979
|
-------
|
5913
5980
|
list
|
5914
|
-
The
|
5981
|
+
The degree centrality of the input list of vertices within the input graph. The values are in the range 0 to 1.
|
5915
5982
|
|
5916
5983
|
"""
|
5917
5984
|
|
5918
|
-
from topologicpy.Dictionary import Dictionary
|
5919
|
-
from topologicpy.Color import Color
|
5920
5985
|
from topologicpy.Topology import Topology
|
5986
|
+
from topologicpy.Dictionary import Dictionary
|
5921
5987
|
from topologicpy.Helper import Helper
|
5988
|
+
from topologicpy.Color import Color
|
5922
5989
|
|
5923
|
-
if not Topology.IsInstance(graph, "
|
5990
|
+
if not Topology.IsInstance(graph, "Graph"):
|
5924
5991
|
if not silent:
|
5925
5992
|
print("Graph.DegreeCentrality - Error: The input graph is not a valid graph. Returning None.")
|
5926
5993
|
return None
|
5994
|
+
if vertices == None:
|
5995
|
+
vertices = Graph.Vertices(graph)
|
5996
|
+
values = [Graph.VertexDegree(graph, v, weightKey=weightKey, mantissa=mantissa, tolerance=tolerance, silent=silent) for v in vertices]
|
5997
|
+
if normalize == True:
|
5998
|
+
if mantissa > 0:
|
5999
|
+
values = [round(v, mantissa) for v in Helper.Normalize(values)]
|
6000
|
+
else:
|
6001
|
+
values = Helper.Normalize(values)
|
6002
|
+
min_value = 0
|
6003
|
+
max_value = 1
|
6004
|
+
else:
|
6005
|
+
min_value = min(values)
|
6006
|
+
max_value = max(values)
|
6007
|
+
|
6008
|
+
if abs(max_value - min_value) < tolerance:
|
6009
|
+
max_value = min_value + tolerance
|
5927
6010
|
|
5928
|
-
|
5929
|
-
|
5930
|
-
|
5931
|
-
|
5932
|
-
|
5933
|
-
|
5934
|
-
colorScale=colorScale,
|
5935
|
-
mantissa=mantissa,
|
5936
|
-
tolerance=tolerance,
|
5937
|
-
silent=silent)
|
6011
|
+
for i, value in enumerate(values):
|
6012
|
+
color = Color.AnyToHex(Color.ByValueInRange(value, minValue=min_value, maxValue=max_value, colorScale=colorScale))
|
6013
|
+
d = Topology.Dictionary(vertices[i])
|
6014
|
+
d = Dictionary.SetValuesAtKeys(d, [key, colorKey], [value, color])
|
6015
|
+
v = Topology.SetDictionary(vertices[i], d)
|
6016
|
+
return values
|
5938
6017
|
|
5939
6018
|
@staticmethod
|
5940
6019
|
def DegreeMatrix(graph):
|
@@ -9442,6 +9521,70 @@ class Graph:
|
|
9442
9521
|
incoming_vertices.append(Graph.NearestVertex(graph, sv))
|
9443
9522
|
return incoming_vertices
|
9444
9523
|
|
9524
|
+
@staticmethod
|
9525
|
+
def Integration(
|
9526
|
+
graph,
|
9527
|
+
weightKey: str = "length",
|
9528
|
+
normalize: bool = False,
|
9529
|
+
nxCompatible: bool = True,
|
9530
|
+
key: str = "integration",
|
9531
|
+
colorKey: str = "in_color",
|
9532
|
+
colorScale: str = "viridis",
|
9533
|
+
mantissa: int = 6,
|
9534
|
+
tolerance: float = 0.0001,
|
9535
|
+
silent: bool = False
|
9536
|
+
):
|
9537
|
+
"""
|
9538
|
+
This is an alias method for Graph.ClosenessCentrality. Returns the integration (closeness centrality) of the input graph. The order of the returned
|
9539
|
+
list matches the order of Graph.Vertices(graph).
|
9540
|
+
See: https://en.wikipedia.org/wiki/Closeness_centrality
|
9541
|
+
|
9542
|
+
Parameters
|
9543
|
+
----------
|
9544
|
+
graph : topologic_core.Graph
|
9545
|
+
The input graph.
|
9546
|
+
weightKey : str , optional
|
9547
|
+
If specified, this edge attribute will be used as the distance weight when
|
9548
|
+
computing shortest paths. If set to a name containing "Length" or "Distance",
|
9549
|
+
it will be mapped to "length".
|
9550
|
+
Note: Graph.NetworkXGraph automatically provides a "length" attribute on all edges.
|
9551
|
+
normalize : bool , optional
|
9552
|
+
If True, the returned values are rescaled to [0, 1]. Otherwise raw values
|
9553
|
+
from NetworkX (optionally using the improved formula) are returned.
|
9554
|
+
nxCompatible : bool , optional
|
9555
|
+
If True, use NetworkX's wf_improved scaling (Wasserman and Faust).
|
9556
|
+
For single-component graphs it matches the original formula.
|
9557
|
+
key : str , optional
|
9558
|
+
The dictionary key under which to store the closeness centrality score.
|
9559
|
+
colorKey : str , optional
|
9560
|
+
The dictionary key under which to store a color derived from the score.
|
9561
|
+
colorScale : str , optional
|
9562
|
+
Plotly color scale name (e.g., "viridis", "plasma").
|
9563
|
+
mantissa : int , optional
|
9564
|
+
The desired length of the mantissa. The default is 6.
|
9565
|
+
tolerance : float , optional
|
9566
|
+
The desired tolerance. The default is 0.0001.
|
9567
|
+
silent : bool , optional
|
9568
|
+
If set to True, error and warning messages are suppressed. The default is False.
|
9569
|
+
|
9570
|
+
Returns
|
9571
|
+
-------
|
9572
|
+
list[float]
|
9573
|
+
Integration (closeness centrality) values for vertices in the same order as Graph.Vertices(graph).
|
9574
|
+
"""
|
9575
|
+
return Graph.ClosenessCentrality(
|
9576
|
+
graph,
|
9577
|
+
weightKey=weightKey,
|
9578
|
+
normalize=normalize,
|
9579
|
+
nxCompatible=nxCompatible,
|
9580
|
+
key=key,
|
9581
|
+
colorKey=colorKey,
|
9582
|
+
colorScale=colorScale,
|
9583
|
+
mantissa=mantissa,
|
9584
|
+
tolerance=tolerance,
|
9585
|
+
silent=silent
|
9586
|
+
)
|
9587
|
+
|
9445
9588
|
@staticmethod
|
9446
9589
|
def IsBipartite(graph, tolerance=0.0001):
|
9447
9590
|
"""
|
@@ -13749,6 +13892,171 @@ class Graph:
|
|
13749
13892
|
diffBA = Graph.Difference(graphB, graphA, vertexKeys=vertexKeys, useCentroid=useCentroid, tolerance=tolerance, silent=True)
|
13750
13893
|
return Graph.Union(diffAB, diffBA, vertexKeys=vertexKeys, useCentroid=useCentroid, tolerance=tolerance, silent=True)
|
13751
13894
|
|
13895
|
+
@staticmethod
|
13896
|
+
def WLFeatures(graph, key: str = None, iterations: int = 2, silent: bool = False):
|
13897
|
+
"""
|
13898
|
+
Returns a Weisfeiler-Lehman subtree features for a Graph. See https://en.wikipedia.org/wiki/Weisfeiler_Leman_graph_isomorphism_test
|
13899
|
+
|
13900
|
+
Parameters
|
13901
|
+
----------
|
13902
|
+
graph : topologic_core.Graph
|
13903
|
+
The input graph.
|
13904
|
+
key : str , optional
|
13905
|
+
The vertex key to use as an initial label. The default is None which means the vertex degree is used instead.
|
13906
|
+
iterations : int , optional
|
13907
|
+
The desired number of WL iterations. (non-negative int). The default is 2.
|
13908
|
+
silent : bool, optional
|
13909
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
13910
|
+
|
13911
|
+
Returns
|
13912
|
+
-------
|
13913
|
+
dict
|
13914
|
+
{feature_id: count} where feature_id is an int representing a WL label.
|
13915
|
+
"""
|
13916
|
+
|
13917
|
+
from topologicpy.Topology import Topology
|
13918
|
+
from topologicpy.Dictionary import Dictionary
|
13919
|
+
from collections import defaultdict
|
13920
|
+
|
13921
|
+
def _neighbors_map(graph):
|
13922
|
+
"""
|
13923
|
+
Returns:
|
13924
|
+
vertices: list of vertex objects in a stable order
|
13925
|
+
vidx: dict mapping vertex -> index
|
13926
|
+
nbrs: dict index -> sorted list of neighbor indices
|
13927
|
+
"""
|
13928
|
+
vertices = Graph.Vertices(graph)
|
13929
|
+
vidx = {v: i for i, v in enumerate(vertices)}
|
13930
|
+
nbrs = {}
|
13931
|
+
for v in vertices:
|
13932
|
+
i = vidx[v]
|
13933
|
+
adj = Graph.AdjacentVertices(graph, v) or []
|
13934
|
+
nbrs[i] = sorted(vidx[a] for a in adj if a in vidx and a is not v)
|
13935
|
+
return vertices, vidx, nbrs
|
13936
|
+
|
13937
|
+
def _initial_labels(graph, key=None, default="degree"):
|
13938
|
+
"""
|
13939
|
+
Returns an integer label per node index using either vertex dictionary labels
|
13940
|
+
or a structural default (degree or constant).
|
13941
|
+
"""
|
13942
|
+
vertices, vidx, nbrs = _neighbors_map(graph)
|
13943
|
+
labels = {}
|
13944
|
+
if key:
|
13945
|
+
found_any = False
|
13946
|
+
tmp = {}
|
13947
|
+
for v in vertices:
|
13948
|
+
d = Topology.Dictionary(v)
|
13949
|
+
val = Dictionary.ValueAtKey(d, key)
|
13950
|
+
if val is not None:
|
13951
|
+
found_any = True
|
13952
|
+
tmp[vidx[v]] = str(val) if val is not None else None
|
13953
|
+
if found_any:
|
13954
|
+
# fill missing with a sentinel
|
13955
|
+
for i, val in tmp.items():
|
13956
|
+
labels[i] = val if val is not None else "__MISSING__"
|
13957
|
+
else:
|
13958
|
+
# fall back to structural init if no labels exist
|
13959
|
+
if default == "degree":
|
13960
|
+
labels = {i: str(len(nbrs[i])) for i in nbrs}
|
13961
|
+
else:
|
13962
|
+
labels = {i: "0" for i in nbrs}
|
13963
|
+
else: # Add a vertex degree information.
|
13964
|
+
_ = Graph.DegreeCentrality(graph, key="_dc_")
|
13965
|
+
return _initial_labels(graph, key="_dc_")
|
13966
|
+
return labels, nbrs
|
13967
|
+
|
13968
|
+
def _canonize_string_labels(str_labels):
|
13969
|
+
"""
|
13970
|
+
Deterministically map arbitrary strings to dense integer ids.
|
13971
|
+
Returns:
|
13972
|
+
int_labels: dict node_index -> int label
|
13973
|
+
vocab: dict string_label -> int id
|
13974
|
+
"""
|
13975
|
+
# stable order by string to keep mapping deterministic across runs
|
13976
|
+
unique = sorted(set(str_labels.values()))
|
13977
|
+
vocab = {lab: k for k, lab in enumerate(unique)}
|
13978
|
+
return {i: vocab[s] for i, s in str_labels.items()}, vocab
|
13979
|
+
|
13980
|
+
from topologicpy.Topology import Topology
|
13981
|
+
|
13982
|
+
if not Topology.IsInstance(graph, "Graph"):
|
13983
|
+
if not silent:
|
13984
|
+
print("Graph.WLFeatures - Error: The input graph parameter is not a valid topologic graph. Returning None.")
|
13985
|
+
return None
|
13986
|
+
|
13987
|
+
str_labels, nbrs = _initial_labels(graph, key=key)
|
13988
|
+
features = defaultdict(int)
|
13989
|
+
|
13990
|
+
# iteration 0
|
13991
|
+
labels, _ = _canonize_string_labels(str_labels)
|
13992
|
+
for lab in labels.values():
|
13993
|
+
features[lab] += 1
|
13994
|
+
|
13995
|
+
# WL iterations
|
13996
|
+
cur = labels
|
13997
|
+
for _ in range(iterations):
|
13998
|
+
new_str = {}
|
13999
|
+
for i in nbrs:
|
14000
|
+
neigh = [cur[j] for j in nbrs[i]]
|
14001
|
+
neigh.sort()
|
14002
|
+
new_str[i] = f"{cur[i]}|{','.join(map(str, neigh))}"
|
14003
|
+
cur, _ = _canonize_string_labels(new_str)
|
14004
|
+
for lab in cur.values():
|
14005
|
+
features[lab] += 1
|
14006
|
+
|
14007
|
+
return dict(features)
|
14008
|
+
|
14009
|
+
@staticmethod
|
14010
|
+
def WLKernel(graphA, graphB, key: str = None, iterations: int = 2, normalize: bool = True, mantissa: int = 6, silent: bool = False):
|
14011
|
+
"""
|
14012
|
+
Returns a cosine-normalized Weisfeiler-Lehman kernel between two graphs. See https://en.wikipedia.org/wiki/Weisfeiler_Leman_graph_isomorphism_test
|
14013
|
+
|
14014
|
+
Parameters
|
14015
|
+
----------
|
14016
|
+
graphA : topologic_core.Graph
|
14017
|
+
The first input graph.
|
14018
|
+
graphB : topologic_core.Graph
|
14019
|
+
The second input graph.
|
14020
|
+
key : str , optional
|
14021
|
+
The vertex key to use as an initial label. The default is None which means the vertex degree is used instead.
|
14022
|
+
iterations : int , optional
|
14023
|
+
The desired number of WL iterations. (non-negative int). The default is 2.
|
14024
|
+
normalize : bool , optional
|
14025
|
+
if set to True, the returned value is normalized between 0 and 1. The default is True.
|
14026
|
+
mantissa : int , optional
|
14027
|
+
The desired length of the mantissa. The default is 6.
|
14028
|
+
|
14029
|
+
Returns
|
14030
|
+
-------
|
14031
|
+
float
|
14032
|
+
The cosine-normalized Weisfeiler-Lehman kernel
|
14033
|
+
"""
|
14034
|
+
from topologicpy.Topology import Topology
|
14035
|
+
|
14036
|
+
if not Topology.IsInstance(graphA, "Graph"):
|
14037
|
+
if not silent:
|
14038
|
+
print("Graph.WLFeatures - Error: The input graphA parameter is not a valid topologic graph. Returning None.")
|
14039
|
+
return None
|
14040
|
+
if not Topology.IsInstance(graphB, "Graph"):
|
14041
|
+
if not silent:
|
14042
|
+
print("Graph.WLFeatures - Error: The input graphB parameter is not a valid topologic graph. Returning None.")
|
14043
|
+
return None
|
14044
|
+
f1 = Graph.WLFeatures(graphA, key=key, iterations=iterations)
|
14045
|
+
f2 = Graph.WLFeatures(graphB, key=key, iterations=iterations)
|
14046
|
+
|
14047
|
+
# dot product
|
14048
|
+
keys = set(f1) | set(f2)
|
14049
|
+
dot = sum(f1.get(k, 0) * f2.get(k, 0) for k in keys)
|
14050
|
+
|
14051
|
+
if not normalize:
|
14052
|
+
return round(float(dot), mantissa)
|
14053
|
+
|
14054
|
+
import math
|
14055
|
+
n1 = math.sqrt(sum(v*v for v in f1.values()))
|
14056
|
+
n2 = math.sqrt(sum(v*v for v in f2.values()))
|
14057
|
+
return_value = float(dot) / (n1 * n2) if n1 > 0 and n2 > 0 else 0.0
|
14058
|
+
return round(return_value, mantissa)
|
14059
|
+
|
13752
14060
|
@staticmethod
|
13753
14061
|
def XOR(graphA, graphB, vertexKeys, useCentroid: bool = False, tolerance: float = 0.001, silent: bool = False):
|
13754
14062
|
"""
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.8.
|
1
|
+
__version__ = '0.8.46'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.46
|
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=sPskW5bopbDzLz6MGKm8lN_OeyeAgsqdLvwwNcG0J3g,446
|
|
12
12
|
topologicpy/Edge.py,sha256=dLoAPuRKbjVg_dzloTgjRnQyv_05U9nfrtLO3tqyuys,74167
|
13
13
|
topologicpy/EnergyModel.py,sha256=Pyb28gDDwhzlQIH0xqAygqS0P3SJxWyyV7OWS_AAfRs,53856
|
14
14
|
topologicpy/Face.py,sha256=pN1fssyDLYWf1vU0NOBRx69DaUL958wRSxT-7VBCuCg,203184
|
15
|
-
topologicpy/Graph.py,sha256=
|
15
|
+
topologicpy/Graph.py,sha256=io5l8IV60q5ySrpPTid7TL0he4ouD9nJIoTU-erL6Z8,661704
|
16
16
|
topologicpy/Grid.py,sha256=EbI2NcYhQDpD5mItd7A1Lpr8Puuf87vZPWuoh7_gChQ,18483
|
17
17
|
topologicpy/Helper.py,sha256=qEsE4yaboEGW94q9lFCff0I_JwwTTQnDAFXw006yHaQ,31203
|
18
18
|
topologicpy/Honeybee.py,sha256=yctkwfdupKnp7bAOjP1Z4YaYpRrWoMEb4gz9Z5zaWwE,21751
|
@@ -30,9 +30,9 @@ topologicpy/Vector.py,sha256=X12eqskn28bdB7sLY1EZhq3noPYzPbNEgHPb4a959ss,42302
|
|
30
30
|
topologicpy/Vertex.py,sha256=RlGQnxQSb_kAus3tJgXd-v-Ptubtt09PQPA9IMwfXmI,84835
|
31
31
|
topologicpy/Wire.py,sha256=sJE8qwqYOomvN3snMWmj2P2-Sq25ul_OQ95YFz6DFUw,230553
|
32
32
|
topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
|
33
|
-
topologicpy/version.py,sha256=
|
34
|
-
topologicpy-0.8.
|
35
|
-
topologicpy-0.8.
|
36
|
-
topologicpy-0.8.
|
37
|
-
topologicpy-0.8.
|
38
|
-
topologicpy-0.8.
|
33
|
+
topologicpy/version.py,sha256=lWn332GY1e40h1ASpBjJjwG1eoQEgys8nvUCuPd_VMU,23
|
34
|
+
topologicpy-0.8.46.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
35
|
+
topologicpy-0.8.46.dist-info/METADATA,sha256=TJ0qyM1tswkGy-aJjUmdi5Zmn1BLH1RAoFV9iEi6EI8,10535
|
36
|
+
topologicpy-0.8.46.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
37
|
+
topologicpy-0.8.46.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
38
|
+
topologicpy-0.8.46.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|