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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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,3 @@
1
+ numpy
2
+ matplotlib
3
+ networkx