bloqade-circuit 0.7.5__py3-none-any.whl → 0.7.7__py3-none-any.whl

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.

Potentially problematic release.


This version of bloqade-circuit might be problematic. Click here for more details.

bloqade/pyqrack/device.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any, TypeVar, ParamSpec
1
+ from typing import Any, TypeVar, ParamSpec, NamedTuple
2
2
  from dataclasses import field, dataclass
3
3
 
4
4
  import numpy as np
@@ -28,9 +28,99 @@ RetType = TypeVar("RetType")
28
28
  Params = ParamSpec("Params")
29
29
 
30
30
 
31
+ class QuantumState(NamedTuple):
32
+ """
33
+ A representation of a quantum state as a density matrix, where the density matrix is
34
+ rho = sum_i eigenvalues[i] |eigenvectors[:,i]><eigenvectors[:,i]|.
35
+
36
+ This reprsentation is efficient for low-rank density matrices by only storing
37
+ the non-zero eigenvalues and corresponding eigenvectors of the density matrix.
38
+ For example, a pure state has only one non-zero eigenvalue equal to 1.0.
39
+
40
+ Endianness and qubit ordering of the state vector is consistent with Cirq, where
41
+ eigenvectors[0,0] corresponds to the amplitude of the |00..000> element of the zeroth eigenvector;
42
+ eigenvectors[1,0] corresponds to the amplitude of the |00..001> element of the zeroth eigenvector;
43
+ eigenvectors[3,0] corresponds to the amplitude of the |00..011> element of the zeroth eigenvector;
44
+ eigenvectors[-1,0] corresponds to the amplitude of the |11..111> element of the zeroth eigenvector.
45
+ A flip of the LAST bit |00..000><00..001| corresponds to applying a PauliX gate to the FIRST qubit.
46
+ A flip of the FIRST bit |00..000><10..000| corresponds to applying a PauliX gate to the LAST qubit.
47
+
48
+ Attributes:
49
+ eigenvalues (1d np.ndarray):
50
+ The non-zero eigenvalues of the density matrix.
51
+ eigenvectors (2d np.ndarray):
52
+ The corresponding eigenvectors of the density matrix,
53
+ where eigenvectors[:,i] is the i-th eigenvector.
54
+ Methods:
55
+ Not Implemented, pending https://github.com/QuEraComputing/bloqade-circuit/issues/447
56
+ """
57
+
58
+ eigenvalues: np.ndarray
59
+ eigenvectors: np.ndarray
60
+
61
+ def canonicalize(self, tol: float = 1e-12) -> "QuantumState":
62
+ raise NotImplementedError(
63
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
64
+ )
65
+
66
+ def __add__(self, other: "QuantumState") -> "QuantumState":
67
+ raise NotImplementedError(
68
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
69
+ )
70
+
71
+ def __mul__(self, scalar: float) -> "QuantumState":
72
+ raise NotImplementedError(
73
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
74
+ )
75
+
76
+ @property
77
+ def dense(self) -> np.ndarray[tuple[int, int], np.complexfloating]:
78
+ raise NotImplementedError(
79
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
80
+ )
81
+
82
+ def __matmul__(self, right: "cirq.Circuit") -> "QuantumState": # noqa: F821
83
+ raise NotImplementedError(
84
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
85
+ )
86
+
87
+ def expect(self, operator: Any) -> float:
88
+ raise NotImplementedError(
89
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
90
+ )
91
+
92
+ def probability(self) -> np.ndarray[tuple[int], np.floating]:
93
+ raise NotImplementedError(
94
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
95
+ )
96
+
97
+ def von_neumann_entropy(self) -> float:
98
+ raise NotImplementedError(
99
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
100
+ )
101
+
102
+ @property
103
+ def qubit_basis(self) -> list[PyQrackQubit]:
104
+ raise NotImplementedError(
105
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
106
+ )
107
+
108
+ def reduced_density_matrix(
109
+ self, qubits: list[PyQrackQubit], tol: float = 1e-12
110
+ ) -> "QuantumState":
111
+ raise NotImplementedError(
112
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
113
+ )
114
+
115
+ def overlap(self, other: "QuantumState") -> complex:
116
+ raise NotImplementedError(
117
+ "https://github.com/QuEraComputing/bloqade-circuit/issues/447"
118
+ )
119
+
120
+
31
121
  def _pyqrack_reduced_density_matrix(
32
122
  inds: tuple[int, ...], sim_reg: QrackSimulator, tol: float = 1e-12
33
- ) -> "np.linalg._linalg.EighResult":
123
+ ) -> QuantumState:
34
124
  """
35
125
  Extract the reduced density matrix representing the state of a list
36
126
  of qubits from a PyQRack simulator register.
@@ -73,7 +163,7 @@ def _pyqrack_reduced_density_matrix(
73
163
  s = s[:, nonzero_inds]
74
164
  v = v[nonzero_inds] ** 2
75
165
  # Forge into the correct result type
76
- result = np.linalg._linalg.EighResult(eigenvalues=v, eigenvectors=s)
166
+ result = QuantumState(eigenvalues=v, eigenvectors=s)
77
167
  return result
78
168
 
79
169
 
@@ -165,7 +255,7 @@ class PyQrackSimulatorBase(AbstractSimulatorDevice[PyQrackSimulatorTask]):
165
255
  @staticmethod
166
256
  def quantum_state(
167
257
  qubits: list[PyQrackQubit] | IList[PyQrackQubit, Any], tol: float = 1e-12
168
- ) -> "np.linalg._linalg.EighResult":
258
+ ) -> "QuantumState":
169
259
  """
170
260
  Extract the reduced density matrix representing the state of a list
171
261
  of qubits from a PyQRack simulator register.
@@ -177,7 +267,7 @@ class PyQrackSimulatorBase(AbstractSimulatorDevice[PyQrackSimulatorTask]):
177
267
  An eigh result containing the eigenvalues and eigenvectors of the reduced density matrix.
178
268
  """
179
269
  if len(qubits) == 0:
180
- return np.linalg._linalg.EighResult(
270
+ return QuantumState(
181
271
  eigenvalues=np.array([]), eigenvectors=np.array([]).reshape(0, 0)
182
272
  )
183
273
  sim_reg = qubits[0].sim_reg
bloqade/pyqrack/task.py CHANGED
@@ -1,6 +1,10 @@
1
1
  from typing import TypeVar, ParamSpec, cast
2
+ from collections import Counter
2
3
  from dataclasses import dataclass
3
4
 
5
+ import numpy as np
6
+ from kirin.dialects.ilist import IList
7
+
4
8
  from bloqade.task import AbstractSimulatorTask
5
9
  from bloqade.pyqrack.reg import QubitState, PyQrackQubit
6
10
  from bloqade.pyqrack.base import (
@@ -51,3 +55,93 @@ class PyQrackSimulatorTask(AbstractSimulatorTask[Param, RetType, MemoryType]):
51
55
  except AttributeError:
52
56
  Warning("Task has not been run, there are no qubits!")
53
57
  return []
58
+
59
+ def batch_run(self, shots: int = 1) -> dict[RetType, float]:
60
+ """
61
+ Repeatedly run the task to collect statistics on the shot outcomes.
62
+ The average is done over [shots] repetitions and thus is frequentist
63
+ and converges to exact only in the shots -> infinity limit.
64
+
65
+ Args:
66
+ shots (int):
67
+ the number of repetitions of the task
68
+ Returns:
69
+ dict[RetType, float]:
70
+ a dictionary mapping outcomes to their probabilities,
71
+ as estimated from counting the shot outcomes. RetType must be hashable.
72
+ """
73
+
74
+ results: list[RetType] = [self.run() for _ in range(shots)]
75
+
76
+ # Convert IList to tuple so that it is hashable by Counter
77
+ def convert(data):
78
+ if isinstance(data, (list, IList)):
79
+ return tuple(convert(item) for item in data)
80
+ return data
81
+
82
+ results = convert(results)
83
+
84
+ data = {
85
+ key: value / len(results) for key, value in Counter(results).items()
86
+ } # Normalize to probabilities
87
+ return data
88
+
89
+ def batch_state(
90
+ self, shots: int = 1, qubit_map: None = None
91
+ ) -> "QuantumState": # noqa: F821
92
+ """
93
+ Repeatedly run the task to extract the averaged quantum state.
94
+ The average is done over [shots] repetitions and thus is frequentist
95
+ and converges to exact only in the shots -> infinity limit.
96
+
97
+ Args:
98
+ shots (int):
99
+ the number of repetitions of the task
100
+ qubit_map (callable | None):
101
+ an optional callable that takes the output of self.run() and extract
102
+ the [returned] qubits to be used for the quantum state.
103
+ If None, all qubits in the simulator are used, in the order set by the simulator.
104
+ If callable, qubit_map must have the signature
105
+ > qubit_map(output:RetType) -> list[PyQrackQubit]
106
+ and the averaged state is
107
+ > quantum_state(qubit_map(self.run())).
108
+ If qubit_map is not None, self.run() must return qubit(s).
109
+ Two common patterns here are:
110
+ > qubit_map = lambda qubits: qubits
111
+ for the case where self.run() returns a list of qubits, or
112
+ > qubit_map = lambda qubit: [qubits]
113
+ for the case where self.run() returns a single qubit.
114
+ Returns:
115
+ QuantumState:
116
+ the averaged quantum state as a density matrix,
117
+ represented in its eigenbasis.
118
+ """
119
+ # Import here to avoid circular dependencies.
120
+ from bloqade.pyqrack.device import QuantumState, PyQrackSimulatorBase
121
+
122
+ states: list[QuantumState] = []
123
+ for _ in range(shots):
124
+ res = self.run()
125
+ if callable(qubit_map):
126
+ qbs = qubit_map(res)
127
+ else:
128
+ qbs = self.qubits()
129
+ states.append(PyQrackSimulatorBase.quantum_state(qbs))
130
+
131
+ state = QuantumState(
132
+ eigenvectors=np.concatenate(
133
+ [state.eigenvectors for state in states], axis=1
134
+ ),
135
+ eigenvalues=np.concatenate([state.eigenvalues for state in states], axis=0)
136
+ / len(states),
137
+ )
138
+
139
+ # Canonicalize the state by orthoganalizing the basis vectors.
140
+ tol = 1e-7
141
+ s, v, d = np.linalg.svd(
142
+ state.eigenvectors * np.sqrt(state.eigenvalues), full_matrices=False
143
+ )
144
+ mask = v > tol
145
+ v = v[mask] ** 2
146
+ s = s[:, mask]
147
+ return QuantumState(eigenvalues=v, eigenvectors=s)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bloqade-circuit
3
- Version: 0.7.5
3
+ Version: 0.7.7
4
4
  Summary: The software development toolkit for neutral atom arrays.
5
5
  Author-email: Roger-luo <rluo@quera.com>, kaihsin <khwu@quera.com>, weinbe58 <pweinberg@quera.com>, johnzl-777 <jlong@quera.com>
6
6
  License-File: LICENSE
@@ -9,8 +9,8 @@ Requires-Dist: kirin-toolchain~=0.17.23
9
9
  Requires-Dist: numpy>=1.22.0
10
10
  Requires-Dist: pandas>=2.2.3
11
11
  Requires-Dist: pydantic<2.11.0,>=1.3.0
12
- Requires-Dist: pyqrack-cpu<1.41,>=1.38.2; sys_platform != 'darwin'
13
- Requires-Dist: pyqrack<1.41,>=1.38.2; sys_platform == 'darwin'
12
+ Requires-Dist: pyqrack-cpu>=1.69.0; sys_platform != 'darwin-arm64'
13
+ Requires-Dist: pyqrack>=1.69.0; sys_platform == 'darwin-arm64'
14
14
  Requires-Dist: rich>=13.9.4
15
15
  Requires-Dist: scipy>=1.13.1
16
16
  Provides-Extra: cirq
@@ -18,9 +18,9 @@ Requires-Dist: cirq-core>=1.4.1; extra == 'cirq'
18
18
  Requires-Dist: cirq-core[contrib]>=1.4.1; extra == 'cirq'
19
19
  Requires-Dist: qpsolvers[clarabel]>=4.7.0; extra == 'cirq'
20
20
  Provides-Extra: pyqrack-cuda
21
- Requires-Dist: pyqrack-cuda>=1.38.2; extra == 'pyqrack-cuda'
21
+ Requires-Dist: pyqrack-cuda>=1.69.0; extra == 'pyqrack-cuda'
22
22
  Provides-Extra: pyqrack-opencl
23
- Requires-Dist: pyqrack>=1.38.2; (sys_platform != 'darwin') and extra == 'pyqrack-opencl'
23
+ Requires-Dist: pyqrack>=1.69.0; (sys_platform == 'darwin-arm64') and extra == 'pyqrack-opencl'
24
24
  Provides-Extra: qasm2
25
25
  Requires-Dist: lark>=1.2.2; extra == 'qasm2'
26
26
  Provides-Extra: qbraid
@@ -33,11 +33,11 @@ bloqade/native/stdlib/broadcast.py,sha256=0DEK6pRcz0IG5BZHt0sV1wZpDvlJgvw4-Qwd1x
33
33
  bloqade/native/stdlib/simple.py,sha256=mK_p9x_TUr4g-q-Q4LfbVBtXxBS21XPh1meyCC4to_k,5416
34
34
  bloqade/pyqrack/__init__.py,sha256=OV8-2fw44gP_JgY8mAUiwISO_qYxS-t0fKsbuUB-r9Y,861
35
35
  bloqade/pyqrack/base.py,sha256=g0GRlEgyJ_P8z-lR8RK2CAuRTj6KPfglKX0iwrgg4DM,4408
36
- bloqade/pyqrack/device.py,sha256=-zlr1lSzOvcj5l28nnevy1oMYct79DTOdLYyGaT2Yco,11567
36
+ bloqade/pyqrack/device.py,sha256=ruseNWt3scvjw0KyHZjwnM6Z6lFjROgmW3Zdi1lucgQ,15195
37
37
  bloqade/pyqrack/native.py,sha256=ErbVQCatn_JT3Ej-iQzMMfb_q50JF_K1Iv1vRYvu5VA,1857
38
38
  bloqade/pyqrack/reg.py,sha256=uTL07CT1R0xUsInLmwU9YuuNdV6lV0lCs1zhdUz1qIs,1660
39
39
  bloqade/pyqrack/target.py,sha256=c78VtLWAiDNp_0sXwvVzhaEoeFsr1fUVsupxWuo6p3s,3661
40
- bloqade/pyqrack/task.py,sha256=hzMueE03C-MR_-ti_5Z0lqFZcHDVCxmQHFaX9tPNm2o,1522
40
+ bloqade/pyqrack/task.py,sha256=nRmI3tM_y4134_Uld-D9xwNXG9ie-OHNAqwRT5Ss-Y0,5266
41
41
  bloqade/pyqrack/noise/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  bloqade/pyqrack/noise/native.py,sha256=BmRlRzqCzvSgfSsDlIjuB8L0gx4uaPqmPyfJ5-Ole6M,2751
43
43
  bloqade/pyqrack/qasm2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -243,7 +243,7 @@ bloqade/visual/animation/runtime/atoms.py,sha256=EmjxhujLiHHPS_HtH_B-7TiqeHgvW5u
243
243
  bloqade/visual/animation/runtime/ppoly.py,sha256=JB9IP53N1w6adBJEue6J5Nmj818Id9JvrlgrmiQTU1I,1385
244
244
  bloqade/visual/animation/runtime/qpustate.py,sha256=rlmxQeJSvaohXrTpXQL5y-NJcpvfW33xPaYM1slv7cc,4270
245
245
  bloqade/visual/animation/runtime/utils.py,sha256=ju9IzOWX-vKwfpqUjlUKu3Ssr_UFPFFq-tzH_Nqyo_c,1212
246
- bloqade_circuit-0.7.5.dist-info/METADATA,sha256=mPPVbTxuk3cA4_obi8jd4Pw09pDDbxa3IIp8Z8vZMn4,3850
247
- bloqade_circuit-0.7.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
248
- bloqade_circuit-0.7.5.dist-info/licenses/LICENSE,sha256=S5GIJwR6QCixPA9wryYb44ZEek0Nz4rt_zLUqP05UbU,13160
249
- bloqade_circuit-0.7.5.dist-info/RECORD,,
246
+ bloqade_circuit-0.7.7.dist-info/METADATA,sha256=PoLZ_Qd3XjiPu-9u9HaIJv-07GZAu0XIyuoUwCXVCic,3856
247
+ bloqade_circuit-0.7.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
248
+ bloqade_circuit-0.7.7.dist-info/licenses/LICENSE,sha256=S5GIJwR6QCixPA9wryYb44ZEek0Nz4rt_zLUqP05UbU,13160
249
+ bloqade_circuit-0.7.7.dist-info/RECORD,,