sly-quantum-walks 1.0.2__tar.gz
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.
- sly_quantum_walks-1.0.2/PKG-INFO +38 -0
- sly_quantum_walks-1.0.2/README.md +27 -0
- sly_quantum_walks-1.0.2/pyproject.toml +18 -0
- sly_quantum_walks-1.0.2/setup.cfg +4 -0
- sly_quantum_walks-1.0.2/src/quantum_walks/__init__.py +63 -0
- sly_quantum_walks-1.0.2/src/quantum_walks/graphs.py +330 -0
- sly_quantum_walks-1.0.2/src/quantum_walks/lattice.py +195 -0
- sly_quantum_walks-1.0.2/src/sly_quantum_walks.egg-info/PKG-INFO +38 -0
- sly_quantum_walks-1.0.2/src/sly_quantum_walks.egg-info/SOURCES.txt +10 -0
- sly_quantum_walks-1.0.2/src/sly_quantum_walks.egg-info/dependency_links.txt +1 -0
- sly_quantum_walks-1.0.2/src/sly_quantum_walks.egg-info/requires.txt +3 -0
- sly_quantum_walks-1.0.2/src/sly_quantum_walks.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sly-quantum-walks
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A unified Quantum Walk simulation package
|
|
5
|
+
Project-URL: Homepage, https://example.com/quantum_walks
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: matplotlib
|
|
10
|
+
Requires-Dist: networkx
|
|
11
|
+
|
|
12
|
+
# Quantum Walks Package
|
|
13
|
+
|
|
14
|
+
This package provides a unified interface for various Quantum Walk simulations.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install quantumwalks
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import quantumwalks as qw
|
|
26
|
+
|
|
27
|
+
# 1D/2D/3D Lattice Walks
|
|
28
|
+
qw.run_simulation('1d', steps=100)
|
|
29
|
+
qw.run_simulation('2d', steps=50)
|
|
30
|
+
qw.run_simulation('3d', steps=20)
|
|
31
|
+
|
|
32
|
+
# Graph Walks
|
|
33
|
+
qw.run_simulation('ranking', file_path='graph.edgelist')
|
|
34
|
+
qw.run_simulation('pagerank')
|
|
35
|
+
qw.run_simulation('hadamard')
|
|
36
|
+
qw.run_simulation('szegedy')
|
|
37
|
+
qw.run_simulation('community')
|
|
38
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Quantum Walks Package
|
|
2
|
+
|
|
3
|
+
This package provides a unified interface for various Quantum Walk simulations.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install quantumwalks
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import quantumwalks as qw
|
|
15
|
+
|
|
16
|
+
# 1D/2D/3D Lattice Walks
|
|
17
|
+
qw.run_simulation('1d', steps=100)
|
|
18
|
+
qw.run_simulation('2d', steps=50)
|
|
19
|
+
qw.run_simulation('3d', steps=20)
|
|
20
|
+
|
|
21
|
+
# Graph Walks
|
|
22
|
+
qw.run_simulation('ranking', file_path='graph.edgelist')
|
|
23
|
+
qw.run_simulation('pagerank')
|
|
24
|
+
qw.run_simulation('hadamard')
|
|
25
|
+
qw.run_simulation('szegedy')
|
|
26
|
+
qw.run_simulation('community')
|
|
27
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sly-quantum-walks"
|
|
7
|
+
version = "1.0.2"
|
|
8
|
+
description = "A unified Quantum Walk simulation package"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"numpy",
|
|
13
|
+
"matplotlib",
|
|
14
|
+
"networkx"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
"Homepage" = "https://example.com/quantum_walks"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from .lattice import LatticeQuantumWalk
|
|
2
|
+
from .graphs import run_chawla_ranking, run_szegedy_service, run_community_detection, run_quantum_pagerank, run_hadamard
|
|
3
|
+
|
|
4
|
+
def run_simulation(model: str, **kwargs):
|
|
5
|
+
"""
|
|
6
|
+
Unified entry point for Quantum Walk simulations.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
model (str): The type of simulation to run.
|
|
10
|
+
Options: '1d', '2d', '3d' (Lattice Walks)
|
|
11
|
+
'ranking' (Chawla Directed Ranking)
|
|
12
|
+
'pagerank' (Quantum PageRank)
|
|
13
|
+
'hadamard' (Hadamard Edge Importance)
|
|
14
|
+
'szegedy' (Szegedy Walk)
|
|
15
|
+
'community' (Community Detection)
|
|
16
|
+
**kwargs: Parameters specific to the chosen model.
|
|
17
|
+
"""
|
|
18
|
+
model = model.lower()
|
|
19
|
+
|
|
20
|
+
# Lattice Walks
|
|
21
|
+
if model in ['1d', '2d', '3d']:
|
|
22
|
+
dim_map = {'1d': 1, '2d': 2, '3d': 3}
|
|
23
|
+
dimension = dim_map[model]
|
|
24
|
+
network_size = kwargs.get('network_size', 100 if dimension==1 else 20 if dimension==2 else 25)
|
|
25
|
+
steps = kwargs.get('steps', 100)
|
|
26
|
+
initial_dist = kwargs.get('initial_dist', 'uniform')
|
|
27
|
+
compare_classical = kwargs.get('compare_classical', False)
|
|
28
|
+
if dimension in [1, 2] and 'compare_classical' not in kwargs: compare_classical = True
|
|
29
|
+
|
|
30
|
+
qw = LatticeQuantumWalk(dimension=dimension, network_size=network_size)
|
|
31
|
+
qw.run(steps=steps, initial_dist=initial_dist, compare_classical=compare_classical)
|
|
32
|
+
qw.visualize()
|
|
33
|
+
return qw.results
|
|
34
|
+
|
|
35
|
+
# Graph Walks
|
|
36
|
+
elif model == 'ranking':
|
|
37
|
+
return run_chawla_ranking(file_path=kwargs.get('file_path'),
|
|
38
|
+
steps=kwargs.get('steps', 100),
|
|
39
|
+
coin_type=kwargs.get('coin_type', 'fourier'))
|
|
40
|
+
|
|
41
|
+
elif model == 'szegedy':
|
|
42
|
+
return run_szegedy_service(G=kwargs.get('G'),
|
|
43
|
+
steps=kwargs.get('steps', 10),
|
|
44
|
+
initial_type=kwargs.get('initial_type', 'uniform'),
|
|
45
|
+
initial_node=kwargs.get('initial_node'))
|
|
46
|
+
|
|
47
|
+
elif model == 'community':
|
|
48
|
+
return run_community_detection(G=kwargs.get('G'),
|
|
49
|
+
initial_state=kwargs.get('initial_state', 'uniform'),
|
|
50
|
+
coin_type=kwargs.get('coin_type', 'fourier'))
|
|
51
|
+
|
|
52
|
+
elif model == 'pagerank':
|
|
53
|
+
return run_quantum_pagerank(file_path=kwargs.get('file_path'),
|
|
54
|
+
steps=kwargs.get('steps', 20),
|
|
55
|
+
alpha=kwargs.get('alpha', 0.85),
|
|
56
|
+
initial_type=kwargs.get('initial_type', 'uniform'))
|
|
57
|
+
|
|
58
|
+
elif model == 'hadamard':
|
|
59
|
+
return run_hadamard(steps=kwargs.get('steps', 100),
|
|
60
|
+
top_p=kwargs.get('top_p', 0.05))
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
raise ValueError(f"Unknown model type: {model}. Available: 1d, 2d, 3d, ranking, pagerank, hadamard, szegedy, community")
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import networkx as nx
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from matplotlib.colors import Normalize
|
|
5
|
+
from matplotlib.cm import ScalarMappable
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
def run_chawla_ranking(file_path=None, steps=100, coin_type='fourier'):
|
|
9
|
+
"""
|
|
10
|
+
Discrete-time quantum walk algorithm for ranking nodes on a network
|
|
11
|
+
Chawla, P ; Mangal, R ; Chandrashekar, CM
|
|
12
|
+
QUANTUM INFORMATION PROCESSING
|
|
13
|
+
19(5): 158, 2020.
|
|
14
|
+
"""
|
|
15
|
+
if file_path and os.path.exists(file_path):
|
|
16
|
+
G = nx.read_edgelist(file_path, create_using=nx.DiGraph())
|
|
17
|
+
else:
|
|
18
|
+
G = nx.gnp_random_graph(15, 0.2, directed=True, seed=42)
|
|
19
|
+
|
|
20
|
+
nodes = list(G.nodes())
|
|
21
|
+
N = len(nodes)
|
|
22
|
+
mapping = {node: i for i, node in enumerate(nodes)}
|
|
23
|
+
edges = list(G.edges())
|
|
24
|
+
M = len(edges)
|
|
25
|
+
|
|
26
|
+
if M == 0: raise ValueError("Graph must have edges.")
|
|
27
|
+
|
|
28
|
+
idx_map = {edge: i for i, edge in enumerate(edges)}
|
|
29
|
+
U = np.zeros((M, M), dtype=np.complex128)
|
|
30
|
+
|
|
31
|
+
for v in nodes:
|
|
32
|
+
in_edges = [e for e in edges if e[1] == v]
|
|
33
|
+
out_edges = [e for e in edges if e[0] == v]
|
|
34
|
+
d_in = len(in_edges); d_out = len(out_edges)
|
|
35
|
+
|
|
36
|
+
if d_in > 0 and d_out > 0:
|
|
37
|
+
if coin_type == 'fourier':
|
|
38
|
+
for k in range(d_out):
|
|
39
|
+
for l in range(d_in):
|
|
40
|
+
phase = np.exp(2j * np.pi * k * l / max(d_in, d_out))
|
|
41
|
+
U[idx_map[out_edges[k]], idx_map[in_edges[l]]] = phase / np.sqrt(d_in)
|
|
42
|
+
else:
|
|
43
|
+
for k in range(d_out):
|
|
44
|
+
for l in range(d_in):
|
|
45
|
+
U[idx_map[out_edges[k]], idx_map[in_edges[l]]] = np.sqrt(1.0 / d_in)
|
|
46
|
+
|
|
47
|
+
state = np.ones(M, dtype=np.complex128) / np.sqrt(M)
|
|
48
|
+
prob_accumulator = np.zeros(M)
|
|
49
|
+
curr_state = state
|
|
50
|
+
|
|
51
|
+
for t in range(steps):
|
|
52
|
+
curr_state = np.dot(U, curr_state)
|
|
53
|
+
norm = np.linalg.norm(curr_state)
|
|
54
|
+
if norm > 0: curr_state /= norm
|
|
55
|
+
prob_accumulator += np.abs(curr_state) ** 2
|
|
56
|
+
|
|
57
|
+
node_ranks = np.zeros(N)
|
|
58
|
+
for i, (u, v) in enumerate(edges):
|
|
59
|
+
node_ranks[mapping[v]] += prob_accumulator[i] / steps
|
|
60
|
+
|
|
61
|
+
pos = nx.kamada_kawai_layout(G)
|
|
62
|
+
fig, ax = plt.subplots(figsize=(6, 4))
|
|
63
|
+
cmap = plt.cm.YlOrRd
|
|
64
|
+
norm = Normalize(vmin=node_ranks.min(), vmax=node_ranks.max())
|
|
65
|
+
|
|
66
|
+
nx.draw_networkx_edges(G, pos, ax=ax, edge_color='#dddddd', alpha=0.7, arrowsize=12, connectionstyle='arc3,rad=0.1')
|
|
67
|
+
nx.draw_networkx_nodes(G, pos, ax=ax, node_size=220, node_color=node_ranks, cmap=cmap, edgecolors='#444444', linewidths=0.8)
|
|
68
|
+
|
|
69
|
+
for i, node in enumerate(nodes):
|
|
70
|
+
score = node_ranks[i]
|
|
71
|
+
rgb = cmap(norm(score))[:3]
|
|
72
|
+
brightness = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]
|
|
73
|
+
t_color = 'white' if brightness < 0.45 else 'black'
|
|
74
|
+
ax.text(pos[node][0], pos[node][1], s=str(node), color=t_color, fontsize=8, ha='center', va='center', fontweight='bold')
|
|
75
|
+
|
|
76
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
|
|
77
|
+
cb = plt.colorbar(sm, ax=ax, fraction=0.046, pad=0.04)
|
|
78
|
+
cb.outline.set_visible(False)
|
|
79
|
+
cb.ax.tick_params(labelsize=8)
|
|
80
|
+
ax.axis('off')
|
|
81
|
+
plt.tight_layout()
|
|
82
|
+
plt.show()
|
|
83
|
+
return node_ranks
|
|
84
|
+
|
|
85
|
+
def run_szegedy_service(G=None, steps=10, initial_type='uniform', initial_node=None):
|
|
86
|
+
"""
|
|
87
|
+
Quantum speed-up of Markov chain based algorithms
|
|
88
|
+
Szegedy M
|
|
89
|
+
Proceedings of the 45th Annual IEEE Symposium on Foundations of Computer Science
|
|
90
|
+
2004: 32–41.
|
|
91
|
+
"""
|
|
92
|
+
if G is None: G = nx.karate_club_graph()
|
|
93
|
+
nodes = list(G.nodes())
|
|
94
|
+
N = len(nodes)
|
|
95
|
+
mapping = {node: i for i, node in enumerate(nodes)}
|
|
96
|
+
A = nx.to_numpy_array(G, nodelist=nodes)
|
|
97
|
+
degrees = np.sum(A, axis=0)
|
|
98
|
+
degrees[degrees == 0] = 1
|
|
99
|
+
P = A / degrees
|
|
100
|
+
|
|
101
|
+
psi = np.zeros((N * N, N), dtype=np.complex128)
|
|
102
|
+
for i in range(N):
|
|
103
|
+
vec_psi_i = np.zeros((N, N), dtype=np.complex128)
|
|
104
|
+
vec_psi_i[:, i] = np.sqrt(P[:, i])
|
|
105
|
+
psi[:, i] = vec_psi_i.flatten()
|
|
106
|
+
|
|
107
|
+
S = 2 * np.dot(psi, psi.conj().T) - np.eye(N * N)
|
|
108
|
+
SWAP = np.zeros((N * N, N * N))
|
|
109
|
+
for i in range(N):
|
|
110
|
+
for j in range(N): SWAP[i * N + j, j * N + i] = 1
|
|
111
|
+
U = np.dot(SWAP, S)
|
|
112
|
+
|
|
113
|
+
if initial_type == 'node' and initial_node in mapping:
|
|
114
|
+
idx = mapping[initial_node]
|
|
115
|
+
init_matrix = np.zeros((N, N), dtype=np.complex128)
|
|
116
|
+
init_matrix[:, idx] = np.sqrt(P[:, idx])
|
|
117
|
+
state_vec = init_matrix.flatten()
|
|
118
|
+
else:
|
|
119
|
+
state_vec = np.sum(psi, axis=1) / np.sqrt(N)
|
|
120
|
+
|
|
121
|
+
for _ in range(steps):
|
|
122
|
+
state_vec = np.dot(U, state_vec)
|
|
123
|
+
|
|
124
|
+
node_probs = np.sum(np.abs(state_vec.reshape((N, N))) ** 2, axis=1)
|
|
125
|
+
node_probs /= np.sum(node_probs)
|
|
126
|
+
|
|
127
|
+
pos = nx.spring_layout(G, seed=42)
|
|
128
|
+
fig, ax = plt.subplots(figsize=(6, 4))
|
|
129
|
+
cmap = plt.cm.YlGnBu
|
|
130
|
+
norm = Normalize(vmin=node_probs.min(), vmax=node_probs.max())
|
|
131
|
+
nx.draw_networkx_edges(G, pos, ax=ax, edge_color='#bbbbbb', width=1.0)
|
|
132
|
+
nx.draw_networkx_nodes(G, pos, ax=ax, node_size=180, node_color=node_probs, cmap=cmap, edgecolors='#444444', linewidths=1.0)
|
|
133
|
+
|
|
134
|
+
for node in nodes:
|
|
135
|
+
prob = node_probs[mapping[node]]
|
|
136
|
+
rgb = cmap(norm(prob))[:3]
|
|
137
|
+
brightness = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]
|
|
138
|
+
t_color = 'white' if brightness < 0.38 else 'black'
|
|
139
|
+
ax.text(pos[node][0], pos[node][1], s=str(node), color=t_color, fontsize=9, ha='center', va='center')
|
|
140
|
+
|
|
141
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
|
|
142
|
+
sm.set_array([])
|
|
143
|
+
plt.colorbar(sm, ax=ax, fraction=0.046, pad=0.04).outline.set_visible(False)
|
|
144
|
+
ax.axis('off')
|
|
145
|
+
plt.tight_layout()
|
|
146
|
+
plt.show()
|
|
147
|
+
return node_probs
|
|
148
|
+
|
|
149
|
+
def run_community_detection(G=None, initial_state='uniform', coin_type='fourier'):
|
|
150
|
+
"""
|
|
151
|
+
Discrete-time quantum walk on complex networks for community detection
|
|
152
|
+
Kanae Mukai and Naomichi Hatano
|
|
153
|
+
Phys. Rev. Research 2, 023378 (2020)
|
|
154
|
+
"""
|
|
155
|
+
if G is None: G = nx.karate_club_graph()
|
|
156
|
+
nodes = list(G.nodes())
|
|
157
|
+
N = len(nodes)
|
|
158
|
+
mapping = {node: i for i, node in enumerate(nodes)}
|
|
159
|
+
edges = list(G.edges())
|
|
160
|
+
|
|
161
|
+
dir_edges = []
|
|
162
|
+
for u, v in edges:
|
|
163
|
+
dir_edges.append((u, v)); dir_edges.append((v, u))
|
|
164
|
+
idx_map = {edge: i for i, edge in enumerate(dir_edges)}
|
|
165
|
+
dim = len(dir_edges)
|
|
166
|
+
|
|
167
|
+
C = np.zeros((dim, dim), dtype=np.complex128)
|
|
168
|
+
for j in nodes:
|
|
169
|
+
neighbors = list(G.neighbors(j))
|
|
170
|
+
d_j = len(neighbors)
|
|
171
|
+
if d_j == 0: continue
|
|
172
|
+
|
|
173
|
+
if coin_type.lower() == 'fourier':
|
|
174
|
+
coin_matrix = np.exp(2j * np.pi * np.outer(np.arange(d_j), np.arange(d_j)) / d_j) / np.sqrt(d_j)
|
|
175
|
+
elif coin_type.lower() == 'grover':
|
|
176
|
+
coin_matrix = np.full((d_j, d_j), 2.0 / d_j) - np.eye(d_j)
|
|
177
|
+
else: raise ValueError("Unsupported coin_type")
|
|
178
|
+
|
|
179
|
+
for k in range(d_j):
|
|
180
|
+
for l in range(d_j):
|
|
181
|
+
row = idx_map[(j, neighbors[k])]
|
|
182
|
+
col = idx_map[(neighbors[l], j)]
|
|
183
|
+
C[row, col] = coin_matrix[k, l]
|
|
184
|
+
|
|
185
|
+
S = np.zeros((dim, dim))
|
|
186
|
+
for i, j in dir_edges:
|
|
187
|
+
S[idx_map[(j, i)], idx_map[(i, j)]] = 1
|
|
188
|
+
U = S @ C
|
|
189
|
+
|
|
190
|
+
if initial_state == 'uniform':
|
|
191
|
+
phi_0 = np.ones(dim, dtype=np.complex128) / np.sqrt(dim)
|
|
192
|
+
else:
|
|
193
|
+
phi_0 = np.zeros(dim, dtype=np.complex128)
|
|
194
|
+
# Note: 'initial_state' when not uniform implies an index or node logic, keeping simple here
|
|
195
|
+
# Assuming initial_state passed is a valid node index if it's an integer
|
|
196
|
+
if isinstance(initial_state, int) and 0 <= initial_state < N:
|
|
197
|
+
seed_neighbors = list(G.neighbors(nodes[initial_state]))
|
|
198
|
+
for nb in seed_neighbors:
|
|
199
|
+
phi_0[idx_map[(nodes[initial_state], nb)]] = 1.0 / np.sqrt(len(seed_neighbors))
|
|
200
|
+
|
|
201
|
+
vals, vecs = np.linalg.eig(U)
|
|
202
|
+
unique_vals, inverse_indices = np.unique(np.round(vals, 10), return_inverse=True)
|
|
203
|
+
inf_probs = np.zeros(dim)
|
|
204
|
+
|
|
205
|
+
for i in range(len(unique_vals)):
|
|
206
|
+
indices = np.where(inverse_indices == i)[0]
|
|
207
|
+
subspace_vecs = vecs[:, indices]
|
|
208
|
+
proj = subspace_vecs @ (np.conj(subspace_vecs.T) @ phi_0)
|
|
209
|
+
inf_probs += np.abs(proj) ** 2
|
|
210
|
+
|
|
211
|
+
node_scores = np.zeros(N)
|
|
212
|
+
for i, (u, v) in enumerate(dir_edges):
|
|
213
|
+
node_scores[mapping[v]] += np.real(inf_probs[i])
|
|
214
|
+
|
|
215
|
+
return dict(zip(nodes, node_scores))
|
|
216
|
+
|
|
217
|
+
def run_quantum_pagerank(file_path=None, steps=20, alpha=0.85, initial_type='uniform'):
|
|
218
|
+
"""
|
|
219
|
+
Quantum Google in a Complex Network (Paparo et al.)
|
|
220
|
+
"""
|
|
221
|
+
if file_path and os.path.exists(file_path):
|
|
222
|
+
G = nx.read_edgelist(file_path, create_using=nx.Graph())
|
|
223
|
+
else:
|
|
224
|
+
G = nx.karate_club_graph()
|
|
225
|
+
|
|
226
|
+
nodes = list(G.nodes())
|
|
227
|
+
N = len(nodes)
|
|
228
|
+
mapping = {node: i for i, node in enumerate(nodes)}
|
|
229
|
+
A = nx.to_numpy_array(G, nodelist=nodes)
|
|
230
|
+
degrees = np.sum(A, axis=1)
|
|
231
|
+
|
|
232
|
+
P_classic = np.zeros((N, N))
|
|
233
|
+
for i in range(N):
|
|
234
|
+
if degrees[i] > 0: P_classic[:, i] = A[i, :] / degrees[i]
|
|
235
|
+
else: P_classic[:, i] = 1.0 / N
|
|
236
|
+
P = alpha * P_classic + (1 - alpha) / N * np.ones((N, N))
|
|
237
|
+
|
|
238
|
+
psi = np.zeros((N * N, N), dtype=np.complex128)
|
|
239
|
+
for j in range(N):
|
|
240
|
+
column_j = np.zeros((N, N), dtype=np.complex128)
|
|
241
|
+
column_j[:, j] = np.sqrt(P[:, j])
|
|
242
|
+
psi[:, j] = column_j.flatten()
|
|
243
|
+
|
|
244
|
+
S = 2 * np.dot(psi, psi.conj().T) - np.eye(N * N)
|
|
245
|
+
SWAP = np.zeros((N * N, N * N))
|
|
246
|
+
for i in range(N):
|
|
247
|
+
for j in range(N): SWAP[i * N + j, j * N + i] = 1
|
|
248
|
+
U = np.dot(SWAP, S)
|
|
249
|
+
|
|
250
|
+
state = np.sum(psi, axis=1) / np.sqrt(N) if initial_type == 'uniform' else psi[:, 0]
|
|
251
|
+
prob_accumulator = np.zeros(N)
|
|
252
|
+
curr_state = state
|
|
253
|
+
|
|
254
|
+
for t in range(1, steps + 1):
|
|
255
|
+
curr_state = np.dot(U, curr_state)
|
|
256
|
+
probs = np.sum(np.abs(curr_state.reshape((N, N))) ** 2, axis=1)
|
|
257
|
+
prob_accumulator += probs
|
|
258
|
+
q_pagerank_scores = prob_accumulator / steps
|
|
259
|
+
|
|
260
|
+
pos = nx.spring_layout(G, seed=42)
|
|
261
|
+
fig, ax = plt.subplots(figsize=(6, 4))
|
|
262
|
+
cmap = plt.cm.YlGnBu
|
|
263
|
+
norm = Normalize(vmin=q_pagerank_scores.min(), vmax=q_pagerank_scores.max())
|
|
264
|
+
nx.draw_networkx_edges(G, pos, ax=ax, edge_color='#bbbbbb', alpha=0.4, arrows=False)
|
|
265
|
+
nx.draw_networkx_nodes(G, pos, ax=ax, node_size=180, node_color=q_pagerank_scores, cmap=cmap, edgecolors='#444444', linewidths=0.8)
|
|
266
|
+
|
|
267
|
+
for node in nodes:
|
|
268
|
+
score = q_pagerank_scores[mapping[node]]
|
|
269
|
+
rgb = cmap(norm(score))[:3]
|
|
270
|
+
brightness = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]
|
|
271
|
+
t_color = 'white' if brightness < 0.38 else 'black'
|
|
272
|
+
ax.text(pos[node][0], pos[node][1], s=str(node), color=t_color, fontsize=8, ha='center', va='center')
|
|
273
|
+
|
|
274
|
+
plt.colorbar(plt.cm.ScalarMappable(cmap=cmap, norm=norm), ax=ax, fraction=0.046, pad=0.04)
|
|
275
|
+
ax.axis('off')
|
|
276
|
+
plt.tight_layout()
|
|
277
|
+
plt.show()
|
|
278
|
+
return q_pagerank_scores
|
|
279
|
+
|
|
280
|
+
def run_hadamard(steps=100, top_p=0.05):
|
|
281
|
+
"""
|
|
282
|
+
A Hadamard walk model and its application in identification of important edges in complex networks
|
|
283
|
+
Liang et al.
|
|
284
|
+
"""
|
|
285
|
+
G = nx.karate_club_graph()
|
|
286
|
+
nodes, edges = list(G.nodes()), list(G.edges())
|
|
287
|
+
N, M = len(nodes), len(edges)
|
|
288
|
+
|
|
289
|
+
directed_edges = []
|
|
290
|
+
for u, v in edges: directed_edges.extend([(u, v), (v, u)])
|
|
291
|
+
dim = len(directed_edges)
|
|
292
|
+
idx_map = {edge: i for i, edge in enumerate(directed_edges)}
|
|
293
|
+
|
|
294
|
+
H = np.array([[1, 1], [1, -1]]) / np.sqrt(2)
|
|
295
|
+
C = np.zeros((dim, dim))
|
|
296
|
+
for i in range(0, dim, 2): C[i:i + 2, i:i + 2] = H
|
|
297
|
+
S = np.zeros((dim, dim))
|
|
298
|
+
for u, v in directed_edges: S[idx_map[(v, u)], idx_map[(u, v)]] = 1
|
|
299
|
+
U = S @ C
|
|
300
|
+
|
|
301
|
+
psi = np.ones(dim, dtype=np.complex128) / np.sqrt(dim)
|
|
302
|
+
node_probs_accum = np.zeros(N)
|
|
303
|
+
for _ in range(steps):
|
|
304
|
+
psi = U @ psi
|
|
305
|
+
curr_p = np.abs(psi) ** 2
|
|
306
|
+
for i, (u, v) in enumerate(directed_edges):
|
|
307
|
+
node_probs_accum[nodes.index(v)] += curr_p[i]
|
|
308
|
+
node_avg_p = node_probs_accum / steps
|
|
309
|
+
|
|
310
|
+
edge_scores = np.array([node_avg_p[nodes.index(u)] + node_avg_p[nodes.index(v)] for u, v in edges])
|
|
311
|
+
num_top = max(1, int(M * top_p))
|
|
312
|
+
top_indices = np.argsort(edge_scores)[-num_top:]
|
|
313
|
+
top_edges = [edges[i] for i in top_indices]
|
|
314
|
+
top_scores = edge_scores[top_indices]
|
|
315
|
+
|
|
316
|
+
pos = nx.spring_layout(G, seed=42)
|
|
317
|
+
fig, ax = plt.subplots(figsize=(10, 7))
|
|
318
|
+
target_cmap = plt.cm.plasma
|
|
319
|
+
norm = Normalize(vmin=top_scores.min(), vmax=top_scores.max())
|
|
320
|
+
|
|
321
|
+
nx.draw_networkx_nodes(G, pos, ax=ax, node_size=250, node_color='green', edgecolors='white', linewidths=0.6)
|
|
322
|
+
nx.draw_networkx_edges(G, pos, ax=ax, edgelist=edges, edge_color='#444444', width=0.8, alpha=0.3, arrows=False)
|
|
323
|
+
nx.draw_networkx_edges(G, pos, ax=ax, edgelist=top_edges, edge_color=top_scores, edge_cmap=target_cmap,
|
|
324
|
+
edge_vmin=top_scores.min(), edge_vmax=top_scores.max(), width=2.5, alpha=1.0, arrows=False)
|
|
325
|
+
nx.draw_networkx_labels(G, pos, font_size=8, font_color='white', font_weight='bold')
|
|
326
|
+
ax.set_title(f"Hadamard Walk: Top {num_top}", fontsize=12)
|
|
327
|
+
ax.axis('off')
|
|
328
|
+
plt.tight_layout()
|
|
329
|
+
plt.show()
|
|
330
|
+
return edge_scores
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from matplotlib.colors import LogNorm
|
|
4
|
+
|
|
5
|
+
class LatticeQuantumWalk:
|
|
6
|
+
"""
|
|
7
|
+
Unified class for 1D, 2D, and 3D Quantum Walk Simulations on regular lattices.
|
|
8
|
+
"""
|
|
9
|
+
def __init__(self, dimension: int, network_size: int = 100):
|
|
10
|
+
if dimension not in [1, 2, 3]:
|
|
11
|
+
raise ValueError("Dimension must be 1, 2, or 3.")
|
|
12
|
+
|
|
13
|
+
self.dimension = dimension
|
|
14
|
+
self.N = network_size
|
|
15
|
+
self.LSIZE = 2 * self.N + 1
|
|
16
|
+
self.results = None
|
|
17
|
+
self.meta = {}
|
|
18
|
+
|
|
19
|
+
def run(self, steps: int = 100, initial_dist: str = 'uniform', compare_classical: bool = False) -> dict:
|
|
20
|
+
self.meta = {
|
|
21
|
+
"steps": steps,
|
|
22
|
+
"initial_dist": initial_dist,
|
|
23
|
+
"compare_classical": compare_classical,
|
|
24
|
+
"network_size": self.N
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if self.dimension == 1:
|
|
28
|
+
self.results = self._run_1d(steps, initial_dist, compare_classical)
|
|
29
|
+
elif self.dimension == 2:
|
|
30
|
+
self.results = self._run_2d(steps, initial_dist, compare_classical)
|
|
31
|
+
elif self.dimension == 3:
|
|
32
|
+
self.results = self._run_3d(steps, initial_dist)
|
|
33
|
+
|
|
34
|
+
return self.results
|
|
35
|
+
|
|
36
|
+
def _run_1d(self, steps, initial_dist, compare_classical):
|
|
37
|
+
N = self.N
|
|
38
|
+
LSIZE = self.LSIZE
|
|
39
|
+
COINS = 1
|
|
40
|
+
TOTAL_DIM = LSIZE * COINS * 2
|
|
41
|
+
|
|
42
|
+
TOSS = np.zeros(shape=(TOTAL_DIM, TOTAL_DIM), dtype=complex)
|
|
43
|
+
STEP = np.zeros(shape=(TOTAL_DIM, TOTAL_DIM), dtype=complex)
|
|
44
|
+
|
|
45
|
+
for row in range(TOTAL_DIM):
|
|
46
|
+
for col in range(TOTAL_DIM):
|
|
47
|
+
if (row == col) and (row < LSIZE): TOSS[row, col] = 1/np.sqrt(2)
|
|
48
|
+
elif (row == col) and (row >= LSIZE): TOSS[row, col] = -1/np.sqrt(2)
|
|
49
|
+
elif (row == col + LSIZE): TOSS[row, col] = 1/np.sqrt(2)
|
|
50
|
+
elif (col == row + LSIZE): TOSS[row, col] = 1/np.sqrt(2)
|
|
51
|
+
|
|
52
|
+
if (row == col + 1) and (row < LSIZE): STEP[row, col] = 1
|
|
53
|
+
elif (col == row + 1) and (row >= LSIZE): STEP[row, col] = 1
|
|
54
|
+
|
|
55
|
+
state0 = np.zeros(shape=(TOTAL_DIM, 1), dtype=complex)
|
|
56
|
+
def get_indices(lattice_x):
|
|
57
|
+
base_idx = int(lattice_x + N)
|
|
58
|
+
return [base_idx, base_idx + LSIZE]
|
|
59
|
+
center_indices = get_indices(0)
|
|
60
|
+
|
|
61
|
+
if initial_dist == 'uniform':
|
|
62
|
+
state0[center_indices[0]] = 1/np.sqrt(2)
|
|
63
|
+
state0[center_indices[1]] = (0+1j)/np.sqrt(2)
|
|
64
|
+
elif initial_dist == 'biased':
|
|
65
|
+
state0[center_indices[0]] = 1.0
|
|
66
|
+
|
|
67
|
+
current_state = state0
|
|
68
|
+
for t in range(steps):
|
|
69
|
+
current_state = np.dot(TOSS, current_state)
|
|
70
|
+
current_state = np.dot(STEP, current_state)
|
|
71
|
+
|
|
72
|
+
lattice_grid = np.arange(-N, N + 1)
|
|
73
|
+
quantum_probs = np.zeros(LSIZE)
|
|
74
|
+
for i, x in enumerate(lattice_grid):
|
|
75
|
+
locs = get_indices(x)
|
|
76
|
+
quantum_probs[i] = (np.abs(current_state[locs[0]])**2 + np.abs(current_state[locs[1]])**2).item()
|
|
77
|
+
|
|
78
|
+
classical_probs = None
|
|
79
|
+
if compare_classical:
|
|
80
|
+
walkers = 5000
|
|
81
|
+
positions = np.zeros(walkers)
|
|
82
|
+
for _ in range(steps):
|
|
83
|
+
positions += np.random.choice([-1, 1], size=walkers)
|
|
84
|
+
c_counts, _ = np.histogram(positions, bins=np.arange(-N, N + 2) - 0.5, density=True)
|
|
85
|
+
classical_probs = c_counts
|
|
86
|
+
|
|
87
|
+
return {"type": "1D", "grid": lattice_grid, "quantum_probs": quantum_probs, "classical_probs": classical_probs}
|
|
88
|
+
|
|
89
|
+
def _run_2d(self, steps, initial_dist, compare_classical):
|
|
90
|
+
N = self.N
|
|
91
|
+
LSIZE = self.LSIZE
|
|
92
|
+
state = np.zeros((2, 2, LSIZE, LSIZE), dtype=complex)
|
|
93
|
+
if initial_dist == 'uniform':
|
|
94
|
+
state[0, 0, N, N], state[0, 1, N, N] = 0.5, 0.5j
|
|
95
|
+
state[1, 0, N, N], state[1, 1, N, N] = -0.5j, 0.5
|
|
96
|
+
else:
|
|
97
|
+
state[0, 0, N, N] = 1.0
|
|
98
|
+
|
|
99
|
+
for _ in range(steps):
|
|
100
|
+
s = state
|
|
101
|
+
new_s = np.empty_like(s)
|
|
102
|
+
new_s[0, 0] = 0.5 * (s[0, 0] + s[0, 1] + s[1, 0] + s[1, 1])
|
|
103
|
+
new_s[0, 1] = 0.5 * (s[0, 0] - s[0, 1] + s[1, 0] - s[1, 1])
|
|
104
|
+
new_s[1, 0] = 0.5 * (s[0, 0] + s[0, 1] - s[1, 0] - s[1, 1])
|
|
105
|
+
new_s[1, 1] = 0.5 * (s[0, 0] - s[0, 1] - s[1, 0] + s[1, 1])
|
|
106
|
+
state[0, 0] = np.roll(new_s[0, 0], shift=(1, 1), axis=(0, 1))
|
|
107
|
+
state[0, 1] = np.roll(new_s[0, 1], shift=(1, -1), axis=(0, 1))
|
|
108
|
+
state[1, 0] = np.roll(new_s[1, 0], shift=(-1, 1), axis=(0, 1))
|
|
109
|
+
state[1, 1] = np.roll(new_s[1, 1], shift=(-1, -1), axis=(0, 1))
|
|
110
|
+
|
|
111
|
+
q_probs = np.sum(np.abs(state) ** 2, axis=(0, 1))
|
|
112
|
+
c_probs = None
|
|
113
|
+
if compare_classical:
|
|
114
|
+
walkers = 10000
|
|
115
|
+
x, y = np.zeros(walkers, dtype=int), np.zeros(walkers, dtype=int)
|
|
116
|
+
for _ in range(steps):
|
|
117
|
+
x += np.random.choice([-1, 1], size=walkers)
|
|
118
|
+
y += np.random.choice([-1, 1], size=walkers)
|
|
119
|
+
c_probs, _, _ = np.histogram2d(x, y, bins=[np.arange(-N, N + 2) - 0.5] * 2, density=True)
|
|
120
|
+
c_probs = c_probs.T
|
|
121
|
+
return {"type": "2D", "q_data": q_probs, "c_data": c_probs}
|
|
122
|
+
|
|
123
|
+
def _run_3d(self, steps, initial_dist):
|
|
124
|
+
N = self.N
|
|
125
|
+
LSIZE = self.LSIZE
|
|
126
|
+
COIN_DIM = 6
|
|
127
|
+
state = np.zeros((COIN_DIM, LSIZE, LSIZE, LSIZE), dtype=np.complex128)
|
|
128
|
+
center = N
|
|
129
|
+
if initial_dist == 'uniform':
|
|
130
|
+
state[:, center, center, center] = 1.0 / np.sqrt(COIN_DIM)
|
|
131
|
+
else:
|
|
132
|
+
state[0, center, center, center] = 1.0
|
|
133
|
+
grover_coin = (2.0 / COIN_DIM) * np.ones((COIN_DIM, COIN_DIM)) - np.eye(COIN_DIM)
|
|
134
|
+
|
|
135
|
+
for _ in range(steps):
|
|
136
|
+
state = np.einsum('ij,jxyz->ixyz', grover_coin, state)
|
|
137
|
+
new_state = np.zeros_like(state)
|
|
138
|
+
new_state[0] = np.roll(state[0], 1, 0)
|
|
139
|
+
new_state[1] = np.roll(state[1], -1, 0)
|
|
140
|
+
new_state[2] = np.roll(state[2], 1, 1)
|
|
141
|
+
new_state[3] = np.roll(state[3], -1, 1)
|
|
142
|
+
new_state[4] = np.roll(state[4], 1, 2)
|
|
143
|
+
new_state[5] = np.roll(state[5], -1, 2)
|
|
144
|
+
state = new_state
|
|
145
|
+
q_probs = np.sum(np.abs(state) ** 2, axis=0)
|
|
146
|
+
return {"type": "3D", "q_data": q_probs}
|
|
147
|
+
|
|
148
|
+
def visualize(self):
|
|
149
|
+
if self.results is None: raise ValueError("No results. Run simulation first.")
|
|
150
|
+
if self.dimension == 1: self._visualize_1d()
|
|
151
|
+
elif self.dimension == 2: self._visualize_2d()
|
|
152
|
+
elif self.dimension == 3: self._visualize_3d()
|
|
153
|
+
|
|
154
|
+
def _visualize_1d(self):
|
|
155
|
+
result = self.results
|
|
156
|
+
plt.figure()
|
|
157
|
+
plt.plot(result['grid'], result['quantum_probs'], label='Quantum')
|
|
158
|
+
if result['classical_probs'] is not None:
|
|
159
|
+
plt.plot(result['grid'], result['classical_probs'], label='Classical', linestyle='--')
|
|
160
|
+
plt.legend()
|
|
161
|
+
plt.title(f"1D Quantum Walk (N={self.meta['network_size']}, T={self.meta['steps']})")
|
|
162
|
+
plt.show()
|
|
163
|
+
|
|
164
|
+
def _visualize_2d(self):
|
|
165
|
+
result = self.results
|
|
166
|
+
fig, axes = plt.subplots(1, 2 if self.meta['compare_classical'] else 1, figsize=(12, 5))
|
|
167
|
+
ax_q = axes[0] if self.meta['compare_classical'] else axes
|
|
168
|
+
im_q = ax_q.imshow(result['q_data'], cmap='Blues', origin='lower', extent=[-self.N, self.N, -self.N, self.N])
|
|
169
|
+
ax_q.set_title(f"Quantum Walk (Steps={self.meta['steps']})")
|
|
170
|
+
plt.colorbar(im_q, ax=ax_q)
|
|
171
|
+
if self.meta['compare_classical']:
|
|
172
|
+
ax_c = axes[1]
|
|
173
|
+
im_c = ax_c.imshow(result['c_data'], cmap='Blues', origin='lower', extent=[-self.N, self.N, -self.N, self.N])
|
|
174
|
+
ax_c.set_title("Classical Random Walk")
|
|
175
|
+
plt.colorbar(im_c, ax=ax_c)
|
|
176
|
+
plt.tight_layout()
|
|
177
|
+
plt.show()
|
|
178
|
+
|
|
179
|
+
def _visualize_3d(self):
|
|
180
|
+
q_probs = self.results['q_data']
|
|
181
|
+
N = self.N
|
|
182
|
+
center = N
|
|
183
|
+
xy = q_probs[:, :, center]
|
|
184
|
+
yz = q_probs[center, :, :]
|
|
185
|
+
xz = q_probs[:, center, :]
|
|
186
|
+
fig, axes = plt.subplots(1, 3, figsize=(18, 5), dpi=100)
|
|
187
|
+
cmap = 'YlGnBu'
|
|
188
|
+
norm = LogNorm(vmin=max(q_probs.min(), 1e-12), vmax=q_probs.max())
|
|
189
|
+
|
|
190
|
+
for ax, data, title, xlabel, ylabel in zip(axes, [xy, yz, xz], ["XY Slice", "YZ Slice", "XZ Slice"], ["x","y","x"], ["y","z","z"]):
|
|
191
|
+
im = ax.imshow(data.T, cmap=cmap, norm=norm, origin='lower', extent=[-N, N, -N, N])
|
|
192
|
+
ax.set_title(title); ax.set_xlabel(xlabel); ax.set_ylabel(ylabel)
|
|
193
|
+
fig.colorbar(axes[2].images[0], ax=axes.ravel().tolist(), pad=0.02).set_label('Raw Probability Density')
|
|
194
|
+
plt.suptitle(f"3D Quantum Walk Analysis (Steps={self.meta['steps']})")
|
|
195
|
+
plt.show()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sly-quantum-walks
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A unified Quantum Walk simulation package
|
|
5
|
+
Project-URL: Homepage, https://example.com/quantum_walks
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: matplotlib
|
|
10
|
+
Requires-Dist: networkx
|
|
11
|
+
|
|
12
|
+
# Quantum Walks Package
|
|
13
|
+
|
|
14
|
+
This package provides a unified interface for various Quantum Walk simulations.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install quantumwalks
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
import quantumwalks as qw
|
|
26
|
+
|
|
27
|
+
# 1D/2D/3D Lattice Walks
|
|
28
|
+
qw.run_simulation('1d', steps=100)
|
|
29
|
+
qw.run_simulation('2d', steps=50)
|
|
30
|
+
qw.run_simulation('3d', steps=20)
|
|
31
|
+
|
|
32
|
+
# Graph Walks
|
|
33
|
+
qw.run_simulation('ranking', file_path='graph.edgelist')
|
|
34
|
+
qw.run_simulation('pagerank')
|
|
35
|
+
qw.run_simulation('hadamard')
|
|
36
|
+
qw.run_simulation('szegedy')
|
|
37
|
+
qw.run_simulation('community')
|
|
38
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/quantum_walks/__init__.py
|
|
4
|
+
src/quantum_walks/graphs.py
|
|
5
|
+
src/quantum_walks/lattice.py
|
|
6
|
+
src/sly_quantum_walks.egg-info/PKG-INFO
|
|
7
|
+
src/sly_quantum_walks.egg-info/SOURCES.txt
|
|
8
|
+
src/sly_quantum_walks.egg-info/dependency_links.txt
|
|
9
|
+
src/sly_quantum_walks.egg-info/requires.txt
|
|
10
|
+
src/sly_quantum_walks.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
quantum_walks
|