Graph Theory Foundations¶
This document explores how classical graph theory extends into the quantum realm within QE-KGR. Understanding these graph-theoretic concepts is essential for effectively working with quantum entangled knowledge graphs.
π Classical Graph Theory Recap¶
Basic Definitions¶
Graph: A mathematical structure G = (V, E) consisting of:
- V: Set of vertices (nodes)
- E: Set of edges (connections between nodes)
Types of Graphs:
- Undirected: Edges have no direction
- Directed: Edges have direction (source β target)
- Weighted: Edges have numerical weights
- Multigraph: Multiple edges between same node pair
import networkx as nx
import numpy as np
from qekgr import EntangledGraph
# Classical graph representation
classical_graph = nx.Graph()
classical_graph.add_nodes_from(['Alice', 'Bob', 'Charlie'])
classical_graph.add_edges_from([('Alice', 'Bob'), ('Bob', 'Charlie')])
print(f"Classical graph: {classical_graph.number_of_nodes()} nodes, {classical_graph.number_of_edges()} edges")
# Quantum graph representation
quantum_graph = EntangledGraph(hilbert_dim=4)
alice = quantum_graph.add_quantum_node("Alice", state="researcher")
bob = quantum_graph.add_quantum_node("Bob", state="professor")
charlie = quantum_graph.add_quantum_node("Charlie", state="student")
quantum_graph.add_entangled_edge(alice, bob, relations=["collaborates"], amplitudes=[0.8])
quantum_graph.add_entangled_edge(bob, charlie, relations=["mentors"], amplitudes=[0.9])
print(f"Quantum graph: {len(quantum_graph.nodes)} nodes, {len(quantum_graph.edges)} edges")
Graph Metrics¶
Fundamental Metrics:
- Degree: Number of edges connected to a node
- Path Length: Number of edges in shortest path between nodes
- Clustering Coefficient: Measure of local connectivity
- Centrality: Importance measures for nodes
def classical_graph_metrics(graph):
"""Calculate classical graph theory metrics."""
# Degree centrality
degree_centrality = nx.degree_centrality(graph)
# Betweenness centrality
betweenness = nx.betweenness_centrality(graph)
# Clustering coefficient
clustering = nx.clustering(graph)
# Average shortest path length
if nx.is_connected(graph):
avg_path_length = nx.average_shortest_path_length(graph)
else:
avg_path_length = float('inf')
return {
'degree_centrality': degree_centrality,
'betweenness': betweenness,
'clustering': clustering,
'avg_path_length': avg_path_length
}
# Example usage
metrics = classical_graph_metrics(classical_graph)
print(f"Degree centrality: {metrics['degree_centrality']}")
Adjacency Matrix Representation¶
Classical Adjacency Matrix: \(\(A_{ij} = \begin{cases} 1 & \text{if edge } (i,j) \text{ exists} \\ 0 & \text{otherwise} \end{cases}\)\)
Weighted Adjacency Matrix: \(\(W_{ij} = \text{weight of edge } (i,j)\)\)
def create_adjacency_matrix(graph_nodes, graph_edges):
"""Create adjacency matrix from graph structure."""
n = len(graph_nodes)
node_to_idx = {node: i for i, node in enumerate(graph_nodes)}
# Initialize adjacency matrix
adj_matrix = np.zeros((n, n))
# Fill matrix based on edges
for source, target in graph_edges:
i, j = node_to_idx[source], node_to_idx[target]
adj_matrix[i, j] = 1
adj_matrix[j, i] = 1 # Undirected graph
return adj_matrix, node_to_idx
# Example adjacency matrix
nodes = ['Alice', 'Bob', 'Charlie']
edges = [('Alice', 'Bob'), ('Bob', 'Charlie')]
adj_matrix, node_map = create_adjacency_matrix(nodes, edges)
print("Classical Adjacency Matrix:")
print(adj_matrix)
βοΈ Quantum Graph Theory¶
Quantum Adjacency Matrix¶
In quantum graphs, the adjacency matrix becomes complex-valued with quantum amplitudes:
where \(\alpha_k\) are amplitude weights and \(|r_k\rangle\) represent relation types.
def create_quantum_adjacency_matrix(quantum_graph):
"""Create quantum adjacency matrix with complex amplitudes."""
nodes = list(quantum_graph.nodes.keys())
n = len(nodes)
node_to_idx = {node: i for i, node in enumerate(nodes)}
# Complex adjacency matrix
quantum_adj = np.zeros((n, n), dtype=complex)
for (source, target), edge in quantum_graph.edges.items():
i, j = node_to_idx[source], node_to_idx[target]
# Quantum superposition of relations
total_amplitude = sum(edge.amplitudes)
quantum_adj[i, j] = total_amplitude
quantum_adj[j, i] = np.conj(total_amplitude) # Hermitian
return quantum_adj, node_to_idx
# Create quantum adjacency matrix
q_adj, q_node_map = create_quantum_adjacency_matrix(quantum_graph)
print("Quantum Adjacency Matrix:")
print(q_adj)
print(f"Is Hermitian: {np.allclose(q_adj, q_adj.conj().T)}")
Quantum Graph Laplacian¶
Classical Laplacian: \(L = D - A\) where D is degree matrix, A is adjacency matrix
Quantum Laplacian: \(\(\mathcal{L} = \mathcal{D} - \mathcal{A}\)\)
where \(\mathcal{D}\) and \(\mathcal{A}\) are quantum degree and adjacency matrices.
def quantum_laplacian(quantum_adj):
"""Compute quantum graph Laplacian."""
# Quantum degree matrix (diagonal)
degrees = np.sum(quantum_adj, axis=1)
quantum_degree = np.diag(degrees)
# Quantum Laplacian
laplacian = quantum_degree - quantum_adj
return laplacian
# Compute quantum Laplacian
q_laplacian = quantum_laplacian(q_adj)
print("Quantum Laplacian:")
print(q_laplacian)
# Eigenvalue analysis
eigenvals, eigenvecs = np.linalg.eigh(q_laplacian)
print(f"Laplacian eigenvalues: {eigenvals}")
Spectral Properties¶
Spectral Graph Theory: Study of graph properties through eigenvalues and eigenvectors
Key Properties: - Smallest eigenvalue: Always 0 for connected graphs - Second smallest eigenvalue (Fiedler value): Algebraic connectivity - Largest eigenvalue: Related to graph expansion
def spectral_analysis(adjacency_matrix):
"""Perform spectral analysis of graph."""
# Eigendecomposition
eigenvals, eigenvecs = np.linalg.eigh(adjacency_matrix)
# Sort by eigenvalue magnitude
idx = np.argsort(np.abs(eigenvals))[::-1]
eigenvals = eigenvals[idx]
eigenvecs = eigenvecs[:, idx]
# Spectral properties
spectral_radius = np.max(np.abs(eigenvals))
spectral_gap = np.abs(eigenvals[0]) - np.abs(eigenvals[1])
return {
'eigenvalues': eigenvals,
'eigenvectors': eigenvecs,
'spectral_radius': spectral_radius,
'spectral_gap': spectral_gap
}
# Analyze quantum graph spectrum
spectrum = spectral_analysis(q_adj)
print(f"Spectral radius: {spectrum['spectral_radius']:.3f}")
print(f"Spectral gap: {spectrum['spectral_gap']:.3f}")
πΆ Random Walks vs Quantum Walks¶
Classical Random Walk¶
Transition Matrix: \(P_{ij} = \frac{A_{ij}}{d_i}\) where \(d_i\) is degree of node \(i\)
Walk Evolution: \(\pi_t = P^t \pi_0\)
def classical_random_walk(adj_matrix, start_node, steps):
"""Simulate classical random walk on graph."""
n = adj_matrix.shape[0]
# Create transition matrix
degrees = np.sum(adj_matrix, axis=1)
degrees[degrees == 0] = 1 # Avoid division by zero
P = adj_matrix / degrees[:, np.newaxis]
# Initialize state vector
state = np.zeros(n)
state[start_node] = 1.0
# Evolve walk
states = [state.copy()]
for step in range(steps):
state = P.T @ state # Matrix-vector multiplication
states.append(state.copy())
return states
# Classical random walk example
classical_states = classical_random_walk(adj_matrix, 0, 10) # Start from Alice (index 0)
print(f"Classical walk final distribution: {classical_states[-1]}")
Quantum Walk¶
Quantum Evolution: \(|\psi_t\rangle = U^t |\psi_0\rangle\)
Unitary Operator: \(U = e^{-iH\tau}\) where H is Hamiltonian, Ο is time step
def quantum_walk_evolution(quantum_adj, start_node, steps, tau=0.1):
"""Simulate quantum walk evolution."""
n = quantum_adj.shape[0]
# Hamiltonian (use adjacency matrix)
H = quantum_adj
# Time evolution operator
U = expm(-1j * H * tau)
# Initialize quantum state
psi = np.zeros(n, dtype=complex)
psi[start_node] = 1.0
# Evolve quantum walk
quantum_states = [psi.copy()]
for step in range(steps):
psi = U @ psi
quantum_states.append(psi.copy())
return quantum_states, U
# Quantum walk example
from scipy.linalg import expm
quantum_states, U = quantum_walk_evolution(q_adj, 0, 10)
final_probabilities = np.abs(quantum_states[-1])**2
print(f"Quantum walk final probabilities: {final_probabilities}")
Comparing Classical vs Quantum Walks¶
def compare_walks(adj_matrix, quantum_adj, start_node, steps):
"""Compare classical and quantum walk spreading."""
# Classical random walk
classical_states = classical_random_walk(adj_matrix, start_node, steps)
classical_final = classical_states[-1]
# Quantum walk
quantum_states, _ = quantum_walk_evolution(quantum_adj, start_node, steps)
quantum_probs = [np.abs(state)**2 for state in quantum_states]
quantum_final = quantum_probs[-1]
# Spreading metrics
classical_entropy = -np.sum(classical_final * np.log2(classical_final + 1e-12))
quantum_entropy = -np.sum(quantum_final * np.log2(quantum_final + 1e-12))
return {
'classical_final': classical_final,
'quantum_final': quantum_final,
'classical_entropy': classical_entropy,
'quantum_entropy': quantum_entropy
}
# Compare walk behaviors
comparison = compare_walks(adj_matrix, q_adj, 0, 20)
print(f"Classical spreading entropy: {comparison['classical_entropy']:.3f}")
print(f"Quantum spreading entropy: {comparison['quantum_entropy']:.3f}")
π Graph Algorithms in Quantum Regime¶
Quantum Search on Graphs¶
Grover's Algorithm on Graphs: Quantum amplitude amplification for marked vertices
def quantum_graph_search(quantum_adj, marked_nodes, iterations):
"""Quantum search algorithm on graph structure."""
n = quantum_adj.shape[0]
# Initialize uniform superposition
psi = np.ones(n, dtype=complex) / np.sqrt(n)
# Oracle operator (marks target nodes)
oracle = np.eye(n, dtype=complex)
for node in marked_nodes:
oracle[node, node] = -1
# Diffusion operator
diffuser = 2 * np.outer(psi, psi.conj()) - np.eye(n)
# Quantum search iterations
for _ in range(iterations):
psi = oracle @ psi # Apply oracle
psi = diffuser @ psi # Apply diffusion
# Measurement probabilities
probabilities = np.abs(psi)**2
return probabilities, psi
# Search for specific nodes
marked = [1] # Search for Bob (index 1)
search_probs, search_state = quantum_graph_search(q_adj, marked, 3)
print(f"Search probabilities: {search_probs}")
print(f"Success probability: {search_probs[1]:.3f}")
Quantum Page Rank¶
Quantum Version of PageRank: Uses quantum walks for ranking
def quantum_pagerank(quantum_adj, damping=0.85, iterations=100):
"""Quantum PageRank algorithm using quantum walks."""
n = quantum_adj.shape[0]
# Quantum transition matrix
degrees = np.sum(np.abs(quantum_adj), axis=1)
degrees[degrees == 0] = 1
Q = quantum_adj / degrees[:, np.newaxis]
# Quantum PageRank operator
uniform = np.ones((n, n)) / n
M = damping * Q + (1 - damping) * uniform
# Power iteration with quantum states
rank_vector = np.ones(n, dtype=complex) / np.sqrt(n)
for _ in range(iterations):
rank_vector = M @ rank_vector
# Normalize
rank_vector = rank_vector / np.linalg.norm(rank_vector)
# Convert to probabilities
quantum_ranks = np.abs(rank_vector)**2
return quantum_ranks
# Quantum PageRank
q_ranks = quantum_pagerank(q_adj)
print(f"Quantum PageRank scores: {q_ranks}")
# Compare with classical PageRank
classical_ranks = nx.pagerank(classical_graph)
print(f"Classical PageRank: {classical_ranks}")
π Network Analysis in Quantum Graphs¶
Quantum Centrality Measures¶
Quantum Betweenness Centrality: Based on quantum path amplitudes
def quantum_betweenness_centrality(quantum_graph):
"""Calculate quantum betweenness centrality."""
nodes = list(quantum_graph.nodes.keys())
n = len(nodes)
betweenness = {node: 0 for node in nodes}
# For each pair of nodes
for i, source in enumerate(nodes):
for j, target in enumerate(nodes):
if i >= j: # Avoid double counting
continue
# Find quantum paths
paths = find_all_quantum_paths(quantum_graph, source, target, max_length=4)
if not paths:
continue
# Calculate total quantum amplitude
total_amplitude = 0
path_contributions = {}
for path in paths:
amplitude = calculate_quantum_path_amplitude(quantum_graph, path)
total_amplitude += amplitude
# Track intermediate nodes
for intermediate in path[1:-1]: # Exclude source and target
if intermediate not in path_contributions:
path_contributions[intermediate] = 0
path_contributions[intermediate] += amplitude
# Quantum betweenness contribution
if abs(total_amplitude) > 0:
for intermediate, contribution in path_contributions.items():
quantum_weight = abs(contribution / total_amplitude)**2
betweenness[intermediate] += quantum_weight
return betweenness
def find_all_quantum_paths(graph, source, target, max_length):
"""Find all quantum paths between source and target."""
# Use NetworkX for path finding (simplified)
classical_graph = graph._graph
try:
paths = list(nx.all_simple_paths(classical_graph, source, target, max_length))
return paths
except:
return []
def calculate_quantum_path_amplitude(graph, path):
"""Calculate quantum amplitude along a path."""
amplitude = 1.0 + 0j
for i in range(len(path) - 1):
edge_key = (path[i], path[i+1])
if edge_key in graph.edges:
edge = graph.edges[edge_key]
# Use first amplitude for simplicity
amplitude *= edge.amplitudes[0]
else:
amplitude *= 0.1 # Small amplitude for missing edges
return amplitude
# Calculate quantum betweenness
q_betweenness = quantum_betweenness_centrality(quantum_graph)
print(f"Quantum betweenness centrality: {q_betweenness}")
Quantum Clustering¶
Quantum Community Detection: Using quantum modularity
def quantum_modularity(quantum_adj, communities):
"""Calculate quantum modularity for community structure."""
n = quantum_adj.shape[0]
total_weight = np.sum(np.abs(quantum_adj))
if total_weight == 0:
return 0
modularity = 0
# For each community
for community in communities:
for i in community:
for j in community:
# Actual edge weight
actual = np.abs(quantum_adj[i, j])
# Expected weight (null model)
ki = np.sum(np.abs(quantum_adj[i, :]))
kj = np.sum(np.abs(quantum_adj[:, j]))
expected = ki * kj / total_weight
modularity += actual - expected
return modularity / total_weight
def quantum_community_detection(quantum_adj, method='spectral'):
"""Detect communities in quantum graph."""
if method == 'spectral':
# Spectral clustering on quantum Laplacian
laplacian = quantum_laplacian(quantum_adj)
eigenvals, eigenvecs = np.linalg.eigh(laplacian)
# Use Fiedler vector for bisection
fiedler_vector = eigenvecs[:, 1].real # Second smallest eigenvalue
# Split based on sign of Fiedler vector
community1 = np.where(fiedler_vector >= 0)[0]
community2 = np.where(fiedler_vector < 0)[0]
communities = [community1.tolist(), community2.tolist()]
return communities
# Detect quantum communities
communities = quantum_community_detection(q_adj)
modularity = quantum_modularity(q_adj, communities)
print(f"Detected communities: {communities}")
print(f"Quantum modularity: {modularity:.3f}")
π Geometric Properties¶
Quantum Graph Embeddings¶
Embedding in Euclidean Space: Map quantum nodes to geometric coordinates
def quantum_graph_embedding(quantum_adj, dimensions=2):
"""Embed quantum graph in Euclidean space."""
# Use quantum Laplacian eigenvectors for embedding
laplacian = quantum_laplacian(quantum_adj)
eigenvals, eigenvecs = np.linalg.eigh(laplacian)
# Use smallest non-zero eigenvalues for embedding
# Skip first eigenvector (constant)
embedding = eigenvecs[:, 1:dimensions+1].real
return embedding
# Create quantum embedding
embedding = quantum_graph_embedding(q_adj, dimensions=2)
print(f"Quantum embedding shape: {embedding.shape}")
print("Embedding coordinates:")
for i, node in enumerate(quantum_graph.nodes.keys()):
print(f" {node}: ({embedding[i, 0]:.3f}, {embedding[i, 1]:.3f})")
Quantum Graph Distances¶
Quantum Distance Measures:
def quantum_graph_distances(quantum_graph):
"""Calculate various quantum distance measures."""
nodes = list(quantum_graph.nodes.keys())
n = len(nodes)
# Quantum state overlap distances
overlap_distances = np.zeros((n, n))
for i, node1 in enumerate(nodes):
for j, node2 in enumerate(nodes):
if i != j:
state1 = quantum_graph.nodes[node1].state_vector
state2 = quantum_graph.nodes[node2].state_vector
# Quantum fidelity distance
fidelity = abs(np.vdot(state1, state2))**2
distance = 1 - fidelity
overlap_distances[i, j] = distance
return overlap_distances
# Calculate quantum distances
distances = quantum_graph_distances(quantum_graph)
print("Quantum state distances:")
print(distances)
π Dynamic Quantum Graphs¶
Temporal Evolution¶
Time-Dependent Quantum Graphs: Graphs that evolve over time
def evolve_quantum_graph(quantum_graph, time_steps, evolution_rate=0.1):
"""Simulate temporal evolution of quantum graph."""
# Store evolution history
evolution_history = []
for t in range(time_steps):
# Evolve quantum states
for node_id, node in quantum_graph.nodes.items():
# Simple random evolution (more sophisticated models possible)
noise = np.random.normal(0, evolution_rate, len(node.state_vector))
new_state = node.state_vector + noise * (1j if t % 2 else 1)
# Renormalize
new_state = new_state / np.linalg.norm(new_state)
node.state_vector = new_state
# Update density matrix
node.density_matrix = np.outer(new_state, new_state.conj())
# Evolve edge amplitudes
for edge_key, edge in quantum_graph.edges.items():
# Add phase evolution
phase_evolution = np.exp(1j * evolution_rate * np.random.uniform(-1, 1))
edge.amplitudes = [amp * phase_evolution for amp in edge.amplitudes]
# Record state
state_snapshot = {
'time': t,
'node_entropies': {node_id: node.measure_entropy()
for node_id, node in quantum_graph.nodes.items()},
'edge_strengths': {edge_key: edge.entanglement_strength
for edge_key, edge in quantum_graph.edges.items()}
}
evolution_history.append(state_snapshot)
return evolution_history
# Simulate evolution
evolution = evolve_quantum_graph(quantum_graph, time_steps=10)
print("Evolution of node entropies:")
for i, snapshot in enumerate(evolution[::2]): # Every other step
print(f" Time {snapshot['time']}: {snapshot['node_entropies']}")
Graph Growth Models¶
Quantum Preferential Attachment: Quantum version of BarabΓ‘si-Albert model
def quantum_preferential_attachment(initial_nodes, total_nodes, hilbert_dim=4):
"""Generate quantum graph using preferential attachment."""
graph = EntangledGraph(hilbert_dim=hilbert_dim)
# Initialize with small complete graph
for i in range(initial_nodes):
node_id = f"Node_{i}"
state = np.random.uniform(0, 1, hilbert_dim)
state = state / np.linalg.norm(state) # Normalize
graph.add_quantum_node(node_id, state=state)
# Connect initial nodes
for i in range(initial_nodes):
for j in range(i+1, initial_nodes):
amplitude = np.random.uniform(0.5, 1.0)
graph.add_entangled_edge(f"Node_{i}", f"Node_{j}",
relations=["connected"],
amplitudes=[amplitude])
# Add remaining nodes with quantum preferential attachment
for i in range(initial_nodes, total_nodes):
new_node_id = f"Node_{i}"
new_state = np.random.uniform(0, 1, hilbert_dim)
new_state = new_state / np.linalg.norm(new_state)
graph.add_quantum_node(new_node_id, state=new_state)
# Calculate quantum attachment probabilities
existing_nodes = [f"Node_{j}" for j in range(i)]
attachment_probs = []
for existing_node in existing_nodes:
# Quantum preference based on state overlap
existing_state = graph.nodes[existing_node].state_vector
overlap = abs(np.vdot(new_state, existing_state))**2
# Classical degree
degree = len([edge for edge in graph.edges if existing_node in edge])
# Combined quantum-classical preference
preference = 0.7 * overlap + 0.3 * (degree / len(graph.edges) if graph.edges else 0)
attachment_probs.append(preference)
# Normalize probabilities
if sum(attachment_probs) > 0:
attachment_probs = np.array(attachment_probs) / sum(attachment_probs)
# Select nodes to connect to
num_connections = min(2, len(existing_nodes)) # Connect to 2 nodes
selected_indices = np.random.choice(len(existing_nodes),
size=num_connections,
replace=False,
p=attachment_probs)
for idx in selected_indices:
target_node = existing_nodes[idx]
amplitude = np.random.uniform(0.5, 1.0)
graph.add_entangled_edge(new_node_id, target_node,
relations=["attached"],
amplitudes=[amplitude])
return graph
# Generate quantum scale-free network
quantum_network = quantum_preferential_attachment(initial_nodes=3, total_nodes=10)
print(f"Generated quantum network: {len(quantum_network.nodes)} nodes, {len(quantum_network.edges)} edges")
π Graph Visualization and Analysis¶
Quantum Graph Layout Algorithms¶
Force-directed layout with quantum forces:
def quantum_force_directed_layout(quantum_graph, iterations=100, k=1.0):
"""Force-directed layout considering quantum interactions."""
nodes = list(quantum_graph.nodes.keys())
n = len(nodes)
# Initialize random positions
positions = np.random.random((n, 2))
for iteration in range(iterations):
forces = np.zeros((n, 2))
# Calculate forces between all pairs
for i, node1 in enumerate(nodes):
for j, node2 in enumerate(nodes):
if i == j:
continue
# Position difference
diff = positions[i] - positions[j]
distance = np.linalg.norm(diff)
if distance < 1e-6:
diff = np.random.random(2) * 0.01
distance = np.linalg.norm(diff)
# Classical repulsive force
repulsive = k**2 / distance
force_direction = diff / distance
# Quantum attractive force (if connected)
attractive = 0
edge_key = (node1, node2)
if edge_key in quantum_graph.edges:
edge = quantum_graph.edges[edge_key]
quantum_strength = edge.entanglement_strength
attractive = quantum_strength * distance / k
elif (node2, node1) in quantum_graph.edges:
edge = quantum_graph.edges[(node2, node1)]
quantum_strength = edge.entanglement_strength
attractive = quantum_strength * distance / k
# Quantum state similarity force
state1 = quantum_graph.nodes[node1].state_vector
state2 = quantum_graph.nodes[node2].state_vector
similarity = abs(np.vdot(state1, state2))**2
similarity_force = similarity * 0.1
# Total force
total_force = (repulsive - attractive - similarity_force) * force_direction
forces[i] += total_force
# Update positions
positions += forces * 0.01
# Return as dictionary
return {node: positions[i] for i, node in enumerate(nodes)}
# Generate quantum layout
layout = quantum_force_directed_layout(quantum_graph)
print("Quantum force-directed layout:")
for node, pos in layout.items():
print(f" {node}: ({pos[0]:.3f}, {pos[1]:.3f})")
π― Applications and Extensions¶
Quantum Knowledge Graph Completion¶
Link Prediction using Quantum Interference:
def quantum_knowledge_completion(quantum_graph, target_relations):
"""Complete knowledge graph using quantum predictions."""
nodes= list(quantum_graph.nodes.keys())
predictions = []
# For each pair of unconnected nodes
for i, source in enumerate(nodes):
for j, target in enumerate(nodes[i+1:], i+1):
edge_key = (source, target)
reverse_key = (target, source)
# Skip if edge already exists
if edge_key in quantum_graph.edges or reverse_key in quantum_graph.edges:
continue
# Calculate quantum prediction score
score = calculate_quantum_link_probability(quantum_graph, source, target, target_relations)
if score > 0.3: # Threshold for prediction
predictions.append({
'source': source,
'target': target,
'predicted_relations': target_relations,
'quantum_score': score
})
return predictions
def calculate_quantum_link_probability(graph, source, target, relations):
"""Calculate quantum probability of link between nodes."""
# Quantum state similarity
state1 = graph.nodes[source].state_vector
state2 = graph.nodes[target].state_vector
state_similarity = abs(np.vdot(state1, state2))**2
# Path-based quantum interference
paths = find_all_quantum_paths(graph, source, target, max_length=3)
path_amplitude = 0
for path in paths:
amplitude = calculate_quantum_path_amplitude(graph, path)
path_amplitude += amplitude
path_score = abs(path_amplitude)**2 if paths else 0
# Combined score
combined_score = 0.6 * state_similarity + 0.4 * path_score
return combined_score
# Predict missing links
predictions = quantum_knowledge_completion(quantum_graph, ["related_to"])
print(f"Quantum predictions: {len(predictions)} potential links")
for pred in predictions[:3]: # Show top 3
print(f" {pred['source']} -> {pred['target']}: {pred['quantum_score']:.3f}")
This comprehensive foundation in graph theory provides the mathematical underpinnings for understanding how quantum mechanics enhances traditional graph-based knowledge representation. The quantum extensions enable richer modeling of uncertainty, correlation, and interference effects that are impossible to capture in classical graphs! πβοΈ