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.
Files changed (25) hide show
  1. dense_evolution-8.0.5/Dense_Evolution_Library/__init__.py +12 -0
  2. dense_evolution-8.0.5/Dense_Evolution_Library/compiler.py +311 -0
  3. dense_evolution-8.0.5/Dense_Evolution_Library/gates.py +61 -0
  4. dense_evolution-8.0.5/Dense_Evolution_Library/mega_stress_test.py +76 -0
  5. dense_evolution-8.0.5/Dense_Evolution_Library/parser.py +424 -0
  6. dense_evolution-8.0.5/Dense_Evolution_Library/registry.py +372 -0
  7. dense_evolution-8.0.5/Dense_Evolution_Library/simulator.py +488 -0
  8. dense_evolution-8.0.5/Dense_Evolution_Library/stress_test.py +76 -0
  9. dense_evolution-8.0.5/Dense_Evolution_Library/stress_test_quantum.py +76 -0
  10. dense_evolution-8.0.5/Dense_Evolution_Library/test_library.py +39 -0
  11. {dense_evolution-8.0.4 → dense_evolution-8.0.5}/PKG-INFO +1709 -1706
  12. {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dense_evolution.egg-info/PKG-INFO +1709 -1706
  13. dense_evolution-8.0.5/dense_evolution.egg-info/SOURCES.txt +19 -0
  14. dense_evolution-8.0.5/dense_evolution.egg-info/top_level.txt +2 -0
  15. dense_evolution-8.0.5/license.md +58 -0
  16. dense_evolution-8.0.5/pyproject.toml +87 -0
  17. {dense_evolution-8.0.4 → dense_evolution-8.0.5}/setup.cfg +4 -4
  18. dense_evolution-8.0.4/dense_evolution.egg-info/SOURCES.txt +0 -9
  19. dense_evolution-8.0.4/dense_evolution.egg-info/top_level.txt +0 -2
  20. dense_evolution-8.0.4/dense_evolution.py +0 -1948
  21. dense_evolution-8.0.4/pyproject.toml +0 -38
  22. {dense_evolution-8.0.4 → dense_evolution-8.0.5}/README.md +0 -0
  23. {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dash.py +0 -0
  24. {dense_evolution-8.0.4 → dense_evolution-8.0.5}/dense_evolution.egg-info/dependency_links.txt +0 -0
  25. {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("====================================================")