topologicpy 0.8.19__py3-none-any.whl → 0.8.21__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 +378 -0
- topologicpy/version.py +1 -1
- {topologicpy-0.8.19.dist-info → topologicpy-0.8.21.dist-info}/METADATA +1 -1
- {topologicpy-0.8.19.dist-info → topologicpy-0.8.21.dist-info}/RECORD +7 -7
- {topologicpy-0.8.19.dist-info → topologicpy-0.8.21.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.19.dist-info → topologicpy-0.8.21.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.19.dist-info → topologicpy-0.8.21.dist-info}/top_level.txt +0 -0
topologicpy/Graph.py
CHANGED
@@ -4377,6 +4377,302 @@ class Graph:
|
|
4377
4377
|
v = Topology.SetDictionary(v, d)
|
4378
4378
|
return graph
|
4379
4379
|
|
4380
|
+
@staticmethod
|
4381
|
+
def Compare(graphA, graphB,
|
4382
|
+
weightAttributes: float = 1.0,
|
4383
|
+
weightGeometry: float = 1.0,
|
4384
|
+
weightMetrics: float = 1.0,
|
4385
|
+
weightStructure: float = 1.0,
|
4386
|
+
weightWL: float = 1.0,
|
4387
|
+
weightJaccard: float = 1.0,
|
4388
|
+
vertexIDKey: str = "id",
|
4389
|
+
edgeWeightKey: str = None,
|
4390
|
+
iterations: int = 3,
|
4391
|
+
mantissa: int = 6,
|
4392
|
+
silent: bool = False):
|
4393
|
+
"""
|
4394
|
+
Compares two graphs and returns a similarity score based on attributres, geometry, metrics, structure,
|
4395
|
+
the Weisfeiler-Lehman graph kernel. See https://en.wikipedia.org/wiki/Weisfeiler_Leman_graph_isomorphism_test
|
4396
|
+
, and the weight Jaccard Similarity. See https://www.statology.org/jaccard-similarity/
|
4397
|
+
|
4398
|
+
Parameters
|
4399
|
+
----------
|
4400
|
+
graphA : topologic Graph
|
4401
|
+
The first input graph.
|
4402
|
+
graphB : topologic Graph
|
4403
|
+
The second input graph.
|
4404
|
+
weightAttributes : float , optional
|
4405
|
+
The desired weight for attribute similarity (dictionary key overlap at vertices). Default is 1.0.
|
4406
|
+
weightGeometry : float , optional
|
4407
|
+
The desired weight for geometric similarity (vertex positions). Default is 1.0.
|
4408
|
+
weightMetrics : float , optional
|
4409
|
+
The desired weight for metric similarity (graph-level and node-level). Default is 1.0.
|
4410
|
+
The compared metrics are: betweenness centrality, closeness centrality, clustering coefficient, degree, diameter, and pagerank.
|
4411
|
+
weightStructure : float , optional
|
4412
|
+
The desired weight for structural similarity (number of vertices and edges). Default is 1.0.
|
4413
|
+
weightWL : float , optional
|
4414
|
+
The desired weight for Weisfeiler-Lehman kernel similarity (iterative label propagation). Default is 1.0.
|
4415
|
+
weightJaccard: float , optional
|
4416
|
+
The desired weight for the Weighted Jaccard similarity. Default is 1.0.
|
4417
|
+
vertexIDKey: str , optional
|
4418
|
+
The dictionary key under which to find the unique vertex ID. The default is "id".
|
4419
|
+
edgeWeightKey: str , optional
|
4420
|
+
The dictionary key under which to find the weight of the edge for weighted graphs.
|
4421
|
+
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
4422
|
+
The default is None which means all edges are treated as if they have a weight of 1.
|
4423
|
+
iterations : int , optional
|
4424
|
+
The desired number of Weisfeiler-Lehman iterations. Default is 3.
|
4425
|
+
mantissa : int , optional
|
4426
|
+
The desired length of the mantissa. The default is 6.
|
4427
|
+
silent : bool , optional
|
4428
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
4429
|
+
|
4430
|
+
Returns
|
4431
|
+
-------
|
4432
|
+
dict
|
4433
|
+
A dictionary of similarity scores between 0 (completely dissimilar) and 1 (identical), based on weighted components.
|
4434
|
+
The keys in the dictionary are:
|
4435
|
+
"attribute"
|
4436
|
+
"geometry"
|
4437
|
+
"metrics"
|
4438
|
+
"structure"
|
4439
|
+
"wl"
|
4440
|
+
"overall"
|
4441
|
+
|
4442
|
+
"""
|
4443
|
+
|
4444
|
+
import hashlib
|
4445
|
+
from collections import Counter
|
4446
|
+
from topologicpy.Graph import Graph
|
4447
|
+
from topologicpy.Vertex import Vertex
|
4448
|
+
from topologicpy.Topology import Topology
|
4449
|
+
from topologicpy.Dictionary import Dictionary
|
4450
|
+
|
4451
|
+
def attribute_similarity(graphA, graphB, mantissa=6):
|
4452
|
+
v1 = Graph.Vertices(graphA)
|
4453
|
+
v2 = Graph.Vertices(graphB)
|
4454
|
+
if len(v1) != len(v2) or len(v1) == 0:
|
4455
|
+
return 0
|
4456
|
+
|
4457
|
+
match_score = 0
|
4458
|
+
for a, b in zip(v1, v2):
|
4459
|
+
dict_a = Topology.Dictionary(a)
|
4460
|
+
dict_b = Topology.Dictionary(b)
|
4461
|
+
|
4462
|
+
keys_a = set(Dictionary.Keys(dict_a)) if dict_a else set()
|
4463
|
+
keys_b = set(Dictionary.Keys(dict_b)) if dict_b else set()
|
4464
|
+
|
4465
|
+
if not keys_a and not keys_b:
|
4466
|
+
match_score += 1
|
4467
|
+
else:
|
4468
|
+
intersection = len(keys_a & keys_b)
|
4469
|
+
union = len(keys_a | keys_b)
|
4470
|
+
match_score += intersection / union if union > 0 else 0
|
4471
|
+
|
4472
|
+
return round(match_score / len(v1), mantissa)
|
4473
|
+
|
4474
|
+
def geometry_similarity(graphA, graphB, mantissa=6):
|
4475
|
+
v1 = Graph.Vertices(graphA)
|
4476
|
+
v2 = Graph.Vertices(graphB)
|
4477
|
+
if len(v1) != len(v2) or len(v1) == 0:
|
4478
|
+
return 0
|
4479
|
+
|
4480
|
+
total_dist = 0
|
4481
|
+
for a, b in zip(v1, v2): # assumes same order
|
4482
|
+
p1 = Vertex.Coordinates(a, mantissa=mantissa)
|
4483
|
+
p2 = Vertex.Coordinates(b, mantissa=mantissa)
|
4484
|
+
total_dist += sum((i - j) ** 2 for i, j in zip(p1, p2)) ** 0.5
|
4485
|
+
avg_dist = total_dist / len(v1)
|
4486
|
+
return round(1 / (1 + avg_dist), mantissa) # Inverse average distance
|
4487
|
+
|
4488
|
+
def weighted_jaccard_similarity(graph1, graph2, vertexIDKey="id", edgeWeightKey=None, mantissa=6):
|
4489
|
+
"""
|
4490
|
+
Computes weighted Jaccard similarity between two graphs by comparing their edge weights.
|
4491
|
+
|
4492
|
+
Parameters
|
4493
|
+
----------
|
4494
|
+
graph1 : topologic Graph
|
4495
|
+
First graph.
|
4496
|
+
graph2 : topologic Graph
|
4497
|
+
Second graph.
|
4498
|
+
vertexIDKey: str , optional
|
4499
|
+
The dictionary key under which to find the unique vertex ID. The default is "id".
|
4500
|
+
edgeWeightKey: str , optional
|
4501
|
+
The dictionary key under which to find the weight of the edge for weighted graphs.
|
4502
|
+
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
4503
|
+
The default is None which means all edges are treated as if they have a weight of 1.
|
4504
|
+
iterations : int , optional
|
4505
|
+
The desired number of Weisfeiler-Lehman iterations. Default is 3.
|
4506
|
+
mantissa : int , optional
|
4507
|
+
The desired length of the mantissa. The default is 6.
|
4508
|
+
|
4509
|
+
Returns
|
4510
|
+
-------
|
4511
|
+
float
|
4512
|
+
Similarity score between 0 and 1.
|
4513
|
+
"""
|
4514
|
+
from topologicpy.Vertex import Vertex
|
4515
|
+
from topologicpy.Graph import Graph
|
4516
|
+
from topologicpy.Topology import Topology
|
4517
|
+
from topologicpy.Dictionary import Dictionary
|
4518
|
+
from topologicpy.Edge import Edge
|
4519
|
+
|
4520
|
+
|
4521
|
+
def edge_id(edge, vertexIDKey="id", mantissa=6):
|
4522
|
+
v1 = Edge.StartVertex(edge)
|
4523
|
+
v2 = Edge.EndVertex(edge)
|
4524
|
+
d1 = Topology.Dictionary(v1)
|
4525
|
+
d2 = Topology.Dictionary(v2)
|
4526
|
+
v1_id = Dictionary.ValueAtKey(d1, vertexIDKey) if d1 and Dictionary.ValueAtKey(d1, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v1, mantissa=mantissa)))
|
4527
|
+
v2_id = Dictionary.ValueAtKey(d2, vertexIDKey) if d2 and Dictionary.ValueAtKey(d2, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v2, mantissa=mantissa)))
|
4528
|
+
|
4529
|
+
return tuple(sorted(tuple([v1_id, v2_id])))
|
4530
|
+
|
4531
|
+
def edge_weights(graph, edgeWeightKey=None, mantissa=6):
|
4532
|
+
weights = {}
|
4533
|
+
for edge in Graph.Edges(graph):
|
4534
|
+
if edgeWeightKey == None:
|
4535
|
+
weight = 1
|
4536
|
+
elif edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance":
|
4537
|
+
weight = Edge.Length(edge)
|
4538
|
+
else:
|
4539
|
+
d = Topology.Dictionary(edge)
|
4540
|
+
weight = Dictionary.ValueAtKey(d, edgeWeightKey) if d and Dictionary.ValueAtKey(d, edgeWeightKey) is not None else 1.0
|
4541
|
+
eid = edge_id(edge, vertexIDKey=vertexIDKey, mantissa=mantissa)
|
4542
|
+
weights[eid] = weight
|
4543
|
+
return weights
|
4544
|
+
|
4545
|
+
w1 = edge_weights(graph1, edgeWeightKey=edgeWeightKey)
|
4546
|
+
w2 = edge_weights(graph2, edgeWeightKey=edgeWeightKey)
|
4547
|
+
keys = set(w1.keys()) | set(w2.keys())
|
4548
|
+
|
4549
|
+
numerator = sum(min(w1.get(k, 0), w2.get(k, 0)) for k in keys)
|
4550
|
+
denominator = sum(max(w1.get(k, 0), w2.get(k, 0)) for k in keys)
|
4551
|
+
|
4552
|
+
return numerator / denominator if denominator > 0 else 0.0
|
4553
|
+
|
4554
|
+
def metrics_similarity(graphA, graphB, mantissa=6):
|
4555
|
+
# Example using global metrics + mean of node metrics
|
4556
|
+
def safe_mean(lst):
|
4557
|
+
return sum(lst)/len(lst) if lst else 0
|
4558
|
+
|
4559
|
+
metrics1 = {
|
4560
|
+
"closeness": safe_mean(Graph.ClosenessCentrality(graphA)),
|
4561
|
+
"betweenness": safe_mean(Graph.BetweennessCentrality(graphA)),
|
4562
|
+
"degree": safe_mean(Graph.DegreeCentrality(graphA)),
|
4563
|
+
"pagerank": safe_mean(Graph.PageRank(graphA)),
|
4564
|
+
"clustering": Graph.GlobalClusteringCoefficient(graphA),
|
4565
|
+
"diameter": Graph.Diameter(graphA)
|
4566
|
+
}
|
4567
|
+
|
4568
|
+
metrics2 = {
|
4569
|
+
"closeness": safe_mean(Graph.ClosenessCentrality(graphB)),
|
4570
|
+
"betweenness": safe_mean(Graph.BetweennessCentrality(graphB)),
|
4571
|
+
"degree": safe_mean(Graph.DegreeCentrality(graphB)),
|
4572
|
+
"pagerank": safe_mean(Graph.PageRank(graphB)),
|
4573
|
+
"clustering": Graph.GlobalClusteringCoefficient(graphB),
|
4574
|
+
"diameter": Graph.Diameter(graphB)
|
4575
|
+
}
|
4576
|
+
|
4577
|
+
# Compute similarity as 1 - normalized absolute difference
|
4578
|
+
similarities = []
|
4579
|
+
for key in metrics1:
|
4580
|
+
v1, v2 = metrics1[key], metrics2[key]
|
4581
|
+
if v1 == 0 and v2 == 0:
|
4582
|
+
similarities.append(1)
|
4583
|
+
else:
|
4584
|
+
diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
|
4585
|
+
similarities.append(1 - diff)
|
4586
|
+
|
4587
|
+
return round(sum(similarities) / len(similarities), mantissa)
|
4588
|
+
|
4589
|
+
def structure_similarity(graphA, graphB, mantissa=6):
|
4590
|
+
v1 = Graph.Vertices(graphA)
|
4591
|
+
v2 = Graph.Vertices(graphB)
|
4592
|
+
e1 = Graph.Edges(graphA)
|
4593
|
+
e2 = Graph.Edges(graphB)
|
4594
|
+
|
4595
|
+
vertex_score = 1 - abs(len(v1) - len(v2)) / max(len(v1), len(v2), 1)
|
4596
|
+
edge_score = 1 - abs(len(e1) - len(e2)) / max(len(e1), len(e2), 1)
|
4597
|
+
|
4598
|
+
return round((vertex_score + edge_score) / 2, mantissa)
|
4599
|
+
|
4600
|
+
def weisfeiler_lehman_fingerprint(graph, iterations=3):
|
4601
|
+
vertices = Graph.Vertices(graph)
|
4602
|
+
labels = {}
|
4603
|
+
|
4604
|
+
for v in vertices:
|
4605
|
+
d = Topology.Dictionary(v)
|
4606
|
+
label = str(Dictionary.ValueAtKey(d, "label")) if d and Dictionary.ValueAtKey(d, "label") else "0"
|
4607
|
+
labels[v] = label
|
4608
|
+
|
4609
|
+
all_label_counts = Counter()
|
4610
|
+
|
4611
|
+
for _ in range(iterations):
|
4612
|
+
new_labels = {}
|
4613
|
+
for v in vertices:
|
4614
|
+
neighbors = Graph.AdjacentVertices(graph, v)
|
4615
|
+
neighbor_labels = sorted(labels.get(n, "0") for n in neighbors)
|
4616
|
+
long_label = labels[v] + "_" + "_".join(neighbor_labels)
|
4617
|
+
hashed_label = hashlib.md5(long_label.encode()).hexdigest()
|
4618
|
+
new_labels[v] = hashed_label
|
4619
|
+
all_label_counts[hashed_label] += 1
|
4620
|
+
labels = new_labels
|
4621
|
+
|
4622
|
+
return all_label_counts
|
4623
|
+
|
4624
|
+
def weisfeiler_lehman_similarity(graphA, graphB, iterations=3, mantissa=6):
|
4625
|
+
f1 = weisfeiler_lehman_fingerprint(graphA, iterations)
|
4626
|
+
f2 = weisfeiler_lehman_fingerprint(graphB, iterations)
|
4627
|
+
|
4628
|
+
common_labels = set(f1.keys()) & set(f2.keys())
|
4629
|
+
score = sum(min(f1[label], f2[label]) for label in common_labels)
|
4630
|
+
norm = max(sum(f1.values()), sum(f2.values()), 1)
|
4631
|
+
|
4632
|
+
return round(score / norm, mantissa)
|
4633
|
+
|
4634
|
+
if not Topology.IsInstance(graphA, "graph"):
|
4635
|
+
if not silent:
|
4636
|
+
print("Graph.Compare - Error: The graphA input parameter is not a valid topologic graph. Returning None.")
|
4637
|
+
return None
|
4638
|
+
if not Topology.IsInstance(graphB, "graph"):
|
4639
|
+
if not silent:
|
4640
|
+
print("Graph.Compare - Error: The graphB input parameter is not a valid topologic graph. Returning None.")
|
4641
|
+
return
|
4642
|
+
|
4643
|
+
total_weight = weightAttributes + weightGeometry + weightMetrics + weightStructure + weightWL + weightJaccard
|
4644
|
+
|
4645
|
+
attribute_score = attribute_similarity(graphA, graphB, mantissa=mantissa) if weightAttributes else 0
|
4646
|
+
geometry_score = geometry_similarity(graphA, graphB, mantissa=mantissa) if weightGeometry else 0
|
4647
|
+
jaccard_score = weighted_jaccard_similarity(graphA, graphB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa) if weightJaccard else 0
|
4648
|
+
metrics_score = metrics_similarity(graphA, graphB, mantissa=mantissa) if weightMetrics else 0
|
4649
|
+
structure_score = structure_similarity(graphA, graphB, mantissa=mantissa) if weightStructure else 0
|
4650
|
+
wl_score = weisfeiler_lehman_similarity(graphA, graphB, iterations, mantissa=mantissa) if weightWL else 0
|
4651
|
+
|
4652
|
+
weighted_sum = (
|
4653
|
+
attribute_score * weightAttributes +
|
4654
|
+
geometry_score * weightGeometry +
|
4655
|
+
jaccard_score * weightJaccard +
|
4656
|
+
metrics_score * weightMetrics +
|
4657
|
+
structure_score * weightStructure +
|
4658
|
+
wl_score * weightWL
|
4659
|
+
)
|
4660
|
+
|
4661
|
+
if total_weight <= 0:
|
4662
|
+
overall_score = 0
|
4663
|
+
else:
|
4664
|
+
overall_score = weighted_sum / total_weight
|
4665
|
+
|
4666
|
+
return {
|
4667
|
+
"attribute": round(attribute_score, mantissa),
|
4668
|
+
"geometry": round(geometry_score, mantissa),
|
4669
|
+
"jaccard": round(jaccard_score, mantissa),
|
4670
|
+
"metrics": round(metrics_score, mantissa),
|
4671
|
+
"structure": round(structure_score, mantissa),
|
4672
|
+
"wl": round(wl_score, mantissa),
|
4673
|
+
"overall": round(overall_score, mantissa)
|
4674
|
+
}
|
4675
|
+
|
4380
4676
|
@staticmethod
|
4381
4677
|
def Complement(graph, tolerance=0.0001, silent=False):
|
4382
4678
|
"""
|
@@ -10804,3 +11100,85 @@ class Graph:
|
|
10804
11100
|
g = Graph.ByVerticesEdges(final_vertices, final_edges)
|
10805
11101
|
return g
|
10806
11102
|
return None
|
11103
|
+
|
11104
|
+
@staticmethod
|
11105
|
+
def WeightedJaccardSimilarity(graphA, graphB, vertexA, vertexB, vertexIDKey="id", edgeWeightKey=None, mantissa=6, silent=False):
|
11106
|
+
"""
|
11107
|
+
Computes the weighted Jaccard similarity between two vertices based on their neighbors and
|
11108
|
+
edge weights. Accepts either one graph (both vertices are in the same graph) or two graphs
|
11109
|
+
(each vertex is in a separate graph).
|
11110
|
+
|
11111
|
+
Parameters
|
11112
|
+
----------
|
11113
|
+
graphA : topologic_core.Graph
|
11114
|
+
The first graph
|
11115
|
+
graphB : topologic_core.Graph
|
11116
|
+
The second graph (this can be the same as the first graph)
|
11117
|
+
vertexA : topologic_core.Vertex
|
11118
|
+
The first vertex.
|
11119
|
+
vertexB : topologic_core.Vertex
|
11120
|
+
The second vertex.
|
11121
|
+
vertexIDKey : str , optional
|
11122
|
+
The dictionary key under which to find the unique vertex ID. The default is "id".
|
11123
|
+
edgeWeightKey : str , optional
|
11124
|
+
The dictionary key under which to find the weight of the edge for weighted graphs.
|
11125
|
+
If this parameter is specified as "length" or "distance" then the length of the edge is used as its weight.
|
11126
|
+
The default is None which means all edges are treated as if they have a weight of 1.
|
11127
|
+
mantissa : int , optional
|
11128
|
+
The desired length of the mantissa. The default is 6.
|
11129
|
+
silent : bool , optional
|
11130
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
11131
|
+
|
11132
|
+
Returns
|
11133
|
+
-------
|
11134
|
+
float
|
11135
|
+
Weighted Jaccard similarity score between 0 (no overlap) and 1 (perfect match).
|
11136
|
+
|
11137
|
+
"""
|
11138
|
+
from topologicpy.Graph import Graph
|
11139
|
+
from topologicpy.Topology import Topology
|
11140
|
+
from topologicpy.Dictionary import Dictionary
|
11141
|
+
from topologicpy.Vertex import Vertex
|
11142
|
+
from topologicpy.Edge import Edge
|
11143
|
+
|
11144
|
+
if graphB == None:
|
11145
|
+
graphB = graphA
|
11146
|
+
|
11147
|
+
def edge_id(edge, vertexIDKey="id", mantissa=6):
|
11148
|
+
v1 = Edge.StartVertex(edge)
|
11149
|
+
v2 = Edge.EndVertex(edge)
|
11150
|
+
d1 = Topology.Dictionary(v1)
|
11151
|
+
d2 = Topology.Dictionary(v2)
|
11152
|
+
v1_id = Dictionary.ValueAtKey(d1, vertexIDKey) if d1 and Dictionary.ValueAtKey(d1, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v1, mantissa=mantissa)))
|
11153
|
+
v2_id = Dictionary.ValueAtKey(d2, vertexIDKey) if d2 and Dictionary.ValueAtKey(d2, vertexIDKey) is not None else str(sorted(Vertex.Coordinates(v2, mantissa=mantissa)))
|
11154
|
+
return tuple(sorted([v1_id, v2_id]))
|
11155
|
+
|
11156
|
+
def get_neighbors_with_weights(graph, vertex, vertexIDKey="id", edgeWeightKey=None, mantissa=6):
|
11157
|
+
weights = {}
|
11158
|
+
for edge in Graph.Edges(graph, [vertex]):
|
11159
|
+
eid = edge_id(edge, vertexIDKey=vertexIDKey, mantissa=mantissa)
|
11160
|
+
if edgeWeightKey == None:
|
11161
|
+
weight = 1.0
|
11162
|
+
elif edgeWeightKey.lower() == "length" or edgeWeightKey.lower() == "distance":
|
11163
|
+
weight = Edge.Length(edge, mantissa=mantissa)
|
11164
|
+
else:
|
11165
|
+
d = Topology.Dictionary(edge)
|
11166
|
+
d_weight = Dictionary.ValueAtKey(d, edgeWeightKey, silent=silent)
|
11167
|
+
if not d or d_weight is None:
|
11168
|
+
if not silent:
|
11169
|
+
print(f"Graph.WeightedJaccardSimilarity - Warning: The dictionary of edge {eid} is missing '{edgeWeightKey}' key. Defaulting the edge weight to 1.0.")
|
11170
|
+
weight = 1.0
|
11171
|
+
else:
|
11172
|
+
weight = d_weight
|
11173
|
+
weights[eid] = weight
|
11174
|
+
return weights
|
11175
|
+
|
11176
|
+
weights1 = get_neighbors_with_weights(graphA, vertexA, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa)
|
11177
|
+
weights2 = get_neighbors_with_weights(graphB, vertexB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa)
|
11178
|
+
|
11179
|
+
keys = set(weights1.keys()) | set(weights2.keys())
|
11180
|
+
|
11181
|
+
numerator = sum(min(weights1.get(k, 0), weights2.get(k, 0)) for k in keys)
|
11182
|
+
denominator = sum(max(weights1.get(k, 0), weights2.get(k, 0)) for k in keys)
|
11183
|
+
|
11184
|
+
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.21'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.21
|
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
|
@@ -11,7 +11,7 @@ topologicpy/Dictionary.py,sha256=7h-Gszgnt2OEOvOSADJ4pa-mTNlhQ9cuIiB5WHEW6aY,339
|
|
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=Iq2UI-CdW3usxpgxRi-caxQeUmkFfGhD37c6mPT2Gd8,523908
|
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=zZ7tNz8qiJpD43ZZY6gDLsQEo04V3AXFbZKZwwi4Pww,23
|
32
|
+
topologicpy-0.8.21.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
33
|
+
topologicpy-0.8.21.dist-info/METADATA,sha256=uin9-ogEkyhyjro0BGZPrFB4abynjZPuv7G3lTk6XzA,10535
|
34
|
+
topologicpy-0.8.21.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
35
|
+
topologicpy-0.8.21.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
36
|
+
topologicpy-0.8.21.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|