File size: 2,776 Bytes
4818e0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import math
from collections import defaultdict


def load_triples(file_path):
    """Construct neighboring dictionaries containing isolated nodes (automatically includes all entities)"""
    adj = defaultdict(set)
    entities = set()  # Record all occurrences of the entity
    with open(file_path, 'r') as f:
        for line in f:
            parts = line.strip().split('\t')
            h = parts[0]
            t = parts[2]
            entities.update([h, t])
            adj[h].add(t)
            adj[t].add(h)
    # Ensure that isolated entities exist in the adjacency dictionary (empty neighbors)
    for e in entities:
        _ = adj[e]  # Trigger defaultdict to automatically create empty collection
    return adj


def load_ref_ent_ids(file_path):
    """Load alignments, no longer filtering any entities"""
    S_pairs = []
    with open(file_path, 'r') as f:
        for line in f:
            src, tgt = line.strip().split('\t')
            S_pairs.append((src, tgt))
    return S_pairs


def structure_similarity(kg1_adj, kg2_adj, S_pairs):
    """Improved structural similarity computation dealing with three neighbor cases"""
    if not S_pairs:
        return 0.0

    total_sim = 0.0
    m = len(S_pairs)
    S_src = [pair[0] for pair in S_pairs]
    S_tgt = [pair[1] for pair in S_pairs]

    for (i, j) in S_pairs:
        # Get the set of neighbors (all entities exist in the adjacency dictionary at this point)
        neighbors_i = kg1_adj[i]
        neighbors_j = kg2_adj[j]

        # Scenario 1: Neither party has a neighbor
        if not neighbors_i and not neighbors_j:
            sim = 1.0  # The structure is identical

        # Scenario 2: One of the parties has no neighbors
        elif not neighbors_i or not neighbors_j:
            sim = 0.0  # Complete mismatch in structure

        # Scenario 3: Neighbors on both sides
        else:
            # Constructing projection vectors based on alignment spaces
            v1 = [1 if x in neighbors_i else 0 for x in S_src]
            v2 = [1 if y in neighbors_j else 0 for y in S_tgt]

            # Calculate cosine similarity
            dot_product = sum(a * b for a, b in zip(v1, v2))
            norm_v1 = math.sqrt(sum(a ** 2 for a in v1))
            norm_v2 = math.sqrt(sum(b ** 2 for b in v2))

            sim = dot_product / (norm_v1 * norm_v2) if norm_v1 * norm_v2 != 0 else 0.0

        total_sim += sim

    return total_sim / m


if __name__ == '__main__':
    # Note: load_triples will now contain all entities
    kg1_adj = load_triples('triples_1')
    kg2_adj = load_triples('triples_2')
    aligned_pairs = load_ref_ent_ids('ref_ent_ids')

    similarity = structure_similarity(kg1_adj, kg2_adj, aligned_pairs)
    print(f"Structure Similarity: {similarity:.4f}")