pennylane-qrack 0.10.9__py3-none-macosx_14_0_arm64.whl → 0.25.0__py3-none-macosx_14_0_arm64.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 pennylane-qrack might be problematic. Click here for more details.
- pennylane_qrack/QrackAceDeviceConfig.toml +108 -0
- pennylane_qrack/QrackDeviceConfig.toml +85 -123
- pennylane_qrack/QrackStabilizerDeviceConfig.toml +99 -0
- pennylane_qrack/_version.py +2 -2
- pennylane_qrack/qrack_ace_device.py +447 -0
- pennylane_qrack/qrack_device.cpp +66 -44
- pennylane_qrack/qrack_device.py +50 -17
- pennylane_qrack/qrack_stabilizer_device.py +313 -0
- {pennylane_qrack-0.10.9.dist-info → pennylane_qrack-0.25.0.dist-info}/METADATA +7 -3
- pennylane_qrack-0.25.0.dist-info/RECORD +15 -0
- {pennylane_qrack-0.10.9.dist-info → pennylane_qrack-0.25.0.dist-info}/WHEEL +1 -1
- pennylane_qrack-0.25.0.dist-info/entry_points.txt +4 -0
- pennylane_qrack/CMakeCache.txt +0 -394
- pennylane_qrack/Makefile +0 -230
- pennylane_qrack/cmake_install.cmake +0 -78
- pennylane_qrack/libqrack_device.dylib +0 -0
- pennylane_qrack-0.10.9.dist-info/RECORD +0 -15
- pennylane_qrack-0.10.9.dist-info/entry_points.txt +0 -2
- {pennylane_qrack-0.10.9.dist-info → pennylane_qrack-0.25.0.dist-info}/LICENSE +0 -0
- {pennylane_qrack-0.10.9.dist-info → pennylane_qrack-0.25.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Copyright 2020 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""
|
|
15
|
+
Base device class for PennyLane-Qrack.
|
|
16
|
+
"""
|
|
17
|
+
from functools import reduce
|
|
18
|
+
import cmath, math
|
|
19
|
+
import os
|
|
20
|
+
import pathlib
|
|
21
|
+
import sys
|
|
22
|
+
import itertools as it
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
# PennyLane v0.42 introduced the `exceptions` module and will raise
|
|
27
|
+
# deprecation warnings if they are imported from the top-level module.
|
|
28
|
+
|
|
29
|
+
# This ensures backwards compatibility with older versions of PennyLane.
|
|
30
|
+
try:
|
|
31
|
+
from pennylane.exceptions import DeviceError, QuantumFunctionError
|
|
32
|
+
except (ModuleNotFoundError, ImportError) as import_error:
|
|
33
|
+
from pennylane import DeviceError, QuantumFunctionError
|
|
34
|
+
|
|
35
|
+
from pennylane.devices import QubitDevice
|
|
36
|
+
from pennylane.ops import (
|
|
37
|
+
BasisState,
|
|
38
|
+
Adjoint,
|
|
39
|
+
)
|
|
40
|
+
from pennylane.wires import Wires
|
|
41
|
+
|
|
42
|
+
from pyqrack import QrackStabilizer, Pauli
|
|
43
|
+
|
|
44
|
+
from ._version import __version__
|
|
45
|
+
from sys import platform as _platform
|
|
46
|
+
|
|
47
|
+
# tolerance for numerical errors
|
|
48
|
+
tolerance = 1e-10
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class QrackStabilizerDevice(QubitDevice):
|
|
52
|
+
"""Qrack Stabilizer device"""
|
|
53
|
+
|
|
54
|
+
name = "Qrack stabilizer device"
|
|
55
|
+
short_name = "qrack.stabilizer"
|
|
56
|
+
pennylane_requires = ">=0.11.0"
|
|
57
|
+
version = __version__
|
|
58
|
+
author = "Daniel Strano, adapted from Steven Oud and Xanadu"
|
|
59
|
+
|
|
60
|
+
_capabilities = {
|
|
61
|
+
"model": "qubit",
|
|
62
|
+
"tensor_observables": True,
|
|
63
|
+
"inverse_operations": True,
|
|
64
|
+
"returns_state": True,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_observable_map = {
|
|
68
|
+
"PauliX": Pauli.PauliX,
|
|
69
|
+
"PauliY": Pauli.PauliY,
|
|
70
|
+
"PauliZ": Pauli.PauliZ,
|
|
71
|
+
"Identity": Pauli.PauliI,
|
|
72
|
+
"Hadamard": None,
|
|
73
|
+
"Hermitian": None,
|
|
74
|
+
"Prod": None,
|
|
75
|
+
# "Sum": None,
|
|
76
|
+
# "SProd": None,
|
|
77
|
+
# "Exp": None,
|
|
78
|
+
# "Projector": None,
|
|
79
|
+
# "Hamiltonian": None,
|
|
80
|
+
# "SparseHamiltonian": None
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
observables = _observable_map.keys()
|
|
84
|
+
operations = {
|
|
85
|
+
"Identity",
|
|
86
|
+
"C(Identity)",
|
|
87
|
+
"SWAP",
|
|
88
|
+
"ISWAP",
|
|
89
|
+
"PSWAP",
|
|
90
|
+
"CNOT",
|
|
91
|
+
"CY",
|
|
92
|
+
"CZ",
|
|
93
|
+
"S",
|
|
94
|
+
"PauliX",
|
|
95
|
+
"C(PauliX)",
|
|
96
|
+
"PauliY",
|
|
97
|
+
"C(PauliY)",
|
|
98
|
+
"PauliZ",
|
|
99
|
+
"C(PauliZ)",
|
|
100
|
+
"Hadamard",
|
|
101
|
+
"SX",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
config_filepath = pathlib.Path(
|
|
105
|
+
os.path.dirname(sys.modules[__name__].__file__) + "/QrackStabilizerDeviceConfig.toml"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def __init__(self, wires=0, shots=None, **kwargs):
|
|
109
|
+
super().__init__(wires=wires, shots=shots)
|
|
110
|
+
self.shots = shots
|
|
111
|
+
self._state = QrackStabilizer(self.num_wires)
|
|
112
|
+
self.device_kwargs = {}
|
|
113
|
+
|
|
114
|
+
def _reverse_state(self):
|
|
115
|
+
end = self.num_wires - 1
|
|
116
|
+
mid = self.num_wires >> 1
|
|
117
|
+
for i in range(mid):
|
|
118
|
+
self._state.swap(i, end - i)
|
|
119
|
+
|
|
120
|
+
def apply(self, operations, **kwargs):
|
|
121
|
+
for op in operations:
|
|
122
|
+
if isinstance(op, BasisState):
|
|
123
|
+
self._apply_basis_state(op)
|
|
124
|
+
else:
|
|
125
|
+
self._apply_gate(op)
|
|
126
|
+
|
|
127
|
+
def _apply_basis_state(self, op):
|
|
128
|
+
"""Initialize a basis state"""
|
|
129
|
+
wires = self.map_wires(Wires(op.wires))
|
|
130
|
+
par = op.parameters[0]
|
|
131
|
+
wire_count = len(wires)
|
|
132
|
+
n_basis_state = len(par)
|
|
133
|
+
|
|
134
|
+
if not set(par).issubset({0, 1}):
|
|
135
|
+
raise ValueError("BasisState parameter must consist of 0 or 1 integers.")
|
|
136
|
+
if n_basis_state != wire_count:
|
|
137
|
+
raise ValueError("BasisState parameter and wires must be of equal length.")
|
|
138
|
+
|
|
139
|
+
for i in range(wire_count):
|
|
140
|
+
index = wires.labels[i]
|
|
141
|
+
if par[i] != self._state.m(index):
|
|
142
|
+
self._state.x(index)
|
|
143
|
+
|
|
144
|
+
def _apply_gate(self, op):
|
|
145
|
+
"""Apply native qrack gate"""
|
|
146
|
+
|
|
147
|
+
opname = op.name
|
|
148
|
+
if isinstance(op, Adjoint):
|
|
149
|
+
op = op.base
|
|
150
|
+
opname = op.name + ".inv"
|
|
151
|
+
|
|
152
|
+
par = op.parameters
|
|
153
|
+
|
|
154
|
+
# translate op wire labels to consecutive wire labels used by the device
|
|
155
|
+
device_wires = self.map_wires(
|
|
156
|
+
(op.control_wires + op.wires) if op.control_wires else op.wires
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if opname in [
|
|
160
|
+
"".join(p)
|
|
161
|
+
for p in it.product(
|
|
162
|
+
[
|
|
163
|
+
"CNOT",
|
|
164
|
+
"C(PauliX)",
|
|
165
|
+
],
|
|
166
|
+
["", ".inv"],
|
|
167
|
+
)
|
|
168
|
+
]:
|
|
169
|
+
self._state.mcx(device_wires.labels[:-1], device_wires.labels[-1])
|
|
170
|
+
elif opname in ["C(PauliY)", "C(PauliY).inv"]:
|
|
171
|
+
self._state.mcy(device_wires.labels[:-1], device_wires.labels[-1])
|
|
172
|
+
elif opname in ["C(PauliZ)", "C(PauliZ).inv"]:
|
|
173
|
+
self._state.mcz(device_wires.labels[:-1], device_wires.labels[-1])
|
|
174
|
+
elif opname in ["SWAP", "SWAP.inv"]:
|
|
175
|
+
self._state.swap(device_wires.labels[0], device_wires.labels[1])
|
|
176
|
+
elif opname == "ISWAP":
|
|
177
|
+
self._state.iswap(device_wires.labels[0], device_wires.labels[1])
|
|
178
|
+
elif opname == "ISWAP.inv":
|
|
179
|
+
self._state.adjiswap(device_wires.labels[0], device_wires.labels[1])
|
|
180
|
+
elif opname in ["CY", "CY.inv", "C(CY)", "C(CY).inv"]:
|
|
181
|
+
self._state.mcy(device_wires.labels[:-1], device_wires.labels[-1])
|
|
182
|
+
elif opname in ["CZ", "CZ.inv", "C(CZ)", "C(CZ).inv"]:
|
|
183
|
+
self._state.mcz(device_wires.labels[:-1], device_wires.labels[-1])
|
|
184
|
+
elif opname == "S":
|
|
185
|
+
for label in device_wires.labels:
|
|
186
|
+
self._state.s(label)
|
|
187
|
+
elif opname == "S.inv":
|
|
188
|
+
for label in device_wires.labels:
|
|
189
|
+
self._state.adjs(label)
|
|
190
|
+
elif opname in ["PauliX", "PauliX.inv"]:
|
|
191
|
+
for label in device_wires.labels:
|
|
192
|
+
self._state.x(label)
|
|
193
|
+
elif opname in ["PauliY", "PauliY.inv"]:
|
|
194
|
+
for label in device_wires.labels:
|
|
195
|
+
self._state.y(label)
|
|
196
|
+
elif opname in ["PauliZ", "PauliZ.inv"]:
|
|
197
|
+
for label in device_wires.labels:
|
|
198
|
+
self._state.z(label)
|
|
199
|
+
elif opname in ["Hadamard", "Hadamard.inv"]:
|
|
200
|
+
for label in device_wires.labels:
|
|
201
|
+
self._state.h(label)
|
|
202
|
+
elif opname == "SX":
|
|
203
|
+
half_pi = math.pi / 2
|
|
204
|
+
for label in device_wires.labels:
|
|
205
|
+
self._state.u(label, half_pi, -half_pi, half.pi)
|
|
206
|
+
elif opname == "SX.inv":
|
|
207
|
+
half_pi = math.pi / 2
|
|
208
|
+
for label in device_wires.labels:
|
|
209
|
+
self._state.u(label, -half_pi, -half.pi, half_pi)
|
|
210
|
+
elif opname not in [
|
|
211
|
+
"Identity",
|
|
212
|
+
"Identity.inv",
|
|
213
|
+
"C(Identity)",
|
|
214
|
+
"C(Identity).inv",
|
|
215
|
+
]:
|
|
216
|
+
raise DeviceError(f"Operation {opname} is not supported on a {self.short_name} device.")
|
|
217
|
+
|
|
218
|
+
def analytic_probability(self, wires=None):
|
|
219
|
+
"""Return the (marginal) analytic probability of each computational basis state."""
|
|
220
|
+
if self._state is None:
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
all_probs = self._abs(self.state) ** 2
|
|
224
|
+
prob = self.marginal_prob(all_probs, wires)
|
|
225
|
+
|
|
226
|
+
if (not "QRACK_FPPOW" in os.environ) or (6 > int(os.environ.get("QRACK_FPPOW"))):
|
|
227
|
+
tot_prob = 0
|
|
228
|
+
for p in prob:
|
|
229
|
+
tot_prob = tot_prob + p
|
|
230
|
+
|
|
231
|
+
if tot_prob != 1.0:
|
|
232
|
+
for i in range(len(prob)):
|
|
233
|
+
prob[i] = prob[i] / tot_prob
|
|
234
|
+
|
|
235
|
+
return prob
|
|
236
|
+
|
|
237
|
+
def expval(self, observable, **kwargs):
|
|
238
|
+
if self.shots is None:
|
|
239
|
+
if isinstance(observable.name, list):
|
|
240
|
+
b = [self._observable_map[obs] for obs in observable.name]
|
|
241
|
+
elif observable.name == "Prod":
|
|
242
|
+
b = [self._observable_map[obs.name] for obs in observable.operands]
|
|
243
|
+
else:
|
|
244
|
+
b = [self._observable_map[observable.name]]
|
|
245
|
+
|
|
246
|
+
if None not in b:
|
|
247
|
+
# This will trigger Gaussian elimination,
|
|
248
|
+
# so it only happens once.
|
|
249
|
+
self._state.try_separate_1qb(0)
|
|
250
|
+
# It's cheap to clone a stabilizer,
|
|
251
|
+
# but we don't want to have to transform
|
|
252
|
+
# back after terminal measurement.
|
|
253
|
+
state_clone = self._state.clone()
|
|
254
|
+
|
|
255
|
+
q = self.map_wires(observable.wires)
|
|
256
|
+
for qb, base in zip(q, b):
|
|
257
|
+
match base:
|
|
258
|
+
case Pauli.PauliX:
|
|
259
|
+
state_clone.h(qb)
|
|
260
|
+
case Pauli.PauliY:
|
|
261
|
+
state_clone.adjs(qb)
|
|
262
|
+
state_clone.h(qb)
|
|
263
|
+
b = [Pauli.PauliZ] * len(b)
|
|
264
|
+
|
|
265
|
+
return self._state.pauli_expectation(q, b)
|
|
266
|
+
|
|
267
|
+
# exact expectation value
|
|
268
|
+
if callable(observable.eigvals):
|
|
269
|
+
eigvals = self._asarray(observable.eigvals(), dtype=self.R_DTYPE)
|
|
270
|
+
else: # older version of pennylane
|
|
271
|
+
eigvals = self._asarray(observable.eigvals, dtype=self.R_DTYPE)
|
|
272
|
+
prob = self.probability(wires=observable.wires)
|
|
273
|
+
return self._dot(eigvals, prob)
|
|
274
|
+
|
|
275
|
+
# estimate the ev
|
|
276
|
+
return np.mean(self.sample(observable))
|
|
277
|
+
|
|
278
|
+
def _generate_sample(self):
|
|
279
|
+
rev_sample = self._state.m_all()
|
|
280
|
+
sample = 0
|
|
281
|
+
for i in range(self.num_wires):
|
|
282
|
+
if (rev_sample & (1 << i)) > 0:
|
|
283
|
+
sample |= 1 << (self.num_wires - (i + 1))
|
|
284
|
+
return sample
|
|
285
|
+
|
|
286
|
+
def generate_samples(self):
|
|
287
|
+
if self.shots is None:
|
|
288
|
+
raise QuantumFunctionError(
|
|
289
|
+
"The number of shots has to be explicitly set on the device "
|
|
290
|
+
"when using sample-based measurements."
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return self._samples
|
|
294
|
+
|
|
295
|
+
if self.shots == 1:
|
|
296
|
+
self._samples = QubitDevice.states_to_binary(
|
|
297
|
+
np.array([self._generate_sample()]), self.num_wires
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return self._samples
|
|
301
|
+
|
|
302
|
+
# QubitDevice.states_to_binary() doesn't work for >64qb. (Fix by Elara, the custom OpenAI GPT)
|
|
303
|
+
samples = self._state.measure_shots(list(range(self.num_wires - 1, -1, -1)), self.shots)
|
|
304
|
+
self._samples = np.array(
|
|
305
|
+
[list(format(b, f"0{self.num_wires}b")) for b in samples], dtype=np.int8
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return self._samples
|
|
309
|
+
|
|
310
|
+
def reset(self):
|
|
311
|
+
for i in range(self.num_wires):
|
|
312
|
+
if self._state.m(i):
|
|
313
|
+
self._state.x(i)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pennylane-qrack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.0
|
|
4
4
|
Summary: PennyLane plugin for Qrack.
|
|
5
5
|
Home-page: http://github.com/vm6502q
|
|
6
6
|
Maintainer: vm6502q
|
|
@@ -22,11 +22,11 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
24
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
25
|
-
Classifier: Topic :: Scientific/Engineering ::
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Quantum Computing
|
|
26
26
|
Provides: pennylane_qrack
|
|
27
27
|
Description-Content-Type: text/x-rst
|
|
28
28
|
License-File: LICENSE
|
|
29
|
-
Requires-Dist: pennylane>=0.
|
|
29
|
+
Requires-Dist: pennylane>=0.39.0
|
|
30
30
|
Requires-Dist: pyqrack>=1.30.0
|
|
31
31
|
Requires-Dist: numpy>=1.16
|
|
32
32
|
|
|
@@ -37,6 +37,8 @@ PennyLane-Qrack Plugin
|
|
|
37
37
|
|
|
38
38
|
The PennyLane-Qrack plugin integrates the Qrack quantum computing framework with PennyLane's quantum machine learning capabilities.
|
|
39
39
|
|
|
40
|
+
Performance can benefit greatly from following the `Qrack repository "Quick Start" and "Power user considerations." <https://github.com/unitaryfund/qrack/blob/main/README.md#quick-start>`__
|
|
41
|
+
|
|
40
42
|
This plugin is addapted from the `PennyLane-Qulacs plugin, <https://github.com/PennyLaneAI/pennylane-qulacs>`__ under the Apache License 2.0, with many thanks to the original developers!
|
|
41
43
|
|
|
42
44
|
`PennyLane <https://pennylane.readthedocs.io>`__ is a cross-platform Python library for quantum machine learning, automatic differentiation, and optimization of hybrid quantum-classical computations.
|
|
@@ -52,6 +54,8 @@ Features
|
|
|
52
54
|
|
|
53
55
|
* Provides access to a PyQrack simulator backend via the ``qrack.simulator`` device
|
|
54
56
|
* Provides access to a (C++) Qrack simulator backend for Catalyst (also) via the ``qrack.simulator`` device
|
|
57
|
+
* Provides access to a PyQrack Clifford-only simulator backend via the ``qrack.stabilizer`` device
|
|
58
|
+
* Provides access to a PyQrack simulator backend optimized for large-scale approximate simulation via the ``qrack.ace`` device
|
|
55
59
|
|
|
56
60
|
.. installation-start-inclusion-marker-do-not-remove
|
|
57
61
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
pennylane_qrack/QrackAceDeviceConfig.toml,sha256=KmPPJEa6TMYpbQqQgMSAPfeGPQE8f1lriteyfGW8WeA,4517
|
|
2
|
+
pennylane_qrack/QrackDeviceConfig.toml,sha256=oR9-dIAP6BzP3e2QSLroacaloHZsx7iJiKE6w7LGxUo,5459
|
|
3
|
+
pennylane_qrack/QrackStabilizerDeviceConfig.toml,sha256=zm2M2FBR--RnKkNCtZih7xxtr-Vl6MtI4g1L3SrjFWw,3965
|
|
4
|
+
pennylane_qrack/__init__.py,sha256=_g4NKu07_pXqxvQaxjdAPe769S5tWwYjqyHi3z7YKHc,673
|
|
5
|
+
pennylane_qrack/_version.py,sha256=hecat1DpqSACwVCvwks_EFneoou9obMUkZXXmG-CYck,689
|
|
6
|
+
pennylane_qrack/qrack_ace_device.py,sha256=ILJFd-NxhtrhYBmNVb2St0ffJUd7N2y7m_M7hMuzP9c,17090
|
|
7
|
+
pennylane_qrack/qrack_device.cpp,sha256=RAr5127mBX7btpiOdwbNvNpL_X7k0_vl0_OZdAZ2tZ0,37421
|
|
8
|
+
pennylane_qrack/qrack_device.py,sha256=TzZXizrd4g2MC0CizrJu5PQSfBq33np0DkkUuA2YdRc,28614
|
|
9
|
+
pennylane_qrack/qrack_stabilizer_device.py,sha256=1LuIczUAvq-vI0lZiWohn771-n_owtjM7wVc-MQv1NQ,10665
|
|
10
|
+
pennylane_qrack-0.25.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
+
pennylane_qrack-0.25.0.dist-info/METADATA,sha256=xx3PN5UGiCMtEu0464BmofGe2g5Wmv6fHZ0S7FaSQOQ,5954
|
|
12
|
+
pennylane_qrack-0.25.0.dist-info/WHEEL,sha256=BM14wstXunLvpW3hePaerLq1eZfN4NnYgS3CIXR7_AM,106
|
|
13
|
+
pennylane_qrack-0.25.0.dist-info/entry_points.txt,sha256=5eoa4LFV7DuSLFbs6tzFvuVIHr6zOovxqlDsn2cinP4,220
|
|
14
|
+
pennylane_qrack-0.25.0.dist-info/top_level.txt,sha256=5NFMNHqCHtVLwNkLg66xz846uUJAlnOJ5VGa6ulW1ow,16
|
|
15
|
+
pennylane_qrack-0.25.0.dist-info/RECORD,,
|