topologicpy 0.8.20__py3-none-any.whl → 0.8.22__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/Dictionary.py +6 -4
- topologicpy/Graph.py +283 -60
- topologicpy/version.py +1 -1
- {topologicpy-0.8.20.dist-info → topologicpy-0.8.22.dist-info}/METADATA +1 -1
- {topologicpy-0.8.20.dist-info → topologicpy-0.8.22.dist-info}/RECORD +8 -8
- {topologicpy-0.8.20.dist-info → topologicpy-0.8.22.dist-info}/WHEEL +1 -1
- {topologicpy-0.8.20.dist-info → topologicpy-0.8.22.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.20.dist-info → topologicpy-0.8.22.dist-info}/top_level.txt +0 -0
topologicpy/Dictionary.py
CHANGED
@@ -773,7 +773,7 @@ class Dictionary():
|
|
773
773
|
return None
|
774
774
|
|
775
775
|
@staticmethod
|
776
|
-
def ValueAtKey(dictionary, key, silent=False):
|
776
|
+
def ValueAtKey(dictionary, key, defaultValue=None, silent=False):
|
777
777
|
"""
|
778
778
|
Returns the value of the input key in the input dictionary.
|
779
779
|
|
@@ -783,6 +783,8 @@ class Dictionary():
|
|
783
783
|
The input dictionary.
|
784
784
|
key : string
|
785
785
|
The input key.
|
786
|
+
defaultValue : any , optional
|
787
|
+
The default value to return if the key or value are not found. The default is None.
|
786
788
|
silent : bool , optional
|
787
789
|
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
788
790
|
|
@@ -804,10 +806,10 @@ class Dictionary():
|
|
804
806
|
return None
|
805
807
|
if Topology.IsInstance(dictionary, "Dictionary"):
|
806
808
|
dic = Dictionary.PythonDictionary(dictionary)
|
807
|
-
return dic.get(key,
|
809
|
+
return dic.get(key, defaultValue)
|
808
810
|
elif isinstance(dictionary, dict):
|
809
|
-
return dictionary.get(key,
|
810
|
-
return
|
811
|
+
return dictionary.get(key, defaultValue)
|
812
|
+
return defaultValue
|
811
813
|
|
812
814
|
# if isinstance(dictionary, dict):
|
813
815
|
# attr = dictionary[key]
|
topologicpy/Graph.py
CHANGED
@@ -4379,17 +4379,26 @@ class Graph:
|
|
4379
4379
|
|
4380
4380
|
@staticmethod
|
4381
4381
|
def Compare(graphA, graphB,
|
4382
|
-
weightAttributes: float =
|
4383
|
-
weightGeometry: float =
|
4384
|
-
|
4385
|
-
|
4386
|
-
|
4382
|
+
weightAttributes: float = 0.0,
|
4383
|
+
weightGeometry: float = 0.0,
|
4384
|
+
weightBetwennessCentrality: float = 0.0,
|
4385
|
+
weightClosenessCentrality: float = 0.0,
|
4386
|
+
weightDegreeCentrality: float = 0.0,
|
4387
|
+
weightDiameter: float = 0.0,
|
4388
|
+
weightGlobalClusteringCoefficient: float = 0.0,
|
4389
|
+
weightPageRank: float = 0.0,
|
4390
|
+
weightStructure: float = 0.0,
|
4391
|
+
weightWeisfeilerLehman: float = 0.0,
|
4392
|
+
weightJaccard: float = 0.0,
|
4393
|
+
vertexIDKey: str = "id",
|
4394
|
+
edgeWeightKey: str = None,
|
4387
4395
|
iterations: int = 3,
|
4388
4396
|
mantissa: int = 6,
|
4389
4397
|
silent: bool = False):
|
4390
4398
|
"""
|
4391
|
-
Compares two graphs and returns a similarity score based on attributres, geometry, metrics, structure,
|
4399
|
+
Compares two graphs and returns a similarity score based on attributres, geometry, metrics, structure,
|
4392
4400
|
the Weisfeiler-Lehman graph kernel. See https://en.wikipedia.org/wiki/Weisfeiler_Leman_graph_isomorphism_test
|
4401
|
+
, and the weight Jaccard Similarity. See https://www.statology.org/jaccard-similarity/
|
4393
4402
|
|
4394
4403
|
Parameters
|
4395
4404
|
----------
|
@@ -4398,16 +4407,34 @@ class Graph:
|
|
4398
4407
|
graphB : topologic Graph
|
4399
4408
|
The second input graph.
|
4400
4409
|
weightAttributes : float , optional
|
4401
|
-
The desired weight for attribute similarity (dictionary key overlap at vertices). Default is
|
4410
|
+
The desired weight for attribute similarity (dictionary key overlap at vertices). Default is 0.0.
|
4411
|
+
weightBetwennessCentrality : float , optional
|
4412
|
+
The desired weight for betweenness centrality similarity (graph-level and node-level). Default is 0.0.
|
4413
|
+
weightClosenessCentrality : float , optional
|
4414
|
+
The desired weight for closeness centrality similarity (graph-level and node-level). Default is 0.0.
|
4415
|
+
weightDegreeCentrality : float , optional
|
4416
|
+
The desired weight for degree centrality similarity (graph-level and node-level). Default is 0.0.
|
4417
|
+
weightDiameter : float , optional
|
4418
|
+
The desired weight for diameter similarity (graph-level and node-level). Default is 0.0.
|
4402
4419
|
weightGeometry : float , optional
|
4403
|
-
The desired weight for geometric similarity (vertex positions). Default is
|
4404
|
-
|
4405
|
-
The desired weight for
|
4406
|
-
|
4420
|
+
The desired weight for geometric similarity (vertex positions). Default is 0.0.
|
4421
|
+
weightGlobalClusteringCoefficient : float , optional
|
4422
|
+
The desired weight for global clustering coefficient similarity (graph-level and node-level). Default is 0.0.
|
4423
|
+
weightJaccard: float , optional
|
4424
|
+
The desired weight for the Weighted Jaccard similarity. Default is 0.0.
|
4425
|
+
weightPageRank : float , optional
|
4426
|
+
The desired weight for PageRank similarity (graph-level and node-level). Default is 0.0.
|
4407
4427
|
weightStructure : float , optional
|
4408
|
-
The desired weight for structural similarity (number of vertices and edges). Default is
|
4409
|
-
|
4410
|
-
The desired weight for Weisfeiler-Lehman kernel similarity (iterative label propagation). Default is
|
4428
|
+
The desired weight for structural similarity (number of vertices and edges). Default is 0.0.
|
4429
|
+
weightWeisfeilerLehman : float , optional
|
4430
|
+
The desired weight for Weisfeiler-Lehman kernel similarity (iterative label propagation). Default is 0.0.
|
4431
|
+
|
4432
|
+
vertexIDKey: str , optional
|
4433
|
+
The dictionary key under which to find the unique vertex ID. The default is "id".
|
4434
|
+
edgeWeightKey: str , optional
|
4435
|
+
The dictionary key under which to find the weight of the edge for weighted graphs.
|
4436
|
+
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
4437
|
+
The default is None which means all edges are treated as if they have a weight of 1.
|
4411
4438
|
iterations : int , optional
|
4412
4439
|
The desired number of Weisfeiler-Lehman iterations. Default is 3.
|
4413
4440
|
mantissa : int , optional
|
@@ -4421,12 +4448,16 @@ class Graph:
|
|
4421
4448
|
A dictionary of similarity scores between 0 (completely dissimilar) and 1 (identical), based on weighted components.
|
4422
4449
|
The keys in the dictionary are:
|
4423
4450
|
"attribute"
|
4451
|
+
"betwenness_centrality"
|
4452
|
+
"closeness_centrality"
|
4453
|
+
"degree_centrality"
|
4424
4454
|
"geometry"
|
4425
|
-
"
|
4455
|
+
"global_clustering_coefficient"
|
4456
|
+
"jaccard"
|
4457
|
+
"pagerank"
|
4426
4458
|
"structure"
|
4427
|
-
"
|
4459
|
+
"weisfeiler_lehman"
|
4428
4460
|
"overall"
|
4429
|
-
|
4430
4461
|
"""
|
4431
4462
|
|
4432
4463
|
import hashlib
|
@@ -4473,40 +4504,122 @@ class Graph:
|
|
4473
4504
|
avg_dist = total_dist / len(v1)
|
4474
4505
|
return round(1 / (1 + avg_dist), mantissa) # Inverse average distance
|
4475
4506
|
|
4476
|
-
def
|
4477
|
-
|
4478
|
-
|
4479
|
-
|
4507
|
+
def weighted_jaccard_similarity(graph1, graph2, vertexIDKey="id", edgeWeightKey=None, mantissa=6):
|
4508
|
+
"""
|
4509
|
+
Computes weighted Jaccard similarity between two graphs by comparing their edge weights.
|
4510
|
+
|
4511
|
+
Parameters
|
4512
|
+
----------
|
4513
|
+
graph1 : topologic Graph
|
4514
|
+
First graph.
|
4515
|
+
graph2 : topologic Graph
|
4516
|
+
Second graph.
|
4517
|
+
vertexIDKey: str , optional
|
4518
|
+
The dictionary key under which to find the unique vertex ID. The default is "id".
|
4519
|
+
edgeWeightKey: str , optional
|
4520
|
+
The dictionary key under which to find the weight of the edge for weighted graphs.
|
4521
|
+
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
4522
|
+
The default is None which means all edges are treated as if they have a weight of 1.
|
4523
|
+
iterations : int , optional
|
4524
|
+
The desired number of Weisfeiler-Lehman iterations. Default is 3.
|
4525
|
+
mantissa : int , optional
|
4526
|
+
The desired length of the mantissa. The default is 6.
|
4527
|
+
|
4528
|
+
Returns
|
4529
|
+
-------
|
4530
|
+
float
|
4531
|
+
Similarity score between 0 and 1.
|
4532
|
+
"""
|
4533
|
+
from topologicpy.Vertex import Vertex
|
4534
|
+
from topologicpy.Graph import Graph
|
4535
|
+
from topologicpy.Topology import Topology
|
4536
|
+
from topologicpy.Dictionary import Dictionary
|
4537
|
+
from topologicpy.Edge import Edge
|
4480
4538
|
|
4481
|
-
metrics1 = {
|
4482
|
-
"closeness": safe_mean(Graph.ClosenessCentrality(graphA)),
|
4483
|
-
"betweenness": safe_mean(Graph.BetweennessCentrality(graphA)),
|
4484
|
-
"degree": safe_mean(Graph.DegreeCentrality(graphA)),
|
4485
|
-
"pagerank": safe_mean(Graph.PageRank(graphA)),
|
4486
|
-
"clustering": Graph.GlobalClusteringCoefficient(graphA),
|
4487
|
-
"diameter": Graph.Diameter(graphA)
|
4488
|
-
}
|
4489
|
-
|
4490
|
-
metrics2 = {
|
4491
|
-
"closeness": safe_mean(Graph.ClosenessCentrality(graphB)),
|
4492
|
-
"betweenness": safe_mean(Graph.BetweennessCentrality(graphB)),
|
4493
|
-
"degree": safe_mean(Graph.DegreeCentrality(graphB)),
|
4494
|
-
"pagerank": safe_mean(Graph.PageRank(graphB)),
|
4495
|
-
"clustering": Graph.GlobalClusteringCoefficient(graphB),
|
4496
|
-
"diameter": Graph.Diameter(graphB)
|
4497
|
-
}
|
4498
|
-
|
4499
|
-
# Compute similarity as 1 - normalized absolute difference
|
4500
|
-
similarities = []
|
4501
|
-
for key in metrics1:
|
4502
|
-
v1, v2 = metrics1[key], metrics2[key]
|
4503
|
-
if v1 == 0 and v2 == 0:
|
4504
|
-
similarities.append(1)
|
4505
|
-
else:
|
4506
|
-
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4507
|
-
similarities.append(1 - diff)
|
4508
4539
|
|
4509
|
-
|
4540
|
+
def edge_id(edge, vertexIDKey="id", mantissa=6):
|
4541
|
+
v1 = Edge.StartVertex(edge)
|
4542
|
+
v2 = Edge.EndVertex(edge)
|
4543
|
+
d1 = Topology.Dictionary(v1)
|
4544
|
+
d2 = Topology.Dictionary(v2)
|
4545
|
+
v1_id = Dictionary.ValueAtKey(d1, vertexIDKey) if d1 and Dictionary.ValueAtKey(d1, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v1, mantissa=mantissa)))
|
4546
|
+
v2_id = Dictionary.ValueAtKey(d2, vertexIDKey) if d2 and Dictionary.ValueAtKey(d2, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v2, mantissa=mantissa)))
|
4547
|
+
|
4548
|
+
return tuple(sorted(tuple([v1_id, v2_id])))
|
4549
|
+
|
4550
|
+
def edge_weights(graph, edgeWeightKey=None, mantissa=6):
|
4551
|
+
weights = {}
|
4552
|
+
for edge in Graph.Edges(graph):
|
4553
|
+
if edgeWeightKey == None:
|
4554
|
+
weight = 1
|
4555
|
+
elif edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance":
|
4556
|
+
weight = Edge.Length(edge)
|
4557
|
+
else:
|
4558
|
+
d = Topology.Dictionary(edge)
|
4559
|
+
weight = Dictionary.ValueAtKey(d, edgeWeightKey) if d and Dictionary.ValueAtKey(d, edgeWeightKey) is not None else 1.0
|
4560
|
+
eid = edge_id(edge, vertexIDKey=vertexIDKey, mantissa=mantissa)
|
4561
|
+
weights[eid] = weight
|
4562
|
+
return weights
|
4563
|
+
|
4564
|
+
w1 = edge_weights(graph1, edgeWeightKey=edgeWeightKey)
|
4565
|
+
w2 = edge_weights(graph2, edgeWeightKey=edgeWeightKey)
|
4566
|
+
keys = set(w1.keys()) | set(w2.keys())
|
4567
|
+
|
4568
|
+
numerator = sum(min(w1.get(k, 0), w2.get(k, 0)) for k in keys)
|
4569
|
+
denominator = sum(max(w1.get(k, 0), w2.get(k, 0)) for k in keys)
|
4570
|
+
|
4571
|
+
return numerator / denominator if denominator > 0 else 0.0
|
4572
|
+
|
4573
|
+
def safe_mean(lst):
|
4574
|
+
return sum(lst)/len(lst) if lst else 0
|
4575
|
+
|
4576
|
+
def betweenness_centrality_similarity(graphA, graphB, mantissa=6):
|
4577
|
+
v1 = safe_mean(Graph.BetweennessCentrality(graphA))
|
4578
|
+
v2 = safe_mean(Graph.BetweennessCentrality(graphB))
|
4579
|
+
if v1 == 0 and v2 == 0:
|
4580
|
+
return 1
|
4581
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4582
|
+
return round((1 - diff), mantissa)
|
4583
|
+
|
4584
|
+
def closeness_centrality_similarity(graphA, graphB, mantissa=6):
|
4585
|
+
v1 = safe_mean(Graph.ClosenessCentrality(graphA))
|
4586
|
+
v2 = safe_mean(Graph.ClosenessCentrality(graphB))
|
4587
|
+
if v1 == 0 and v2 == 0:
|
4588
|
+
return 1
|
4589
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4590
|
+
return round((1 - diff), mantissa)
|
4591
|
+
|
4592
|
+
def degree_centrality_similarity(graphA, graphB, mantissa=6):
|
4593
|
+
v1 = safe_mean(Graph.DegreeCentrality(graphA))
|
4594
|
+
v2 = safe_mean(Graph.DegreeCentrality(graphB))
|
4595
|
+
if v1 == 0 and v2 == 0:
|
4596
|
+
return 1
|
4597
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4598
|
+
return round((1 - diff), mantissa)
|
4599
|
+
|
4600
|
+
def diameter_similarity(graphA, graphB, mantissa=6):
|
4601
|
+
v1 = Graph.Diameter(graphA)
|
4602
|
+
v2 = Graph.Diameter(graphB)
|
4603
|
+
if v1 == 0 and v2 == 0:
|
4604
|
+
return 1
|
4605
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4606
|
+
return round((1 - diff), mantissa)
|
4607
|
+
|
4608
|
+
def global_clustering_coefficient_similarity(graphA, graphB, mantissa=6):
|
4609
|
+
v1 = Graph.GlobalClusteringCoefficient(graphA)
|
4610
|
+
v2 = Graph.GlobalClusteringCoefficient(graphB)
|
4611
|
+
if v1 == 0 and v2 == 0:
|
4612
|
+
return 1
|
4613
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4614
|
+
return round((1 - diff), mantissa)
|
4615
|
+
|
4616
|
+
def pagerank_similarity(graphA, graphB, mantissa=6):
|
4617
|
+
v1 = safe_mean(Graph.PageRank(graphA))
|
4618
|
+
v2 = safe_mean(Graph.PageRank(graphB))
|
4619
|
+
if v1 == 0 and v2 == 0:
|
4620
|
+
return 1
|
4621
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4622
|
+
return round((1 - diff), mantissa)
|
4510
4623
|
|
4511
4624
|
def structure_similarity(graphA, graphB, mantissa=6):
|
4512
4625
|
v1 = Graph.Vertices(graphA)
|
@@ -4562,20 +4675,42 @@ class Graph:
|
|
4562
4675
|
print("Graph.Compare - Error: The graphB input parameter is not a valid topologic graph. Returning None.")
|
4563
4676
|
return
|
4564
4677
|
|
4565
|
-
total_weight = weightAttributes
|
4678
|
+
total_weight = sum([weightAttributes,
|
4679
|
+
weightGeometry,
|
4680
|
+
weightBetwennessCentrality,
|
4681
|
+
weightClosenessCentrality,
|
4682
|
+
weightDegreeCentrality,
|
4683
|
+
weightDiameter,
|
4684
|
+
weightGlobalClusteringCoefficient,
|
4685
|
+
weightPageRank,
|
4686
|
+
weightStructure,
|
4687
|
+
weightWeisfeilerLehman,
|
4688
|
+
weightJaccard])
|
4566
4689
|
|
4567
4690
|
attribute_score = attribute_similarity(graphA, graphB, mantissa=mantissa) if weightAttributes else 0
|
4691
|
+
betweenness_centrality_score = betweenness_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightBetwennessCentrality else 0
|
4692
|
+
closeness_centrality_score = closeness_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightClosenessCentrality else 0
|
4693
|
+
degree_centrality_score = degree_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightDegreeCentrality else 0
|
4694
|
+
diameter_score = diameter_similarity(graphA, graphB, mantissa=mantissa) if weightDiameter else 0
|
4695
|
+
global_clustering_coefficient_score = global_clustering_coefficient_similarity(graphA, graphB, mantissa=mantissa) if weightGlobalClusteringCoefficient else 0
|
4568
4696
|
geometry_score = geometry_similarity(graphA, graphB, mantissa=mantissa) if weightGeometry else 0
|
4569
|
-
|
4697
|
+
jaccard_score = weighted_jaccard_similarity(graphA, graphB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa) if weightJaccard else 0
|
4698
|
+
pagerank_score = pagerank_similarity(graphA, graphB, mantissa=mantissa) if weightPageRank else 0
|
4570
4699
|
structure_score = structure_similarity(graphA, graphB, mantissa=mantissa) if weightStructure else 0
|
4571
|
-
|
4700
|
+
weisfeiler_lehman_score = weisfeiler_lehman_similarity(graphA, graphB, iterations, mantissa=mantissa) if weightWeisfeilerLehman else 0
|
4572
4701
|
|
4573
4702
|
weighted_sum = (
|
4574
4703
|
attribute_score * weightAttributes +
|
4704
|
+
betweenness_centrality_score * weightBetwennessCentrality +
|
4705
|
+
closeness_centrality_score * weightClosenessCentrality +
|
4706
|
+
degree_centrality_score * weightDegreeCentrality +
|
4707
|
+
diameter_score * weightDiameter +
|
4575
4708
|
geometry_score * weightGeometry +
|
4576
|
-
|
4709
|
+
global_clustering_coefficient_score * weightGlobalClusteringCoefficient +
|
4710
|
+
jaccard_score * weightJaccard +
|
4711
|
+
pagerank_score * weightPageRank +
|
4577
4712
|
structure_score * weightStructure +
|
4578
|
-
|
4713
|
+
weisfeiler_lehman_score * weightWeisfeilerLehman
|
4579
4714
|
)
|
4580
4715
|
|
4581
4716
|
if total_weight <= 0:
|
@@ -4583,12 +4718,18 @@ class Graph:
|
|
4583
4718
|
else:
|
4584
4719
|
overall_score = weighted_sum / total_weight
|
4585
4720
|
|
4586
|
-
return {
|
4587
|
-
|
4588
|
-
|
4589
|
-
|
4590
|
-
|
4591
|
-
|
4721
|
+
return {
|
4722
|
+
"attribute": round(attribute_score, mantissa),
|
4723
|
+
"betwenness_centrality": round(betweenness_centrality_score, mantissa),
|
4724
|
+
"closeness_centrality": round(closeness_centrality_score, mantissa),
|
4725
|
+
"degree_centrality": round(degree_centrality_score, mantissa),
|
4726
|
+
"geometry": round(geometry_score, mantissa),
|
4727
|
+
"global_clustering_coefficient": round(global_clustering_coefficient_score, mantissa),
|
4728
|
+
"jaccard": round(jaccard_score, mantissa),
|
4729
|
+
"pagerank": round(pagerank_score, mantissa),
|
4730
|
+
"structure": round(structure_score, mantissa),
|
4731
|
+
"weisfeiler_lehman": round(weisfeiler_lehman_score, mantissa),
|
4732
|
+
"overall": round(overall_score, mantissa)
|
4592
4733
|
}
|
4593
4734
|
|
4594
4735
|
@staticmethod
|
@@ -11018,3 +11159,85 @@ class Graph:
|
|
11018
11159
|
g = Graph.ByVerticesEdges(final_vertices, final_edges)
|
11019
11160
|
return g
|
11020
11161
|
return None
|
11162
|
+
|
11163
|
+
@staticmethod
|
11164
|
+
def WeightedJaccardSimilarity(graphA, graphB, vertexA, vertexB, vertexIDKey="id", edgeWeightKey=None, mantissa=6, silent=False):
|
11165
|
+
"""
|
11166
|
+
Computes the weighted Jaccard similarity between two vertices based on their neighbors and
|
11167
|
+
edge weights. Accepts either one graph (both vertices are in the same graph) or two graphs
|
11168
|
+
(each vertex is in a separate graph).
|
11169
|
+
|
11170
|
+
Parameters
|
11171
|
+
----------
|
11172
|
+
graphA : topologic_core.Graph
|
11173
|
+
The first graph
|
11174
|
+
graphB : topologic_core.Graph
|
11175
|
+
The second graph (this can be the same as the first graph)
|
11176
|
+
vertexA : topologic_core.Vertex
|
11177
|
+
The first vertex.
|
11178
|
+
vertexB : topologic_core.Vertex
|
11179
|
+
The second vertex.
|
11180
|
+
vertexIDKey : str , optional
|
11181
|
+
The dictionary key under which to find the unique vertex ID. The default is "id".
|
11182
|
+
edgeWeightKey : str , optional
|
11183
|
+
The dictionary key under which to find the weight of the edge for weighted graphs.
|
11184
|
+
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
11185
|
+
The default is None which means all edges are treated as if they have a weight of 1.
|
11186
|
+
mantissa : int , optional
|
11187
|
+
The desired length of the mantissa. The default is 6.
|
11188
|
+
silent : bool , optional
|
11189
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
11190
|
+
|
11191
|
+
Returns
|
11192
|
+
-------
|
11193
|
+
float
|
11194
|
+
Weighted Jaccard similarity score between 0 (no overlap) and 1 (perfect match).
|
11195
|
+
|
11196
|
+
"""
|
11197
|
+
from topologicpy.Graph import Graph
|
11198
|
+
from topologicpy.Topology import Topology
|
11199
|
+
from topologicpy.Dictionary import Dictionary
|
11200
|
+
from topologicpy.Vertex import Vertex
|
11201
|
+
from topologicpy.Edge import Edge
|
11202
|
+
|
11203
|
+
if graphB == None:
|
11204
|
+
graphB = graphA
|
11205
|
+
|
11206
|
+
def edge_id(edge, vertexIDKey="id", mantissa=6):
|
11207
|
+
v1 = Edge.StartVertex(edge)
|
11208
|
+
v2 = Edge.EndVertex(edge)
|
11209
|
+
d1 = Topology.Dictionary(v1)
|
11210
|
+
d2 = Topology.Dictionary(v2)
|
11211
|
+
v1_id = Dictionary.ValueAtKey(d1, vertexIDKey) if d1 and Dictionary.ValueAtKey(d1, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v1, mantissa=mantissa)))
|
11212
|
+
v2_id = Dictionary.ValueAtKey(d2, vertexIDKey) if d2 and Dictionary.ValueAtKey(d2, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v2, mantissa=mantissa)))
|
11213
|
+
return tuple(sorted([v1_id, v2_id]))
|
11214
|
+
|
11215
|
+
def get_neighbors_with_weights(graph, vertex, vertexIDKey="id", edgeWeightKey=None, mantissa=6):
|
11216
|
+
weights = {}
|
11217
|
+
for edge in Graph.Edges(graph, [vertex]):
|
11218
|
+
eid = edge_id(edge, vertexIDKey=vertexIDKey, mantissa=mantissa)
|
11219
|
+
if edgeWeightKey == None:
|
11220
|
+
weight = 1.0
|
11221
|
+
elif edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance":
|
11222
|
+
weight = Edge.Length(edge, mantissa=mantissa)
|
11223
|
+
else:
|
11224
|
+
d = Topology.Dictionary(edge)
|
11225
|
+
d_weight = Dictionary.ValueAtKey(d, edgeWeightKey, silent=silent)
|
11226
|
+
if not d or d_weight is None:
|
11227
|
+
if not silent:
|
11228
|
+
print(f"Graph.WeightedJaccardSimilarity - Warning: The dictionary of edge {eid} is missing '{edgeWeightKey}' key. Defaulting the edge weight to 1.0.")
|
11229
|
+
weight = 1.0
|
11230
|
+
else:
|
11231
|
+
weight = d_weight
|
11232
|
+
weights[eid] = weight
|
11233
|
+
return weights
|
11234
|
+
|
11235
|
+
weights1 = get_neighbors_with_weights(graphA, vertexA, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa)
|
11236
|
+
weights2 = get_neighbors_with_weights(graphB, vertexB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa)
|
11237
|
+
|
11238
|
+
keys = set(weights1.keys()) | set(weights2.keys())
|
11239
|
+
|
11240
|
+
numerator = sum(min(weights1.get(k, 0), weights2.get(k, 0)) for k in keys)
|
11241
|
+
denominator = sum(max(weights1.get(k, 0), weights2.get(k, 0)) for k in keys)
|
11242
|
+
|
11243
|
+
return round(numerator / denominator, mantissa) if denominator != 0 else 0.0
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.8.
|
1
|
+
__version__ = '0.8.22'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.22
|
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
|
@@ -7,11 +7,11 @@ topologicpy/Cluster.py,sha256=o5jdMRpcGfSGGiXQdFg-e9XcnBF5AqTj3xb1nSpwJWE,58606
|
|
7
7
|
topologicpy/Color.py,sha256=q9xsGmxFMz7sQKmygwSVS12GaTRB-OT0-_i6t3-cthE,20307
|
8
8
|
topologicpy/Context.py,sha256=ppApYKngZZCQBFWaxIMi2z2dokY23c935IDCBosxDAE,3055
|
9
9
|
topologicpy/DGL.py,sha256=M_znFtyPBJJz-iXLYZs2wwBj24fhevIo739dGha0chM,139041
|
10
|
-
topologicpy/Dictionary.py,sha256=
|
10
|
+
topologicpy/Dictionary.py,sha256=TO5Ar9j_xUSV325MYbUHdNrKfgo9VcehcZCeZWAn6b0,34126
|
11
11
|
topologicpy/Edge.py,sha256=yxkCVDYBflJNEYxnjMmlyvbkpg8TNy7y5bSH3yQ4jzs,71418
|
12
12
|
topologicpy/EnergyModel.py,sha256=UoQ9Jm-hYsN383CbcLKw-y6BKitRHj0uyh84yQ-8ACg,53856
|
13
13
|
topologicpy/Face.py,sha256=SlhB8L7BpDjd4a9YZE4UJ3zoGuF1oq9MSpuesEWro_Q,184847
|
14
|
-
topologicpy/Graph.py,sha256=
|
14
|
+
topologicpy/Graph.py,sha256=c2VvEvEM3hGgFR6xP-A58HXcgEVnPyA_heFOoNZ4p_Y,527640
|
15
15
|
topologicpy/Grid.py,sha256=2s9cSlWldivn1i9EUz4OOokJyANveqmRe_vR93CAndI,18245
|
16
16
|
topologicpy/Helper.py,sha256=4H5KPiv_eiEs489UOOyGLe9RaeoZIfmMh3mk_YCHmXg,29100
|
17
17
|
topologicpy/Honeybee.py,sha256=uDVtDbloydNoaBFcSNukKL_2PLyD6XKkCp1VHz1jtaU,21751
|
@@ -28,9 +28,9 @@ topologicpy/Vector.py,sha256=GkGt-aJ591IJ2IPffMAudvITLDPi2qZibZc4UAav6m8,42407
|
|
28
28
|
topologicpy/Vertex.py,sha256=q99IrWwdNlvVfUXz6iP8icmP8NzP2nsiqtgnF9Jnpx8,80913
|
29
29
|
topologicpy/Wire.py,sha256=IVPzBKsckuxC-rHvwxmvtVF7PpApz2lhPHomtQMo_kg,228499
|
30
30
|
topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
|
31
|
-
topologicpy/version.py,sha256=
|
32
|
-
topologicpy-0.8.
|
33
|
-
topologicpy-0.8.
|
34
|
-
topologicpy-0.8.
|
35
|
-
topologicpy-0.8.
|
36
|
-
topologicpy-0.8.
|
31
|
+
topologicpy/version.py,sha256=SuLneBfTbTDP--cc9ZcmXl_eRJruqxfxMHpHGiHHHZA,23
|
32
|
+
topologicpy-0.8.22.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
33
|
+
topologicpy-0.8.22.dist-info/METADATA,sha256=9_VzttjULYoYaVn35tAzKn6Q7cf2l4mfnGKYkOE_tFo,10535
|
34
|
+
topologicpy-0.8.22.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
|
35
|
+
topologicpy-0.8.22.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
36
|
+
topologicpy-0.8.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|