dense-evolution 8.0.4__tar.gz → 8.0.5__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.
- dense_evolution-8.0.5/Dense_Evolution_Library/__init__.py +12 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/compiler.py +311 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/gates.py +61 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/mega_stress_test.py +76 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/parser.py +424 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/registry.py +372 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/simulator.py +488 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/stress_test.py +76 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/stress_test_quantum.py +76 -0
- dense_evolution-8.0.5/Dense_Evolution_Library/test_library.py +39 -0
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/PKG-INFO +1709 -1706
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dense_evolution.egg-info/PKG-INFO +1709 -1706
- dense_evolution-8.0.5/dense_evolution.egg-info/SOURCES.txt +19 -0
- dense_evolution-8.0.5/dense_evolution.egg-info/top_level.txt +2 -0
- dense_evolution-8.0.5/license.md +58 -0
- dense_evolution-8.0.5/pyproject.toml +87 -0
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/setup.cfg +4 -4
- dense_evolution-8.0.4/dense_evolution.egg-info/SOURCES.txt +0 -9
- dense_evolution-8.0.4/dense_evolution.egg-info/top_level.txt +0 -2
- dense_evolution-8.0.4/dense_evolution.py +0 -1948
- dense_evolution-8.0.4/pyproject.toml +0 -38
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/README.md +0 -0
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dash.py +0 -0
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dense_evolution.egg-info/dependency_links.txt +0 -0
- {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dense_evolution.egg-info/requires.txt +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dense-Evolution
|
|
3
|
+
High-performance quantum statevector simulator optimized for NISQ circuits.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .simulator import DenseSVSimulator
|
|
7
|
+
from .parser import QASMParser, QASMCircuit
|
|
8
|
+
from .compiler import QuantumTranspiler
|
|
9
|
+
from .registry import NoiseModel, QuantumHardwareRegistry
|
|
10
|
+
from .gates import GATES, PARAMETRIC_GATES, GATE_IDS
|
|
11
|
+
|
|
12
|
+
__version__ = "8.0.4"
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
# ── optional JAX import (same pattern as registry) ────────────────────────────
|
|
6
|
+
try:
|
|
7
|
+
from registry import HAS_JAX
|
|
8
|
+
except ImportError:
|
|
9
|
+
try:
|
|
10
|
+
import jax
|
|
11
|
+
HAS_JAX = True
|
|
12
|
+
except ImportError:
|
|
13
|
+
HAS_JAX = False
|
|
14
|
+
|
|
15
|
+
if HAS_JAX:
|
|
16
|
+
import jax
|
|
17
|
+
import jax.numpy as jnp
|
|
18
|
+
jax.config.update("jax_enable_x64", True)
|
|
19
|
+
|
|
20
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
# Gate-ID encoding (shared between _apply_gate_fast_step and the beast-mode
|
|
22
|
+
# command builder in the dashboard).
|
|
23
|
+
#
|
|
24
|
+
# 0 I (identity) 7 T
|
|
25
|
+
# 1 H 8 Tdg
|
|
26
|
+
# 2 X 9 Rx(θ)
|
|
27
|
+
# 3 Y 10 Ry(θ)
|
|
28
|
+
# 4 Z 11 Rz(θ)
|
|
29
|
+
# 5 S 12 Phase / P(θ) / U1(θ)
|
|
30
|
+
# 6 Sdg ── 2-qubit gates ──
|
|
31
|
+
# 20 CX / CNOT
|
|
32
|
+
# 21 CZ
|
|
33
|
+
# 22 CP(θ) / CRZ(θ)
|
|
34
|
+
# 23 SWAP
|
|
35
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
if HAS_JAX:
|
|
38
|
+
|
|
39
|
+
@jax.jit
|
|
40
|
+
def _apply_gate_fast_step(sv: "jnp.ndarray",
|
|
41
|
+
operation: "jnp.ndarray"):
|
|
42
|
+
"""
|
|
43
|
+
Apply a single quantum gate to statevector *sv*.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
sv : complex128 statevector of shape (2**n,)
|
|
48
|
+
operation : float64 array [g_id, q1, q2, param]
|
|
49
|
+
g_id — gate identifier (see table above)
|
|
50
|
+
q1 — target qubit (1-qubit gates) or control qubit (2-qubit)
|
|
51
|
+
q2 — target qubit for 2-qubit gates; unused for 1-qubit
|
|
52
|
+
param — rotation angle in radians; 0.0 for non-parametric gates
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
(new_sv, None) — compatible with jax.lax.scan
|
|
57
|
+
"""
|
|
58
|
+
g_id = operation[0].astype(jnp.int32)
|
|
59
|
+
q1 = operation[1].astype(jnp.int32)
|
|
60
|
+
q2 = operation[2].astype(jnp.int32)
|
|
61
|
+
param = operation[3]
|
|
62
|
+
dim = sv.shape[0]
|
|
63
|
+
|
|
64
|
+
inv2 = jnp.float64(1.0 / jnp.sqrt(2.0))
|
|
65
|
+
half_p = param * jnp.float64(0.5)
|
|
66
|
+
cos_p = jnp.cos(half_p).astype(jnp.complex128)
|
|
67
|
+
sin_p = jnp.sin(half_p).astype(jnp.complex128)
|
|
68
|
+
exp_pos = jnp.exp( 1j * param).astype(jnp.complex128)
|
|
69
|
+
exp_neg = jnp.exp(-1j * param).astype(jnp.complex128)
|
|
70
|
+
exp_ph4 = jnp.exp( 1j * jnp.pi / 4.0).astype(jnp.complex128)
|
|
71
|
+
exp_mh4 = jnp.exp(-1j * jnp.pi / 4.0).astype(jnp.complex128)
|
|
72
|
+
|
|
73
|
+
# ── 1-qubit gate matrix selection via lax.switch ──────────────
|
|
74
|
+
# Index must be in [0, 12]; anything outside is clamped to 0 (I).
|
|
75
|
+
safe_gid = jnp.clip(g_id, 0, 12)
|
|
76
|
+
|
|
77
|
+
g_1q = jax.lax.switch(
|
|
78
|
+
safe_gid,
|
|
79
|
+
[
|
|
80
|
+
# 0 I
|
|
81
|
+
lambda _: jnp.eye(2, dtype=jnp.complex128),
|
|
82
|
+
# 1 H
|
|
83
|
+
lambda _: jnp.array(
|
|
84
|
+
[[inv2, inv2],
|
|
85
|
+
[inv2, -inv2]], dtype=jnp.complex128),
|
|
86
|
+
# 2 X
|
|
87
|
+
lambda _: jnp.array(
|
|
88
|
+
[[0.0+0j, 1.0+0j],
|
|
89
|
+
[1.0+0j, 0.0+0j]], dtype=jnp.complex128),
|
|
90
|
+
# 3 Y
|
|
91
|
+
lambda _: jnp.array(
|
|
92
|
+
[[0.0+0j, -1j],
|
|
93
|
+
[1j, 0.0+0j]], dtype=jnp.complex128),
|
|
94
|
+
# 4 Z
|
|
95
|
+
lambda _: jnp.array(
|
|
96
|
+
[[1.0+0j, 0.0+0j],
|
|
97
|
+
[0.0+0j, -1.0+0j]], dtype=jnp.complex128),
|
|
98
|
+
# 5 S
|
|
99
|
+
lambda _: jnp.array(
|
|
100
|
+
[[1.0+0j, 0.0+0j],
|
|
101
|
+
[0.0+0j, 1j ]], dtype=jnp.complex128),
|
|
102
|
+
# 6 Sdg
|
|
103
|
+
lambda _: jnp.array(
|
|
104
|
+
[[1.0+0j, 0.0+0j],
|
|
105
|
+
[0.0+0j, -1j ]], dtype=jnp.complex128),
|
|
106
|
+
# 7 T
|
|
107
|
+
lambda _: jnp.array(
|
|
108
|
+
[[1.0+0j, 0.0+0j],
|
|
109
|
+
[0.0+0j, exp_ph4]], dtype=jnp.complex128),
|
|
110
|
+
# 8 Tdg
|
|
111
|
+
lambda _: jnp.array(
|
|
112
|
+
[[1.0+0j, 0.0+0j],
|
|
113
|
+
[0.0+0j, exp_mh4]], dtype=jnp.complex128),
|
|
114
|
+
# 9 Rx(θ) = [[cos θ/2, -i sin θ/2], [-i sin θ/2, cos θ/2]]
|
|
115
|
+
lambda _: jnp.array(
|
|
116
|
+
[[cos_p, -1j * sin_p],
|
|
117
|
+
[-1j * sin_p, cos_p ]], dtype=jnp.complex128),
|
|
118
|
+
# 10 Ry(θ) = [[cos θ/2, -sin θ/2], [sin θ/2, cos θ/2]]
|
|
119
|
+
lambda _: jnp.array(
|
|
120
|
+
[[cos_p, -sin_p],
|
|
121
|
+
[sin_p, cos_p]], dtype=jnp.complex128),
|
|
122
|
+
# 11 Rz(θ) = [[e^{-iθ/2}, 0], [0, e^{iθ/2}]]
|
|
123
|
+
lambda _: jnp.array(
|
|
124
|
+
[[jnp.exp(-1j * half_p), 0.0+0j ],
|
|
125
|
+
[0.0+0j, jnp.exp(1j * half_p)]],
|
|
126
|
+
dtype=jnp.complex128),
|
|
127
|
+
# 12 Phase / P(θ) / U1(θ) = [[1, 0], [0, e^{iθ}]]
|
|
128
|
+
lambda _: jnp.array(
|
|
129
|
+
[[1.0+0j, 0.0+0j],
|
|
130
|
+
[0.0+0j, exp_pos]], dtype=jnp.complex128),
|
|
131
|
+
],
|
|
132
|
+
operand=None,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# ── 1-qubit application ────────────────────────────────────────
|
|
136
|
+
def do_1q(_sv):
|
|
137
|
+
stride = jnp.int64(1) << q1.astype(jnp.int64)
|
|
138
|
+
idx_full = jnp.arange(dim, dtype=jnp.int64)
|
|
139
|
+
mask_0 = (idx_full & stride) == 0
|
|
140
|
+
|
|
141
|
+
# idx_0: indices where qubit q1 == 0
|
|
142
|
+
# idx_1: corresponding |1⟩ partners
|
|
143
|
+
# We build them without xp.where-tuple confusion:
|
|
144
|
+
# any index i has its pair at i ^ stride.
|
|
145
|
+
# For i in |0⟩ slots (mask_0): partner = i | stride = i ^ stride
|
|
146
|
+
# For i in |1⟩ slots (¬mask_0): partner = i ^ stride (clears bit)
|
|
147
|
+
# We process all indices simultaneously using the |0⟩ slot's amplitude.
|
|
148
|
+
idx_pair = idx_full ^ stride # each element's partner
|
|
149
|
+
amp_self = _sv[idx_full] # a[i]
|
|
150
|
+
amp_pair = _sv[idx_pair] # a[i ^ stride]
|
|
151
|
+
|
|
152
|
+
# When mask_0: amp_self = a_0, amp_pair = a_1
|
|
153
|
+
# new_0 = g00*a_0 + g01*a_1
|
|
154
|
+
# new_1 = g10*a_0 + g11*a_1
|
|
155
|
+
g00 = g_1q[0, 0]; g01 = g_1q[0, 1]
|
|
156
|
+
g10 = g_1q[1, 0]; g11 = g_1q[1, 1]
|
|
157
|
+
|
|
158
|
+
new_when_0 = g00 * amp_self + g01 * amp_pair # result for |0⟩ slot
|
|
159
|
+
new_when_1 = g10 * amp_pair + g11 * amp_self # result for |1⟩ slot
|
|
160
|
+
# NOTE: for |1⟩ slots, amp_pair is the |0⟩ amplitude and
|
|
161
|
+
# amp_self is the |1⟩ amplitude — roles are swapped.
|
|
162
|
+
|
|
163
|
+
return jnp.where(mask_0, new_when_0, new_when_1)
|
|
164
|
+
|
|
165
|
+
# ── 2-qubit application ───────────────────────────────────────
|
|
166
|
+
def do_2q(_sv):
|
|
167
|
+
ctrl = q1.astype(jnp.int64)
|
|
168
|
+
trgt = q2.astype(jnp.int64)
|
|
169
|
+
idx_full = jnp.arange(dim, dtype=jnp.int64)
|
|
170
|
+
|
|
171
|
+
ctrl_bit_set = (idx_full & (jnp.int64(1) << ctrl)) != 0
|
|
172
|
+
trgt_bit_set = (idx_full & (jnp.int64(1) << trgt)) != 0
|
|
173
|
+
|
|
174
|
+
# CX: flip target bit when control is set
|
|
175
|
+
def apply_cx(__sv):
|
|
176
|
+
partner = idx_full ^ (jnp.int64(1) << trgt)
|
|
177
|
+
swapped = __sv[partner]
|
|
178
|
+
return jnp.where(ctrl_bit_set, swapped, __sv)
|
|
179
|
+
|
|
180
|
+
# CZ: negate amplitude when both control and target bits are set
|
|
181
|
+
def apply_cz(__sv):
|
|
182
|
+
both_set = ctrl_bit_set & trgt_bit_set
|
|
183
|
+
return jnp.where(both_set, -__sv, __sv)
|
|
184
|
+
|
|
185
|
+
# CP(θ): phase kick e^{iθ} on |11⟩ component
|
|
186
|
+
def apply_cp(__sv):
|
|
187
|
+
both_set = ctrl_bit_set & trgt_bit_set
|
|
188
|
+
return jnp.where(both_set, exp_pos * __sv, __sv)
|
|
189
|
+
|
|
190
|
+
# SWAP: exchange amplitudes of ctrl-bit and trgt-bit positions
|
|
191
|
+
def apply_swap(__sv):
|
|
192
|
+
# Standard SWAP = CX(c,t) · CX(t,c) · CX(c,t)
|
|
193
|
+
# Computed directly: for each (ctrl=0,trgt=1) pair with
|
|
194
|
+
# the other bits identical, swap the two amplitudes.
|
|
195
|
+
only_ctrl = ( ctrl_bit_set & ~trgt_bit_set)
|
|
196
|
+
only_trgt = (~ctrl_bit_set & trgt_bit_set)
|
|
197
|
+
swap_mask = only_ctrl | only_trgt
|
|
198
|
+
partner = idx_full ^ (jnp.int64(1) << ctrl) ^ (jnp.int64(1) << trgt)
|
|
199
|
+
return jnp.where(swap_mask, __sv[partner], __sv)
|
|
200
|
+
|
|
201
|
+
# Dispatch on g_id: 20=CX, 21=CZ, 22=CP, 23=SWAP
|
|
202
|
+
is_cx = g_id == 20
|
|
203
|
+
is_cz = g_id == 21
|
|
204
|
+
is_cp = g_id == 22
|
|
205
|
+
# is_swap = g_id == 23 (default branch)
|
|
206
|
+
|
|
207
|
+
after_cx = jax.lax.cond(is_cx, apply_cx, lambda s: s, _sv)
|
|
208
|
+
after_cz = jax.lax.cond(is_cz, apply_cz, lambda s: s, _sv)
|
|
209
|
+
after_cp = jax.lax.cond(is_cp, apply_cp, lambda s: s, _sv)
|
|
210
|
+
after_swap = apply_swap(_sv)
|
|
211
|
+
|
|
212
|
+
# Pick the right result
|
|
213
|
+
result = jnp.where(is_cx, after_cx,
|
|
214
|
+
jnp.where(is_cz, after_cz,
|
|
215
|
+
jnp.where(is_cp, after_cp,
|
|
216
|
+
after_swap)))
|
|
217
|
+
return result
|
|
218
|
+
|
|
219
|
+
# ── branch on 1-qubit vs 2-qubit ─────────────────────────────
|
|
220
|
+
# g_id <= 12 → 1-qubit; g_id >= 20 → 2-qubit.
|
|
221
|
+
# Both branches must have identical output dtypes — enforced here
|
|
222
|
+
# by casting both outputs to complex128.
|
|
223
|
+
is_1q = g_id <= 12
|
|
224
|
+
new_sv = jax.lax.cond(
|
|
225
|
+
is_1q,
|
|
226
|
+
lambda s: do_1q(s).astype(jnp.complex128),
|
|
227
|
+
lambda s: do_2q(s).astype(jnp.complex128),
|
|
228
|
+
sv,
|
|
229
|
+
)
|
|
230
|
+
return new_sv, None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@jax.jit
|
|
234
|
+
def _compile_and_run_circuit_jit(state_vector: "jnp.ndarray",
|
|
235
|
+
compiled_ops: "jnp.ndarray") -> "jnp.ndarray":
|
|
236
|
+
"""
|
|
237
|
+
Execute a pre-compiled gate sequence on *state_vector* via jax.lax.scan.
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
state_vector : complex128 array of shape (2**n,)
|
|
242
|
+
compiled_ops : float64 array of shape (n_gates, 4)
|
|
243
|
+
each row = [g_id, q1, q2, param]
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
Final statevector after all gates.
|
|
248
|
+
"""
|
|
249
|
+
final_sv, _ = jax.lax.scan(_apply_gate_fast_step, state_vector, compiled_ops)
|
|
250
|
+
return final_sv
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
254
|
+
# QuantumTranspiler
|
|
255
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
class QuantumTranspiler:
|
|
258
|
+
"""
|
|
259
|
+
Gate-level transpiler: decomposes non-native gates into the native
|
|
260
|
+
{H, T, Tdg, CX, CZ} basis and performs basic circuit optimisations.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def decompose_toffoli(c1: int, c2: int, t: int) -> List[Tuple]:
|
|
265
|
+
"""
|
|
266
|
+
Decompose CCX (Toffoli) into 15 native gates using the
|
|
267
|
+
standard T/Tdg/CX Barenco decomposition.
|
|
268
|
+
|
|
269
|
+
Gate count: 6 CX + 7 single-qubit (H, T, Tdg) = 15 total.
|
|
270
|
+
"""
|
|
271
|
+
return [
|
|
272
|
+
('h', t),
|
|
273
|
+
('cx', c2, t), ('tdg', t),
|
|
274
|
+
('cx', c1, t), ('t', t),
|
|
275
|
+
('cx', c2, t), ('tdg', t),
|
|
276
|
+
('cx', c1, t),
|
|
277
|
+
('t', c2), ('t', t),
|
|
278
|
+
('cx', c1, c2), ('h', t),
|
|
279
|
+
('t', c1), ('tdg', c2),
|
|
280
|
+
('cx', c1, c2),
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
@staticmethod
|
|
284
|
+
def decompose_swap(q1: int, q2: int) -> List[Tuple]:
|
|
285
|
+
"""Decompose SWAP into 3 CX gates."""
|
|
286
|
+
return [('cx', q1, q2), ('cx', q2, q1), ('cx', q1, q2)]
|
|
287
|
+
|
|
288
|
+
@classmethod
|
|
289
|
+
def transpile(cls, circuit: List[Tuple]) -> List[Tuple]:
|
|
290
|
+
"""
|
|
291
|
+
Expand CCX → 15 native gates and SWAP → 3 CX.
|
|
292
|
+
All other gates are passed through unchanged.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
circuit : list of tuples (gate_name, qubit, ...)
|
|
297
|
+
|
|
298
|
+
Returns
|
|
299
|
+
-------
|
|
300
|
+
Expanded circuit as a list of tuples.
|
|
301
|
+
"""
|
|
302
|
+
out: List[Tuple] = []
|
|
303
|
+
for cmd in circuit:
|
|
304
|
+
name = cmd[0].lower() # BUG FIX: was cmd.lower() on a tuple
|
|
305
|
+
if name == 'ccx':
|
|
306
|
+
out.extend(cls.decompose_toffoli(*cmd[1:4]))
|
|
307
|
+
elif name == 'swap':
|
|
308
|
+
out.extend(cls.decompose_swap(*cmd[1:3]))
|
|
309
|
+
else:
|
|
310
|
+
out.append(cmd)
|
|
311
|
+
return out
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from registry import HAS_JAX
|
|
3
|
+
|
|
4
|
+
if HAS_JAX:
|
|
5
|
+
import jax.numpy as jnp
|
|
6
|
+
|
|
7
|
+
INV2 = 1.0 / np.sqrt(2.0)
|
|
8
|
+
|
|
9
|
+
GATES = {
|
|
10
|
+
'h': INV2 * np.array([[1.0, 1.0], [1.0, -1.0]], dtype=complex),
|
|
11
|
+
'x': np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex),
|
|
12
|
+
'y': np.array([[0.0, -1j], [1j, 0.0]], dtype=complex),
|
|
13
|
+
'z': np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex),
|
|
14
|
+
's': np.array([[1.0, 0.0], [0.0, 1j]], dtype=complex),
|
|
15
|
+
'sdg': np.array([[1.0, 0.0], [0.0, -1j]], dtype=complex),
|
|
16
|
+
't': np.array([[1.0, 0.0], [0.0, np.exp(1j * np.pi / 4)]], dtype=complex),
|
|
17
|
+
'tdg': np.array([[1.0, 0.0], [0.0, np.exp(-1j * np.pi / 4)]], dtype=complex),
|
|
18
|
+
'sx': 0.5 * np.array([[1 + 1j, 1 - 1j], [1 - 1j, 1 + 1j]], dtype=complex),
|
|
19
|
+
'id': np.eye(2, dtype=complex),
|
|
20
|
+
'cx': np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]], dtype=complex),
|
|
21
|
+
'cz': np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,-1]], dtype=complex),
|
|
22
|
+
'cy': np.array([[1,0,0,0],[0,1,0,0],[0,0,0,-1j],[0,0,1j,0]], dtype=complex),
|
|
23
|
+
'swap': np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]], dtype=complex),
|
|
24
|
+
'iswap': np.array([[1,0,0,0],[0,0,1j,0],[0,1j,0,0],[0,0,0,1]], dtype=complex),
|
|
25
|
+
'ecr': INV2 * np.array([[0,0,1,1j],[0,0,1j,1],[1,-1j,0,0],[-1j,1,0,0]], dtype=complex),
|
|
26
|
+
'ccx': np.array([[1,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0],[0,0,1,0,0,0,0,0],[0,0,0,1,0,0,0,0],[0,0,0,0,1,0,0,0],[0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,0,1,0]], dtype=complex)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
GATE_IDS = {
|
|
30
|
+
'id': 0, 'h': 1, 'x': 2, 'y': 3, 'z': 4, 's': 5, 'sdg': 6, 't': 7, 'tdg': 8,
|
|
31
|
+
'rx': 9, 'ry': 10, 'rz': 11, 'cx': 20, 'cz': 21
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def _build_parametric_gates():
|
|
35
|
+
if HAS_JAX:
|
|
36
|
+
return {
|
|
37
|
+
'rx': lambda theta: jnp.array([[jnp.cos(theta/2), -1j*jnp.sin(theta/2)], [-1j*jnp.sin(theta/2), jnp.cos(theta/2)]], dtype=complex),
|
|
38
|
+
'ry': lambda theta: jnp.array([[jnp.cos(theta/2), -jnp.sin(theta/2)], [jnp.sin(theta/2), jnp.cos(theta/2)]], dtype=complex),
|
|
39
|
+
'rz': lambda theta: jnp.array([[jnp.exp(-1j*theta/2), 0.0], [0.0, jnp.exp(1j*theta/2)]], dtype=complex),
|
|
40
|
+
'cp': lambda lam: jnp.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,jnp.exp(1j*lam)]], dtype=complex),
|
|
41
|
+
'crz': lambda theta: jnp.array([[1,0,0,0],[0,1,0,0],[0,0,jnp.exp(-1j*theta/2),0],[0,0,0,jnp.exp(1j*theta/2)]], dtype=complex),
|
|
42
|
+
'u3': lambda theta, phi, lam: jnp.array([[jnp.cos(theta/2), -jnp.exp(1j*lam)*jnp.sin(theta/2)], [jnp.exp(1j*phi)*jnp.sin(theta/2), jnp.exp(1j*(phi+lam))*jnp.cos(theta/2)]], dtype=complex),
|
|
43
|
+
'u2': lambda phi, lam: jnp.array([[1.0, -jnp.exp(1j*lam)], [jnp.exp(1j*phi), jnp.exp(1j*(phi+lam))]], dtype=complex) * INV2,
|
|
44
|
+
'u1': lambda lam: jnp.array([[1.0, 0.0], [0.0, jnp.exp(1j*lam)]], dtype=complex),
|
|
45
|
+
'p': lambda lam: jnp.array([[1.0, 0.0], [0.0, jnp.exp(1j*lam)]], dtype=complex)
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
'rx': lambda theta: np.array([[np.cos(theta/2), -1j*np.sin(theta/2)], [-1j*np.sin(theta/2), np.cos(theta/2)]], dtype=complex),
|
|
49
|
+
'ry': lambda theta: np.array([[np.cos(theta/2), -np.sin(theta/2)], [np.sin(theta/2), np.cos(theta/2)]], dtype=complex),
|
|
50
|
+
'rz': lambda theta: np.array([[np.exp(-1j*theta/2), 0.0], [0.0, np.exp(1j*theta/2)]], dtype=complex),
|
|
51
|
+
'cp': lambda lam: np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,np.exp(1j*lam)]], dtype=complex),
|
|
52
|
+
'crz': lambda theta: np.array([[1,0,0,0],[0,1,0,0],[0,0,np.exp(-1j*theta/2),0],[0,0,0,np.exp(1j*theta/2)]], dtype=complex),
|
|
53
|
+
'u3': lambda theta, phi, lam: np.array([[np.cos(theta/2), -np.exp(1j*lam)*np.sin(theta/2)], [np.exp(1j*phi)*np.sin(theta/2), np.exp(1j*(phi+lam))*np.cos(theta/2)]], dtype=complex),
|
|
54
|
+
'u2': lambda phi, lam: np.array([[1.0, -np.exp(1j*lam)], [np.exp(1j*phi), np.exp(1j*(phi+lam))]], dtype=complex) * INV2,
|
|
55
|
+
'u1': lambda lam: np.array([[1.0, 0.0], [0.0, np.exp(1j*lam)]], dtype=complex),
|
|
56
|
+
'p': lambda lam: np.array([[1.0, 0.0], [0.0, np.exp(1j*lam)]], dtype=complex)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
PARAMETRIC_GATES = _build_parametric_gates()
|
|
60
|
+
|
|
61
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import numpy as np
|
|
3
|
+
from dense_evolution import DenseSVSimulator, QASMParser, QuantumTranspiler, NoiseModel
|
|
4
|
+
|
|
5
|
+
print("====================================================")
|
|
6
|
+
print("🔬 ADVANCED PRODUCTION STRESS-TEST: KRAUS & JIT SIMULATION")
|
|
7
|
+
print("====================================================")
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
qasm_bench = """
|
|
11
|
+
OPENQASM 2.0;
|
|
12
|
+
include "qelib1.inc";
|
|
13
|
+
qreg q[6];
|
|
14
|
+
h q[0];
|
|
15
|
+
cx q[0], q[1];
|
|
16
|
+
cx q[1], q[2];
|
|
17
|
+
cx q[2], q[3];
|
|
18
|
+
cx q[3], q[4];
|
|
19
|
+
cx q[4], q[5];
|
|
20
|
+
rx(1.570796) q[0];
|
|
21
|
+
ry(0.785398) q[1];
|
|
22
|
+
rz(0.392699) q[2];
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
print("1. Parsing e Transpilazione del circuito QASM...")
|
|
26
|
+
# Questo testa la corretta importazione e funzione di parser.py e compiler.py
|
|
27
|
+
parser = QASMParser()
|
|
28
|
+
circ = parser.parse(qasm_bench)
|
|
29
|
+
tuples = QuantumTranspiler.transpile(circ.to_tuples())
|
|
30
|
+
n_qubits = circ.n_qubits
|
|
31
|
+
print(f"✓ Parsing completato. Qubit: {n_qubits}, Gate transpilati: {len(tuples)}")
|
|
32
|
+
|
|
33
|
+
print("\n2. Validazione Core JIT in modalità ideale (simulator.py)...")
|
|
34
|
+
t0 = time.perf_counter()
|
|
35
|
+
sim_ideale = DenseSVSimulator(n_qubits)
|
|
36
|
+
sim_ideale.run_circuit_jit_beast_mode(tuples)
|
|
37
|
+
t_ideale = time.perf_counter() - t0
|
|
38
|
+
prob_ideale = sim_ideale.get_probabilities()
|
|
39
|
+
print(f"✓ Successo ideale in {t_ideale:.4f} s | Dimensione stato: {len(prob_ideale)}")
|
|
40
|
+
|
|
41
|
+
print("\n3. Validazione NoiseModel (registry.py): Canale stocastico Amplitude Damping...")
|
|
42
|
+
|
|
43
|
+
# Run Noisy 1
|
|
44
|
+
t0 = time.perf_counter()
|
|
45
|
+
sim_noisy1 = DenseSVSimulator(n_qubits)
|
|
46
|
+
sim_noisy1.run_circuit_jit_beast_mode(tuples)
|
|
47
|
+
# Iniezione diretta del rumore Kraus sullo statevector compilato
|
|
48
|
+
sim_noisy1.sv = NoiseModel.apply_to_sv(sim_noisy1.sv, n_qubits, model='amplitude_damping', p=0.15)
|
|
49
|
+
prob_noisy1 = sim_noisy1.get_probabilities()
|
|
50
|
+
t_noisy1 = time.perf_counter() - t0
|
|
51
|
+
|
|
52
|
+
# Run Noisy 2
|
|
53
|
+
sim_noisy2 = DenseSVSimulator(n_qubits)
|
|
54
|
+
sim_noisy2.run_circuit_jit_beast_mode(tuples)
|
|
55
|
+
sim_noisy2.sv = NoiseModel.apply_to_sv(sim_noisy2.sv, n_qubits, model='amplitude_damping', p=0.15)
|
|
56
|
+
prob_noisy2 = sim_noisy2.get_probabilities()
|
|
57
|
+
|
|
58
|
+
print(f"✓ Successo noisy in {t_noisy1:.4f} s")
|
|
59
|
+
|
|
60
|
+
print("\n4. Analisi statistica delle fluttuazioni (Shot Noise Reale)...")
|
|
61
|
+
scostamento_stocastico = np.linalg.norm(prob_noisy1 - prob_noisy2)
|
|
62
|
+
scostamento_ideale = np.linalg.norm(prob_ideale - prob_noisy1)
|
|
63
|
+
|
|
64
|
+
print(f"🔬 Distanza ideale-noisy: {scostamento_ideale:.6f}")
|
|
65
|
+
print(f"🔬 Fluttuazione quantistica indipendente (Shot Noise): {scostamento_stocastico:.6f}")
|
|
66
|
+
|
|
67
|
+
if scostamento_stocastico > 1e-12:
|
|
68
|
+
print("\n✅ TEST SUPERATO: Moduli collegati, PRNG JAX funzionante e rumore autenticamente non-deterministico!")
|
|
69
|
+
else:
|
|
70
|
+
print("\n❌ FALLIMENTO: Il rumore è ancora lineare o statico. Le due run hanno prodotto stati identici.")
|
|
71
|
+
|
|
72
|
+
print("====================================================")
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"\n❌ CRASH DURANTE L'ELABORAZIONE HARDWARE: {str(e)}")
|
|
76
|
+
print("====================================================")
|