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.

@@ -6,7 +6,7 @@
6
6
  #define CL_HPP_TARGET_OPENCL_VERSION 300
7
7
  #include "qrack/qfactory.hpp"
8
8
 
9
- #define QSIM_CONFIG(numQubits) Qrack::CreateArrangedLayersFull(nw, md, sd, sh, bdt, true, tn, true, oc, numQubits, Qrack::ZERO_BCI, nullptr, Qrack::CMPLX_DEFAULT_ARG, false, true, hp)
9
+ #define QSIM_CONFIG(numQubits) Qrack::CreateArrangedLayersFull(nw, md, sd, sh, bdt, pg, tn, hy, oc, numQubits, Qrack::ZERO_BCI, nullptr, Qrack::CMPLX_DEFAULT_ARG, false, true, hp, sp)
10
10
 
11
11
  std::string trim(std::string s)
12
12
  {
@@ -38,9 +38,13 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
38
38
  bool md;
39
39
  bool bdt;
40
40
  bool oc;
41
+ bool pg;
42
+ bool hy;
41
43
  bool hp;
44
+ bool sp;
42
45
  bool nw;
43
46
  size_t shots;
47
+ Qrack::real1_f noise_param;
44
48
  Qrack::QInterfacePtr qsim;
45
49
  std::map<QubitIdType, bitLenInt> qubit_map;
46
50
  std::vector<QrackObservable> obs_cache;
@@ -184,8 +188,8 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
184
188
  const Qrack::real1 omega = inverse ? -params[0U] : params[2U];
185
189
  const Qrack::real1 cos0 = (Qrack::real1)cos(theta / 2);
186
190
  const Qrack::real1 sin0 = (Qrack::real1)sin(theta / 2);
187
- const Qrack::complex expP = exp(Qrack::I_CMPLX * (phi + omega) / (2 * ONE_R1));
188
- const Qrack::complex expM = exp(Qrack::I_CMPLX * (phi - omega) / (2 * ONE_R1));
191
+ const Qrack::complex expP = exp(Qrack::I_CMPLX * (phi + omega) * HALF_R1);
192
+ const Qrack::complex expM = exp(Qrack::I_CMPLX * (phi - omega) * HALF_R1);
189
193
  const Qrack::complex mtrx[4U]{
190
194
  cos0 / expP, -sin0 * expM,
191
195
  sin0 / expM, cos0 * expP
@@ -226,14 +230,12 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
226
230
  QRACK_CONST Qrack::complex NEG_SQRTI_2_CMPLX(ZERO_R1, -Qrack::SQRT1_2_R1);
227
231
  const Qrack::complex QBRTI_2_CMPLX(ZERO_R1, sqrt(Qrack::SQRT1_2_R1));
228
232
  const Qrack::complex NEG_QBRTI_2_CMPLX(ZERO_R1, sqrt(-Qrack::SQRT1_2_R1));
229
- QRACK_CONST Qrack::complex ONE_PLUS_I_DIV_2 = Qrack::complex((Qrack::real1)(ONE_R1 / 2), (Qrack::real1)(ONE_R1 / 2));
230
- QRACK_CONST Qrack::complex ONE_MINUS_I_DIV_2 = Qrack::complex((Qrack::real1)(ONE_R1 / 2), (Qrack::real1)(-ONE_R1 / 2));
231
233
 
232
234
  QRACK_CONST Qrack::complex pauliX[4U] = { Qrack::ZERO_CMPLX, Qrack::ONE_CMPLX, Qrack::ONE_CMPLX, Qrack::ZERO_CMPLX };
233
235
  QRACK_CONST Qrack::complex pauliY[4U] = { Qrack::ZERO_CMPLX, NEG_I_CMPLX, Qrack::I_CMPLX, Qrack::ZERO_CMPLX };
234
236
  QRACK_CONST Qrack::complex pauliZ[4U] = { Qrack::ONE_CMPLX, Qrack::ZERO_CMPLX, Qrack::ZERO_CMPLX, NEG_1_CMPLX };
235
- QRACK_CONST Qrack::complex sqrtX[4U]{ ONE_PLUS_I_DIV_2, ONE_MINUS_I_DIV_2, ONE_MINUS_I_DIV_2, ONE_PLUS_I_DIV_2 };
236
- QRACK_CONST Qrack::complex iSqrtX[4U]{ ONE_MINUS_I_DIV_2, ONE_PLUS_I_DIV_2, ONE_PLUS_I_DIV_2, ONE_MINUS_I_DIV_2 };
237
+ QRACK_CONST Qrack::complex sqrtX[4U]{ Qrack::HALF_I_HALF_CMPLX, Qrack::HALF_NEG_I_HALF_CMPLX, Qrack::HALF_NEG_I_HALF_CMPLX, Qrack::HALF_I_HALF_CMPLX };
238
+ QRACK_CONST Qrack::complex iSqrtX[4U]{ Qrack::HALF_NEG_I_HALF_CMPLX, Qrack::HALF_I_HALF_CMPLX, Qrack::HALF_I_HALF_CMPLX, Qrack::HALF_NEG_I_HALF_CMPLX };
237
239
  QRACK_CONST Qrack::complex hadamard[4U]{ SQRT1_2_CMPLX, SQRT1_2_CMPLX, SQRT1_2_CMPLX, NEG_SQRT1_2_CMPLX };
238
240
 
239
241
  if ((name == "PauliX") || (name == "CNOT") || (name == "Toffoli") || (name == "MultiControlledX")) {
@@ -348,8 +350,8 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
348
350
  const Qrack::real1 omega = inverse ? -params[0U] : params[2U];
349
351
  const Qrack::real1 cos0 = (Qrack::real1)cos(theta / 2);
350
352
  const Qrack::real1 sin0 = (Qrack::real1)sin(theta / 2);
351
- const Qrack::complex expP = exp(Qrack::I_CMPLX * (phi + omega) / (2 * ONE_R1));
352
- const Qrack::complex expM = exp(Qrack::I_CMPLX * (phi - omega) / (2 * ONE_R1));
353
+ const Qrack::complex expP = exp(Qrack::I_CMPLX * (phi + omega) * HALF_R1);
354
+ const Qrack::complex expM = exp(Qrack::I_CMPLX * (phi - omega) * HALF_R1);
353
355
  const Qrack::complex mtrx[4U]{
354
356
  cos0 / expP, -sin0 * expM,
355
357
  sin0 / expM, cos0 * expP
@@ -384,12 +386,16 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
384
386
  , sh(true)
385
387
  , tn(true)
386
388
  , sd(true)
387
- , md(true)
389
+ , md(false)
388
390
  , bdt(false)
389
391
  , oc(true)
392
+ , pg(true)
393
+ , hy(true)
390
394
  , hp(false)
395
+ , sp(false)
391
396
  , nw(false)
392
397
  , shots(1U)
398
+ , noise_param(ZERO_R1_F)
393
399
  , qsim(nullptr)
394
400
  {
395
401
  // Cut leading '{' and trailing '}'
@@ -405,11 +411,13 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
405
411
  keyMap["'is_schmidt_decomposition_parallel'"] = 4;
406
412
  keyMap["'is_qbdd'"] = 5;
407
413
  keyMap["'is_gpu'"] = 6;
408
- keyMap["'is_host_pointer'"] =7;
409
- keyMap["'noise'"] = 8;
414
+ keyMap["'is_paged'"] = 7;
415
+ keyMap["'is_hybrid_cpu_gpu'"] = 8;
416
+ keyMap["'is_host_pointer'"] =9;
417
+ keyMap["'is_sparse'"] =10;
418
+ keyMap["'noise'"] = 11;
410
419
 
411
420
  size_t pos;
412
- Qrack::real1_f noiseParam = 0;
413
421
  while ((pos = kwargs.find(":")) != std::string::npos) {
414
422
  std::string key = trim(kwargs.substr(0, pos));
415
423
  kwargs.erase(0, pos + 1U);
@@ -437,11 +445,20 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
437
445
  oc = val;
438
446
  break;
439
447
  case 7:
440
- hp = val;
448
+ pg = val;
441
449
  break;
442
450
  case 8:
443
- noiseParam = std::stof(value);
444
- nw = noiseParam > ZERO_R1;
451
+ hy = val;
452
+ break;
453
+ case 9:
454
+ hp = val;
455
+ break;
456
+ case 10:
457
+ sp = val;
458
+ break;
459
+ case 11:
460
+ noise_param = std::stof(value);
461
+ nw = noise_param > ZERO_R1;
445
462
  break;
446
463
  default:
447
464
  break;
@@ -449,8 +466,8 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
449
466
  }
450
467
 
451
468
  qsim = QSIM_CONFIG(0U);
452
- if (noiseParam > ZERO_R1) {
453
- qsim->SetNoiseParameter(noiseParam);
469
+ if (noise_param > ZERO_R1) {
470
+ qsim->SetNoiseParameter(noise_param);
454
471
  }
455
472
  }
456
473
 
@@ -472,8 +489,6 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
472
489
  }
473
490
  return ids;
474
491
  }
475
- [[nodiscard]] auto Zero() const -> Result override { return const_cast<Result>(&QRACK_RESULT_FALSE_CONST); }
476
- [[nodiscard]] auto One() const -> Result override { return const_cast<Result>(&QRACK_RESULT_TRUE_CONST); }
477
492
  auto Observable(ObsId id, const std::vector<std::complex<double>> &matrix,
478
493
  const std::vector<QubitIdType> &wires) -> ObsIdType override
479
494
  {
@@ -544,34 +559,30 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
544
559
  // Deallocate
545
560
  qsim->Dispose(id, 1U);
546
561
  }
547
- void ReleaseAllQubits() override
562
+ void ReleaseQubits(const std::vector<QubitIdType> &qubits) override
548
563
  {
549
- // State vector is left empty
550
- qsim = QSIM_CONFIG(0U);
564
+ if (qubits.size() == qsim->GetQubitCount()) {
565
+ qsim = QSIM_CONFIG(0U);
566
+ return;
567
+ }
568
+
569
+ for (const QubitIdType& q : qubits) {
570
+ ReleaseQubit(q);
571
+ }
551
572
  }
552
573
  [[nodiscard]] auto GetNumQubits() const -> size_t override
553
574
  {
554
575
  return qsim->GetQubitCount();
555
576
  }
556
- void SetDeviceShots(size_t s) override { shots = s; }
577
+ void SetDeviceShots(size_t s) override {
578
+ if ((s > 1U) && (noise_param > ZERO_R1_F)) {
579
+ throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
580
+ }
581
+ shots = s;
582
+ }
557
583
  [[nodiscard]] auto GetDeviceShots() const -> size_t override { return shots; }
558
584
  void StartTapeRecording() override { tapeRecording = true; }
559
585
  void StopTapeRecording() override { tapeRecording = false; }
560
- void PrintState() override
561
- {
562
- const size_t numQubits = qsim->GetQubitCount();
563
- const size_t maxQPower = (size_t)((uint64_t)qsim->GetMaxQPower());
564
- const size_t maxLcv = maxQPower - 1U;
565
- size_t idx = 0U;
566
- std::cout << "*** State-Vector of Size " << maxQPower << " ***" << std::endl;
567
- std::cout << "[";
568
- std::unique_ptr<Qrack::complex> sv(new Qrack::complex[maxQPower]);
569
- qsim->GetQuantumState(sv.get());
570
- for (; idx < maxLcv; ++idx) {
571
- std::cout << sv.get()[idx] << ", ";
572
- }
573
- std::cout << sv.get()[idx] << "]" << std::endl;
574
- }
575
586
  void NamedOperation(const std::string &name, const std::vector<double> &params,
576
587
  const std::vector<QubitIdType> &wires, bool inverse,
577
588
  const std::vector<QubitIdType> &controlled_wires,
@@ -716,10 +727,14 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
716
727
  }
717
728
  }
718
729
  }
719
- void Sample(DataView<double, 2> &samples, size_t shots) override
730
+ void Sample(DataView<double, 2> &samples) override
720
731
  {
721
732
  RT_FAIL_IF(samples.size() != shots * qsim->GetQubitCount(), "Invalid size for the pre-allocated samples");
722
733
 
734
+ if ((shots > 1U) && (noise_param > ZERO_R1_F)) {
735
+ throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
736
+ }
737
+
723
738
  if (shots == 1U) {
724
739
  const bitCapInt rev_sample = qsim->MAll();
725
740
  const bitLenInt numQubits = qsim->GetQubitCount();
@@ -741,10 +756,14 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
741
756
  const std::map<bitCapInt, int> q_samples = qsim->MultiShotMeasureMask(qPowers, shots);
742
757
  _SampleBody(qPowers.size(), q_samples, samples);
743
758
  }
744
- void PartialSample(DataView<double, 2> &samples, const std::vector<QubitIdType> &wires, size_t shots) override
759
+ void PartialSample(DataView<double, 2> &samples, const std::vector<QubitIdType> &wires) override
745
760
  {
746
761
  RT_FAIL_IF(samples.size() != shots * wires.size(), "Invalid size for the pre-allocated samples");
747
762
 
763
+ if ((shots > 1U) && (noise_param > ZERO_R1_F)) {
764
+ throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
765
+ }
766
+
748
767
  auto &&dev_wires = getDeviceWires(wires);
749
768
 
750
769
  if (shots == 1U) {
@@ -782,8 +801,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
782
801
  }
783
802
  }
784
803
  }
785
- void Counts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts,
786
- size_t shots) override
804
+ void Counts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts) override
787
805
  {
788
806
  const size_t numQubits = qsim->GetQubitCount();
789
807
  const size_t numElements = 1U << numQubits;
@@ -817,7 +835,7 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
817
835
  }
818
836
 
819
837
  void PartialCounts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts,
820
- const std::vector<QubitIdType> &wires, size_t shots) override
838
+ const std::vector<QubitIdType> &wires) override
821
839
  {
822
840
  const size_t numQubits = wires.size();
823
841
  const size_t numElements = 1U << numQubits;
@@ -825,6 +843,10 @@ struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
825
843
  RT_FAIL_IF(eigvals.size() != numElements || counts.size() != numElements,
826
844
  "Invalid size for the pre-allocated counts");
827
845
 
846
+ if ((shots > 1U) && (noise_param > ZERO_R1_F)) {
847
+ throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
848
+ }
849
+
828
850
  auto &&dev_wires = getDeviceWires(wires);
829
851
 
830
852
  std::map<bitCapInt, int> q_samples;
@@ -14,7 +14,6 @@
14
14
  """
15
15
  Base device class for PennyLane-Qrack.
16
16
  """
17
- from collections import OrderedDict
18
17
  from functools import reduce
19
18
  import cmath, math
20
19
  import os
@@ -24,14 +23,20 @@ import itertools as it
24
23
 
25
24
  import numpy as np
26
25
 
27
- from pennylane import DeviceError, QuantumFunctionError
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
+
28
35
  from pennylane.devices import QubitDevice
29
36
  from pennylane.ops import (
30
- QubitStateVector,
37
+ StatePrep,
31
38
  BasisState,
32
39
  QubitUnitary,
33
- CRZ,
34
- PhaseShift,
35
40
  Adjoint,
36
41
  )
37
42
  from pennylane.wires import Wires
@@ -66,9 +71,9 @@ class QrackDevice(QubitDevice):
66
71
  "PauliY": Pauli.PauliY,
67
72
  "PauliZ": Pauli.PauliZ,
68
73
  "Identity": Pauli.PauliI,
74
+ "Hadamard": None,
75
+ "Hermitian": None,
69
76
  "Prod": None,
70
- # "Hadamard": None,
71
- # "Hermitian": None,
72
77
  # "Sum": None,
73
78
  # "SProd": None,
74
79
  # "Exp": None,
@@ -141,7 +146,7 @@ class QrackDevice(QubitDevice):
141
146
  "C(MultiControlledX)",
142
147
  }
143
148
 
144
- config = pathlib.Path(
149
+ config_filepath = pathlib.Path(
145
150
  os.path.dirname(sys.modules[__name__].__file__) + "/QrackDeviceConfig.toml"
146
151
  )
147
152
 
@@ -151,14 +156,20 @@ class QrackDevice(QubitDevice):
151
156
  isTensorNetwork = True
152
157
  # Use Schmidt decomposition optimizations? (Default is "true")
153
158
  isSchmidtDecompose = True
154
- # Distribute Schmidt-decomposed qubit subsystems to multiple GPUs or accelerators, if available? (Default is "true"; mismatched device capacities might hurt overall performance)
155
- isSchmidtDecomposeMulti = True
159
+ # Distribute Schmidt-decomposed qubit subsystems to multiple GPUs or accelerators, if available? (Default is "False"; mismatched device capacities might hurt overall performance)
160
+ isSchmidtDecomposeMulti = False
156
161
  # Use "quantum binary decision diagram" ("QBDD") methods? (Default is "false"; note that QBDD is CPU-only)
157
162
  isBinaryDecisionTree = False
158
163
  # Use GPU acceleration? (Default is "true")
159
164
  isOpenCL = True
165
+ # Use multi-GPU (or "multi-page") acceleration? (Default is "true")
166
+ isPaged = True
167
+ # Use CPU/GPU method hybridization? (Default is "True")
168
+ isCpuGpuHybrid = True
160
169
  # Allocate GPU buffer from general host heap? (Default is "false"; "true" might improve performance or reliability in certain cases, like if using an Intel HD as accelerator)
161
- isHostPointer = False
170
+ isHostPointer = True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False
171
+ # For CPU-based simulation, use sparse state vectors (Default is "false")
172
+ isSparse = False
162
173
  # Noise parameter. (Default is "0"; depolarizing noise intensity can also be controlled by "QRACK_GATE_DEPOLARIZATION" environment variable)
163
174
  noise = 0
164
175
 
@@ -186,8 +197,14 @@ class QrackDevice(QubitDevice):
186
197
  self.isBinaryDecisionTree = options["isBinaryDecisionTree"]
187
198
  if "isOpenCL" in options:
188
199
  self.isOpenCL = options["isOpenCL"]
200
+ if "isPaged" in options:
201
+ self.isPaged = options["isPaged"]
202
+ if "isCpuGpuHybrid" in options:
203
+ self.isCpuGpuHybrid = options["isCpuGpuHybrid"]
189
204
  if "isHostPointer" in options:
190
205
  self.isHostPointer = options["isHostPointer"]
206
+ if "isSparse" in options:
207
+ self.isSparse = options["isSparse"]
191
208
  if "noise" in options:
192
209
  self.noise = options["noise"]
193
210
  if (self.noise != 0) and (shots is None):
@@ -201,9 +218,24 @@ class QrackDevice(QubitDevice):
201
218
  isSchmidtDecompose=self.isSchmidtDecompose,
202
219
  isBinaryDecisionTree=self.isBinaryDecisionTree,
203
220
  isOpenCL=self.isOpenCL,
221
+ isCpuGpuHybrid=self.isCpuGpuHybrid,
204
222
  isHostPointer=self.isHostPointer,
223
+ isSparse=self.isSparse,
205
224
  noise=self.noise,
206
225
  )
226
+ self.device_kwargs = {
227
+ "is_hybrid_stabilizer": self.isStabilizerHybrid,
228
+ "is_tensor_network": self.isTensorNetwork,
229
+ "is_schmidt_decompose": self.isSchmidtDecompose,
230
+ "is_schmidt_decompose_parallel": self.isSchmidtDecomposeMulti,
231
+ "is_qpdd": self.isBinaryDecisionTree,
232
+ "is_gpu": self.isOpenCL,
233
+ "is_paged": self.isPaged,
234
+ "is_hybrid_cpu_gpu": self.isCpuGpuHybrid,
235
+ "is_host_pointer": self.isHostPointer,
236
+ "is_sparse": self.isSparse,
237
+ "noise": self.noise,
238
+ }
207
239
  self._circuit = []
208
240
 
209
241
  def _reverse_state(self):
@@ -229,8 +261,8 @@ class QrackDevice(QubitDevice):
229
261
 
230
262
  def _apply(self):
231
263
  for op in self._circuit:
232
- if isinstance(op, QubitStateVector):
233
- self._apply_qubit_state_vector(op)
264
+ if isinstance(op, StatePrep):
265
+ self._apply_state_prep(op)
234
266
  elif isinstance(op, BasisState):
235
267
  self._apply_basis_state(op)
236
268
  elif isinstance(op, QubitUnitary):
@@ -259,7 +291,7 @@ class QrackDevice(QubitDevice):
259
291
 
260
292
  return state_vector.flatten()
261
293
 
262
- def _apply_qubit_state_vector(self, op):
294
+ def _apply_state_prep(self, op):
263
295
  """Initialize state with a state vector"""
264
296
  wires = op.wires
265
297
  input_state = op.parameters[0]
@@ -692,10 +724,11 @@ class QrackDevice(QubitDevice):
692
724
 
693
725
  return self._samples
694
726
 
695
- samples = np.array(
696
- self._state.measure_shots(list(range(self.num_wires - 1, -1, -1)), self.shots)
727
+ # QubitDevice.states_to_binary() doesn't work for >64qb. (Fix by Elara, the custom OpenAI GPT)
728
+ samples = self._state.measure_shots(list(range(self.num_wires - 1, -1, -1)), self.shots)
729
+ self._samples = np.array(
730
+ [list(format(b, f"0{self.num_wires}b")) for b in samples], dtype=np.int8
697
731
  )
698
- self._samples = QubitDevice.states_to_binary(samples, self.num_wires)
699
732
 
700
733
  return self._samples
701
734