pyqrack 1.29.0__py3-none-win_amd64.whl → 1.72.5__py3-none-win_amd64.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.
@@ -1,4 +1,4 @@
1
- # (C) Daniel Strano and the Qrack contributors 2017-2023. All rights reserved.
1
+ # (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style license that can be
4
4
  # found in the LICENSE file or at https://opensource.org/licenses/MIT.
@@ -6,21 +6,24 @@
6
6
  import copy
7
7
  import ctypes
8
8
  import math
9
+ import os
9
10
  import re
10
11
  from .qrack_system import Qrack
11
12
  from .pauli import Pauli
12
13
 
13
14
  _IS_QISKIT_AVAILABLE = True
14
15
  try:
15
- from qiskit.circuit import QuantumRegister, Qubit
16
16
  from qiskit.circuit.quantumcircuit import QuantumCircuit
17
17
  from qiskit.compiler import transpile
18
- from qiskit.qobj.qasm_qobj import QasmQobjExperiment
19
18
  from qiskit.quantum_info.operators.symplectic.clifford import Clifford
20
- from .util import convert_qiskit_circuit_to_qasm_experiment
21
19
  except ImportError:
22
20
  _IS_QISKIT_AVAILABLE = False
23
21
 
22
+ try:
23
+ from qiskit import qasm3
24
+ except ImportError:
25
+ pass
26
+
24
27
  _IS_NUMPY_AVAILABLE = True
25
28
  try:
26
29
  import numpy as np
@@ -29,10 +32,9 @@ except:
29
32
 
30
33
 
31
34
  class QrackSimulator:
32
- """Interface for all the QRack functionality.
35
+ """Interface for all the Qrack functionality.
33
36
 
34
37
  Attributes:
35
- qubitCount(int): Number of qubits that are to be simulated.
36
38
  sid(int): Corresponding simulator id.
37
39
  """
38
40
 
@@ -48,14 +50,18 @@ class QrackSimulator:
48
50
  qubitCount=-1,
49
51
  cloneSid=-1,
50
52
  isTensorNetwork=True,
51
- isSchmidtDecomposeMulti=True,
53
+ isSchmidtDecomposeMulti=False,
52
54
  isSchmidtDecompose=True,
53
- isStabilizerHybrid=True,
55
+ isStabilizerHybrid=False,
54
56
  isBinaryDecisionTree=False,
55
57
  isPaged=True,
56
58
  isCpuGpuHybrid=True,
57
59
  isOpenCL=True,
58
- isHostPointer=False,
60
+ isHostPointer=(
61
+ True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False
62
+ ),
63
+ isSparse=False,
64
+ noise=0,
59
65
  pyzxCircuit=None,
60
66
  qiskitCircuit=None,
61
67
  ):
@@ -74,6 +80,7 @@ class QrackSimulator:
74
80
  )
75
81
 
76
82
  self.is_tensor_network = isTensorNetwork
83
+ self.is_pure_stabilizer = False
77
84
 
78
85
  if cloneSid > -1:
79
86
  self.sid = Qrack.qrack_lib.init_clone(cloneSid)
@@ -89,14 +96,18 @@ class QrackSimulator:
89
96
  isStabilizerHybrid,
90
97
  isBinaryDecisionTree,
91
98
  isPaged,
92
- False,
99
+ (noise > 0),
93
100
  isCpuGpuHybrid,
94
101
  isOpenCL,
95
102
  isHostPointer,
103
+ isSparse
96
104
  )
97
105
 
98
106
  self._throw_if_error()
99
107
 
108
+ if noise > 0:
109
+ self.set_noise_parameter(noise)
110
+
100
111
  if pyzxCircuit is not None:
101
112
  self.run_pyzx_gates(pyzxCircuit.gates)
102
113
  elif qiskitCircuit is not None:
@@ -113,6 +124,9 @@ class QrackSimulator:
113
124
  def _ulonglong_byref(self, a):
114
125
  return (ctypes.c_ulonglong * len(a))(*a)
115
126
 
127
+ def _longlong_byref(self, a):
128
+ return (ctypes.c_longlong * len(a))(*a)
129
+
116
130
  def _double_byref(self, a):
117
131
  return (ctypes.c_double * len(a))(*a)
118
132
 
@@ -172,9 +186,23 @@ class QrackSimulator:
172
186
  self._throw_if_error()
173
187
 
174
188
  def set_concurrency(self, p):
189
+ """Set the CPU parallel thread count"""
175
190
  Qrack.qrack_lib.set_concurrency(self.sid, p)
176
191
  self._throw_if_error()
177
192
 
193
+ def set_device(self, d):
194
+ """Set the GPU device ID"""
195
+ Qrack.qrack_lib.set_device(self.sid, d)
196
+ self._throw_if_error()
197
+
198
+ def set_device_list(self, d):
199
+ """Set the GPU device ID"""
200
+ Qrack.qrack_lib.set_device_list(self.sid, len(d), self._longlong_byref(d))
201
+ self._throw_if_error()
202
+
203
+ def clone(self):
204
+ return QrackSimulator(cloneSid=self.sid)
205
+
178
206
  # standard gates
179
207
 
180
208
  ## single-qubits gates
@@ -330,7 +358,9 @@ class QrackSimulator:
330
358
  RuntimeError: QrackSimulator raised an exception.
331
359
  """
332
360
  if len(m) < 4:
333
- raise ValueError("2x2 matrix 'm' in QrackSimulator.mtrx() must contain at least 4 elements.")
361
+ raise ValueError(
362
+ "2x2 matrix 'm' in QrackSimulator.mtrx() must contain at least 4 elements."
363
+ )
334
364
  Qrack.qrack_lib.Mtrx(self.sid, self._complex_byref(m), q)
335
365
  self._throw_if_error()
336
366
 
@@ -548,7 +578,9 @@ class QrackSimulator:
548
578
  RuntimeError: QrackSimulator raised an exception.
549
579
  """
550
580
  if len(m) < 4:
551
- raise ValueError("2x2 matrix 'm' in QrackSimulator.mcmtrx() must contain at least 4 elements.")
581
+ raise ValueError(
582
+ "2x2 matrix 'm' in QrackSimulator.mcmtrx() must contain at least 4 elements."
583
+ )
552
584
  Qrack.qrack_lib.MCMtrx(
553
585
  self.sid, len(c), self._ulonglong_byref(c), self._complex_byref(m), q
554
586
  )
@@ -724,7 +756,9 @@ class QrackSimulator:
724
756
  RuntimeError: QrackSimulator raised an exception.
725
757
  """
726
758
  if len(m) < 4:
727
- raise ValueError("2x2 matrix 'm' in QrackSimulator.macmtrx() must contain at least 4 elements.")
759
+ raise ValueError(
760
+ "2x2 matrix 'm' in QrackSimulator.macmtrx() must contain at least 4 elements."
761
+ )
728
762
  Qrack.qrack_lib.MACMtrx(
729
763
  self.sid, len(c), self._ulonglong_byref(c), self._complex_byref(m), q
730
764
  )
@@ -747,7 +781,9 @@ class QrackSimulator:
747
781
  RuntimeError: QrackSimulator raised an exception.
748
782
  """
749
783
  if len(m) < 4:
750
- raise ValueError("2x2 matrix 'm' in QrackSimulator.ucmtrx() must contain at least 4 elements.")
784
+ raise ValueError(
785
+ "2x2 matrix 'm' in QrackSimulator.ucmtrx() must contain at least 4 elements."
786
+ )
751
787
  Qrack.qrack_lib.UCMtrx(
752
788
  self.sid, len(c), self._ulonglong_byref(c), self._complex_byref(m), q, p
753
789
  )
@@ -769,7 +805,9 @@ class QrackSimulator:
769
805
  RuntimeError: QrackSimulator raised an exception.
770
806
  """
771
807
  if len(m) < ((1 << len(c)) * 4):
772
- raise ValueError("Multiplex matrix 'm' in QrackSimulator.multiplex1_mtrx() must contain at least (4 * 2 ** len(c)) elements.")
808
+ raise ValueError(
809
+ "Multiplex matrix 'm' in QrackSimulator.multiplex1_mtrx() must contain at least (4 * 2 ** len(c)) elements."
810
+ )
773
811
  Qrack.qrack_lib.Multiplex1Mtrx(
774
812
  self.sid, len(c), self._ulonglong_byref(c), q, self._complex_byref(m)
775
813
  )
@@ -1017,9 +1055,16 @@ class QrackSimulator:
1017
1055
  Returns:
1018
1056
  Measurement result of all qubits.
1019
1057
  """
1020
- result = Qrack.qrack_lib.MAll(self.sid)
1058
+ num_q = self.num_qubits()
1059
+ num_words = (num_q + 63) // 64
1060
+ _r = (ctypes.c_ulonglong * num_words)()
1061
+ Qrack.qrack_lib.MAllLong(self.sid, _r)
1021
1062
  self._throw_if_error()
1022
- return result
1063
+ r = 0
1064
+ for w in range(num_words):
1065
+ r <<= 64
1066
+ r |= _r[w]
1067
+ return r
1023
1068
 
1024
1069
  def measure_pauli(self, b, q):
1025
1070
  """Pauli Measurement gate
@@ -1239,7 +1284,13 @@ class QrackSimulator:
1239
1284
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot mul()! (Turn off just this option, in the constructor.)
1240
1285
  """
1241
1286
  if self.is_tensor_network:
1242
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot mul()! (Turn off just this option, in the constructor.)")
1287
+ raise RuntimeError(
1288
+ "QrackSimulator with isTensorNetwork=True option cannot mul()! (Turn off just this option, in the constructor.)"
1289
+ )
1290
+ if self.is_pure_stabilizer:
1291
+ raise RuntimeError(
1292
+ "QrackStabilizer cannot mul()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1293
+ )
1243
1294
 
1244
1295
  if len(q) != len(o):
1245
1296
  raise RuntimeError("Lengths of list parameters are mismatched.")
@@ -1272,7 +1323,13 @@ class QrackSimulator:
1272
1323
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot div()! (Turn off just this option, in the constructor.)
1273
1324
  """
1274
1325
  if self.is_tensor_network:
1275
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot div()! (Turn off just this option, in the constructor.)")
1326
+ raise RuntimeError(
1327
+ "QrackSimulator with isTensorNetwork=True option cannot div()! (Turn off just this option, in the constructor.)"
1328
+ )
1329
+ if self.is_pure_stabilizer:
1330
+ raise RuntimeError(
1331
+ "QrackStabilizer cannot div()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1332
+ )
1276
1333
 
1277
1334
  if len(q) != len(o):
1278
1335
  raise RuntimeError("Lengths of list parameters are mismatched.")
@@ -1363,7 +1420,13 @@ class QrackSimulator:
1363
1420
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot pown()! (Turn off just this option, in the constructor.)
1364
1421
  """
1365
1422
  if self.is_tensor_network:
1366
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot pown()! (Turn off just this option, in the constructor.)")
1423
+ raise RuntimeError(
1424
+ "QrackSimulator with isTensorNetwork=True option cannot pown()! (Turn off just this option, in the constructor.)"
1425
+ )
1426
+ if self.is_pure_stabilizer:
1427
+ raise RuntimeError(
1428
+ "QrackStabilizer cannot pown()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1429
+ )
1367
1430
 
1368
1431
  if len(q) != len(o):
1369
1432
  raise RuntimeError("Lengths of list parameters are mismatched.")
@@ -1450,7 +1513,13 @@ class QrackSimulator:
1450
1513
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot mcmul()! (Turn off just this option, in the constructor.)
1451
1514
  """
1452
1515
  if self.is_tensor_network:
1453
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot mcmul()! (Turn off just this option, in the constructor.)")
1516
+ raise RuntimeError(
1517
+ "QrackSimulator with isTensorNetwork=True option cannot mcmul()! (Turn off just this option, in the constructor.)"
1518
+ )
1519
+ if self.is_pure_stabilizer:
1520
+ raise RuntimeError(
1521
+ "QrackStabilizer cannot mcmul()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1522
+ )
1454
1523
 
1455
1524
  if len(q) != len(o):
1456
1525
  raise RuntimeError("Lengths of list parameters are mismatched.")
@@ -1486,7 +1555,13 @@ class QrackSimulator:
1486
1555
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot mcdiv()! (Turn off just this option, in the constructor.)
1487
1556
  """
1488
1557
  if self.is_tensor_network:
1489
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot mcdiv()! (Turn off just this option, in the constructor.)")
1558
+ raise RuntimeError(
1559
+ "QrackSimulator with isTensorNetwork=True option cannot mcdiv()! (Turn off just this option, in the constructor.)"
1560
+ )
1561
+ if self.is_pure_stabilizer:
1562
+ raise RuntimeError(
1563
+ "QrackStabilizer cannot mcdiv()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1564
+ )
1490
1565
 
1491
1566
  if len(q) != len(o):
1492
1567
  raise RuntimeError("Lengths of list parameters are mismatched.")
@@ -1588,7 +1663,13 @@ class QrackSimulator:
1588
1663
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot mcpown()! (Turn off just this option, in the constructor.)
1589
1664
  """
1590
1665
  if self.is_tensor_network:
1591
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot mcpown()! (Turn off just this option, in the constructor.)")
1666
+ raise RuntimeError(
1667
+ "QrackSimulator with isTensorNetwork=True option cannot mcpown()! (Turn off just this option, in the constructor.)"
1668
+ )
1669
+ if self.is_pure_stabilizer:
1670
+ raise RuntimeError(
1671
+ "QrackStabilizer cannot mcpown()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1672
+ )
1592
1673
 
1593
1674
  if len(q) != len(o):
1594
1675
  raise RuntimeError("Lengths of list parameters are mismatched.")
@@ -1623,7 +1704,13 @@ class QrackSimulator:
1623
1704
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot lda()! (Turn off just this option, in the constructor.)
1624
1705
  """
1625
1706
  if self.is_tensor_network:
1626
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot lda()! (Turn off just this option, in the constructor.)")
1707
+ raise RuntimeError(
1708
+ "QrackSimulator with isTensorNetwork=True option cannot lda()! (Turn off just this option, in the constructor.)"
1709
+ )
1710
+ if self.is_pure_stabilizer:
1711
+ raise RuntimeError(
1712
+ "QrackStabilizer cannot lda()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1713
+ )
1627
1714
 
1628
1715
  Qrack.qrack_lib.LDA(
1629
1716
  self.sid,
@@ -1651,7 +1738,13 @@ class QrackSimulator:
1651
1738
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot adc()! (Turn off just this option, in the constructor.)
1652
1739
  """
1653
1740
  if self.is_tensor_network:
1654
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot adc()! (Turn off just this option, in the constructor.)")
1741
+ raise RuntimeError(
1742
+ "QrackSimulator with isTensorNetwork=True option cannot adc()! (Turn off just this option, in the constructor.)"
1743
+ )
1744
+ if self.is_pure_stabilizer:
1745
+ raise RuntimeError(
1746
+ "QrackStabilizer cannot adc()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1747
+ )
1655
1748
 
1656
1749
  Qrack.qrack_lib.ADC(
1657
1750
  self.sid,
@@ -1680,7 +1773,13 @@ class QrackSimulator:
1680
1773
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot sbc()! (Turn off just this option, in the constructor.)
1681
1774
  """
1682
1775
  if self.is_tensor_network:
1683
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot sbc()! (Turn off just this option, in the constructor.)")
1776
+ raise RuntimeError(
1777
+ "QrackSimulator with isTensorNetwork=True option cannot sbc()! (Turn off just this option, in the constructor.)"
1778
+ )
1779
+ if self.is_pure_stabilizer:
1780
+ raise RuntimeError(
1781
+ "QrackStabilizer cannot sbc()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1782
+ )
1684
1783
 
1685
1784
  Qrack.qrack_lib.SBC(
1686
1785
  self.sid,
@@ -1710,7 +1809,13 @@ class QrackSimulator:
1710
1809
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot hash()! (Turn off just this option, in the constructor.)
1711
1810
  """
1712
1811
  if self.is_tensor_network:
1713
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot hash()! (Turn off just this option, in the constructor.)")
1812
+ raise RuntimeError(
1813
+ "QrackSimulator with isTensorNetwork=True option cannot hash()! (Turn off just this option, in the constructor.)"
1814
+ )
1815
+ if self.is_pure_stabilizer:
1816
+ raise RuntimeError(
1817
+ "QrackStabilizer cannot hash()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
1818
+ )
1714
1819
 
1715
1820
  Qrack.qrack_lib.Hash(
1716
1821
  self.sid, len(q), self._ulonglong_byref(q), self._to_ubyte(len(q), t)
@@ -2019,7 +2124,9 @@ class QrackSimulator:
2019
2124
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot compose()! (Turn off just this option, in the constructor.)
2020
2125
  """
2021
2126
  if self.is_tensor_network:
2022
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot compose()! (Turn off just this option, in the constructor.)")
2127
+ raise RuntimeError(
2128
+ "QrackSimulator with isTensorNetwork=True option cannot compose()! (Turn off just this option, in the constructor.)"
2129
+ )
2023
2130
 
2024
2131
  Qrack.qrack_lib.Compose(self.sid, other.sid, self._ulonglong_byref(q))
2025
2132
  self._throw_if_error()
@@ -2027,9 +2134,7 @@ class QrackSimulator:
2027
2134
  def decompose(self, q):
2028
2135
  """Decompose system
2029
2136
 
2030
- Decompose the given qubit out of the system.
2031
- Warning: The qubit subsystem state must be separable, or the behavior
2032
- of this method is undefined.
2137
+ Factorize a set of contiguous bits with minimal fidelity loss.
2033
2138
 
2034
2139
  Args:
2035
2140
  q: qubit id
@@ -2039,10 +2144,12 @@ class QrackSimulator:
2039
2144
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot decompose()! (Turn off just this option, in the constructor.)
2040
2145
 
2041
2146
  Returns:
2042
- State of the systems.
2147
+ Decomposed subsystem simulator.
2043
2148
  """
2044
2149
  if self.is_tensor_network:
2045
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot decompose()! (Turn off just this option, in the constructor.)")
2150
+ raise RuntimeError(
2151
+ "QrackSimulator with isTensorNetwork=True option cannot decompose()! (Turn off just this option, in the constructor.)"
2152
+ )
2046
2153
 
2047
2154
  other = QrackSimulator()
2048
2155
  Qrack.qrack_lib.destroy(other.sid)
@@ -2054,10 +2161,8 @@ class QrackSimulator:
2054
2161
  def dispose(self, q):
2055
2162
  """Dispose qubits
2056
2163
 
2057
- Minimally decompose a set of contiguous bits from the separably
2058
- composed unit, and discard the separable bits.
2059
- Warning: The qubit subsystem state must be separable, or the behavior
2060
- of this method is undefined.
2164
+ Factorize a set of contiguous bits with minimal fidelity loss,
2165
+ and discard the separable bits.
2061
2166
 
2062
2167
  Args:
2063
2168
  q: qubit
@@ -2065,12 +2170,11 @@ class QrackSimulator:
2065
2170
  Raises:
2066
2171
  RuntimeError: QrackSimulator raised an exception.
2067
2172
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot dispose()! (Turn off just this option, in the constructor.)
2068
-
2069
- Returns:
2070
- State of the systems.
2071
2173
  """
2072
2174
  if self.is_tensor_network:
2073
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot dispose()! (Turn off just this option, in the constructor.)")
2175
+ raise RuntimeError(
2176
+ "QrackSimulator with isTensorNetwork=True option cannot dispose()! (Turn off just this option, in the constructor.)"
2177
+ )
2074
2178
 
2075
2179
  l = len(q)
2076
2180
  Qrack.qrack_lib.Dispose(self.sid, l, self._ulonglong_byref(q))
@@ -2133,8 +2237,8 @@ class QrackSimulator:
2133
2237
  def in_ket(self, ket):
2134
2238
  """Set state vector
2135
2239
 
2136
- Set state vector for the selected simulator ID.
2137
- Warning: State vector is not always the internal representation leading
2240
+ Set state vector for the selected simulator ID.
2241
+ Warning: State vector is not always the internal representation leading
2138
2242
  to sub-optimal performance of the method.
2139
2243
 
2140
2244
  Args:
@@ -2147,10 +2251,10 @@ class QrackSimulator:
2147
2251
  self._throw_if_error()
2148
2252
 
2149
2253
  def out_ket(self):
2150
- """Set state vector
2254
+ """Get state vector
2151
2255
 
2152
2256
  Returns the raw state vector of the simulator.
2153
- Warning: State vector is not always the internal representation leading
2257
+ Warning: State vector is not always the internal representation leading
2154
2258
  to sub-optimal performance of the method.
2155
2259
 
2156
2260
  Raises:
@@ -2165,6 +2269,90 @@ class QrackSimulator:
2165
2269
  self._throw_if_error()
2166
2270
  return [complex(r, i) for r, i in self._pairwise(ket)]
2167
2271
 
2272
+ def out_probs(self):
2273
+ """Get basis dimension probabilities
2274
+
2275
+ Returns the probabilities of each basis dimension in the state vector
2276
+ of the simulator.
2277
+
2278
+ Raises:
2279
+ RuntimeError: QrackSimulator raised an exception.
2280
+
2281
+ Returns:
2282
+ list representing the basis dimension probabilities.
2283
+ """
2284
+ prob_count = 1 << self.num_qubits()
2285
+ probs = self._real1_byref([0.0] * prob_count)
2286
+ Qrack.qrack_lib.OutProbs(self.sid, probs)
2287
+ self._throw_if_error()
2288
+ return list(probs)
2289
+
2290
+ def out_rdm(self, q):
2291
+ """Get reduced density matrix
2292
+
2293
+ Returns the raw reduced density matrix of the simulator, for the qubit list.
2294
+ Warning: State vector or is not always the internal representation leading
2295
+ to sub-optimal performance of the method.
2296
+
2297
+ Raises:
2298
+ RuntimeError: QrackSimulator raised an exception.
2299
+
2300
+ Returns:
2301
+ flat list structure representing the reduced density matrix.
2302
+ """
2303
+ amp_count = 1 << len(q)
2304
+ sqr_amp_count = amp_count * amp_count
2305
+ flat_rdm = self._qrack_complex_byref([complex(0, 0)] * sqr_amp_count)
2306
+ Qrack.qrack_lib.OutReducedDensityMatrix(self.sid, len(q), self._ulonglong_byref(q), flat_rdm)
2307
+ self._throw_if_error()
2308
+ return [complex(r, i) for r, i in self._pairwise(flat_rdm)]
2309
+
2310
+ def highest_prob_perm(self):
2311
+ """Get the permutation (bit string) with the highest probability
2312
+
2313
+ Returns the single highest-probability bit string in the Hilbert space
2314
+
2315
+ Raises:
2316
+ RuntimeError: QrackSimulator raised an exception.
2317
+
2318
+ Returns:
2319
+ Highest probability dimension index
2320
+ """
2321
+ num_q = self.num_qubits()
2322
+ num_words = (num_q + 63) // 64
2323
+ _r = (ctypes.c_ulonglong * num_words)()
2324
+ Qrack.qrack_lib.HighestProbAll(self.sid, _r)
2325
+ self._throw_if_error()
2326
+ r = 0
2327
+ for w in range(num_words):
2328
+ r <<= 64
2329
+ r |= _r[w]
2330
+ return r
2331
+
2332
+ def highest_n_prob_perm(self, n):
2333
+ """Get the top n permutations (bit strings) with the highest probability
2334
+
2335
+ Returns the top n highest-probability bit strings in the Hilbert space
2336
+
2337
+ Raises:
2338
+ RuntimeError: QrackSimulator raised an exception.
2339
+
2340
+ Returns:
2341
+ Top n highest probability dimension indices
2342
+ """
2343
+ num_q = self.num_qubits()
2344
+ num_words = (num_q + 63) // 64
2345
+ _r = (ctypes.c_ulonglong * (num_words * n))()
2346
+ Qrack.qrack_lib.HighestProbAllN(self.sid, n, _r)
2347
+ self._throw_if_error()
2348
+ r = [0] * n
2349
+ for i in range(n):
2350
+ r[i] = 0
2351
+ for w in range(num_words):
2352
+ r[i] <<= 64
2353
+ r[i] |= _r[(i * num_words) + w]
2354
+ return r
2355
+
2168
2356
  def prob_all(self, q):
2169
2357
  """Probabilities of all subset permutations
2170
2358
 
@@ -2241,11 +2429,13 @@ class QrackSimulator:
2241
2429
 
2242
2430
  if len(q) != len(c):
2243
2431
  raise RuntimeError("prob_perm argument lengths do not match.")
2244
- result = Qrack.qrack_lib.PermutationProb(self.sid, len(q), self._ulonglong_byref(q), self._bool_byref(c));
2432
+ result = Qrack.qrack_lib.PermutationProb(
2433
+ self.sid, len(q), self._ulonglong_byref(q), self._bool_byref(c)
2434
+ )
2245
2435
  self._throw_if_error()
2246
2436
  return result
2247
2437
 
2248
- def prob_perm_rdm(self, q, c, r = True):
2438
+ def prob_perm_rdm(self, q, c, r=True):
2249
2439
  """Probability of permutation, (tracing out the reduced
2250
2440
  density matrix without stabilizer ancillary qubits)
2251
2441
 
@@ -2267,7 +2457,9 @@ class QrackSimulator:
2267
2457
 
2268
2458
  if len(q) != len(c):
2269
2459
  raise RuntimeError("prob_perm argument lengths do not match.")
2270
- result = Qrack.qrack_lib.PermutationProbRdm(self.sid, len(q), self._ulonglong_byref(q), self._bool_byref(c), r);
2460
+ result = Qrack.qrack_lib.PermutationProbRdm(
2461
+ self.sid, len(q), self._ulonglong_byref(q), self._bool_byref(c), r
2462
+ )
2271
2463
  self._throw_if_error()
2272
2464
  return result
2273
2465
 
@@ -2292,7 +2484,7 @@ class QrackSimulator:
2292
2484
  self._throw_if_error()
2293
2485
  return result
2294
2486
 
2295
- def permutation_expectation_rdm(self, q, r = True):
2487
+ def permutation_expectation_rdm(self, q, r=True):
2296
2488
  """Permutation expectation value, (tracing out the reduced
2297
2489
  density matrix without stabilizer ancillary qubits)
2298
2490
 
@@ -2341,7 +2533,7 @@ class QrackSimulator:
2341
2533
  self._throw_if_error()
2342
2534
  return result
2343
2535
 
2344
- def factorized_expectation_rdm(self, q, c, r = True):
2536
+ def factorized_expectation_rdm(self, q, c, r=True):
2345
2537
  """Factorized expectation value, (tracing out the reduced
2346
2538
  density matrix without stabilizer ancillary qubits)
2347
2539
 
@@ -2361,7 +2553,9 @@ class QrackSimulator:
2361
2553
  Expectation value
2362
2554
  """
2363
2555
  if (len(q) << 1) != len(c):
2364
- raise RuntimeError("factorized_expectation_rdm argument lengths do not match.")
2556
+ raise RuntimeError(
2557
+ "factorized_expectation_rdm argument lengths do not match."
2558
+ )
2365
2559
  m = max([(x.bit_length() + 63) // 64 for x in c])
2366
2560
  result = Qrack.qrack_lib.FactorizedExpectationRdm(
2367
2561
  self.sid, len(q), self._ulonglong_byref(q), m, self._to_ulonglong(m, c), r
@@ -2387,14 +2581,16 @@ class QrackSimulator:
2387
2581
  Expectation value
2388
2582
  """
2389
2583
  if (len(q) << 1) != len(c):
2390
- raise RuntimeError("factorized_expectation_rdm argument lengths do not match.")
2584
+ raise RuntimeError(
2585
+ "factorized_expectation_rdm argument lengths do not match."
2586
+ )
2391
2587
  result = Qrack.qrack_lib.FactorizedExpectationFp(
2392
2588
  self.sid, len(q), self._ulonglong_byref(q), self._real1_byref(c)
2393
2589
  )
2394
2590
  self._throw_if_error()
2395
2591
  return result
2396
2592
 
2397
- def factorized_expectation_fp_rdm(self, q, c, r = True):
2593
+ def factorized_expectation_fp_rdm(self, q, c, r=True):
2398
2594
  """Factorized expectation value, (tracing out the reduced
2399
2595
  density matrix without stabilizer ancillary qubits)
2400
2596
 
@@ -2414,7 +2610,9 @@ class QrackSimulator:
2414
2610
  Expectation value
2415
2611
  """
2416
2612
  if (len(q) << 1) != len(c):
2417
- raise RuntimeError("factorized_expectation_fp_rdm argument lengths do not match.")
2613
+ raise RuntimeError(
2614
+ "factorized_expectation_fp_rdm argument lengths do not match."
2615
+ )
2418
2616
  result = Qrack.qrack_lib.FactorizedExpectationFpRdm(
2419
2617
  self.sid, len(q), self._ulonglong_byref(q), self._real1_byref(c), r
2420
2618
  )
@@ -2486,11 +2684,19 @@ class QrackSimulator:
2486
2684
  Expectation value
2487
2685
  """
2488
2686
  if (3 * len(q)) != len(b):
2489
- raise RuntimeError("unitary_expectation_eigenval qubit and basis argument lengths do not match.")
2687
+ raise RuntimeError(
2688
+ "unitary_expectation_eigenval qubit and basis argument lengths do not match."
2689
+ )
2490
2690
  if (len(q) << 1) != len(e):
2491
- raise RuntimeError("unitary_expectation_eigenval qubit and eigenvalue argument lengths do not match.")
2691
+ raise RuntimeError(
2692
+ "unitary_expectation_eigenval qubit and eigenvalue argument lengths do not match."
2693
+ )
2492
2694
  result = Qrack.qrack_lib.UnitaryExpectationEigenVal(
2493
- self.sid, len(q), self._ulonglong_byref(q), self._real1_byref(b), self._real1_byref(e)
2695
+ self.sid,
2696
+ len(q),
2697
+ self._ulonglong_byref(q),
2698
+ self._real1_byref(b),
2699
+ self._real1_byref(e),
2494
2700
  )
2495
2701
  self._throw_if_error()
2496
2702
  return result
@@ -2512,11 +2718,19 @@ class QrackSimulator:
2512
2718
  Expectation value
2513
2719
  """
2514
2720
  if (len(q) << 2) != len(b):
2515
- raise RuntimeError("matrix_expectation_eigenval qubit and basis argument lengths do not match.")
2721
+ raise RuntimeError(
2722
+ "matrix_expectation_eigenval qubit and basis argument lengths do not match."
2723
+ )
2516
2724
  if (len(q) << 1) != len(e):
2517
- raise RuntimeError("matrix_expectation_eigenval qubit and eigenvalue argument lengths do not match.")
2725
+ raise RuntimeError(
2726
+ "matrix_expectation_eigenval qubit and eigenvalue argument lengths do not match."
2727
+ )
2518
2728
  result = Qrack.qrack_lib.MatrixExpectationEigenVal(
2519
- self.sid, len(q), self._ulonglong_byref(q), self._complex_byref(b), self._real1_byref(e)
2729
+ self.sid,
2730
+ len(q),
2731
+ self._ulonglong_byref(q),
2732
+ self._complex_byref(b),
2733
+ self._real1_byref(e),
2520
2734
  )
2521
2735
  self._throw_if_error()
2522
2736
  return result
@@ -2565,7 +2779,7 @@ class QrackSimulator:
2565
2779
  self._throw_if_error()
2566
2780
  return result
2567
2781
 
2568
- def variance_rdm(self, q, r = True):
2782
+ def variance_rdm(self, q, r=True):
2569
2783
  """Permutation variance, (tracing out the reduced
2570
2784
  density matrix without stabilizer ancillary qubits)
2571
2785
 
@@ -2614,7 +2828,7 @@ class QrackSimulator:
2614
2828
  self._throw_if_error()
2615
2829
  return result
2616
2830
 
2617
- def factorized_variance_rdm(self, q, c, r = True):
2831
+ def factorized_variance_rdm(self, q, c, r=True):
2618
2832
  """Factorized variance, (tracing out the reduced
2619
2833
  density matrix without stabilizer ancillary qubits)
2620
2834
 
@@ -2667,7 +2881,7 @@ class QrackSimulator:
2667
2881
  self._throw_if_error()
2668
2882
  return result
2669
2883
 
2670
- def factorized_variance_fp_rdm(self, q, c, r = True):
2884
+ def factorized_variance_fp_rdm(self, q, c, r=True):
2671
2885
  """Factorized variance, (tracing out the reduced
2672
2886
  density matrix without stabilizer ancillary qubits)
2673
2887
 
@@ -2687,7 +2901,9 @@ class QrackSimulator:
2687
2901
  variance
2688
2902
  """
2689
2903
  if (len(q) << 1) != len(c):
2690
- raise RuntimeError("factorized_variance_fp_rdm argument lengths do not match.")
2904
+ raise RuntimeError(
2905
+ "factorized_variance_fp_rdm argument lengths do not match."
2906
+ )
2691
2907
  result = Qrack.qrack_lib.FactorizedVarianceFpRdm(
2692
2908
  self.sid, len(q), self._ulonglong_byref(q), self._real1_byref(c), r
2693
2909
  )
@@ -2759,11 +2975,19 @@ class QrackSimulator:
2759
2975
  variance
2760
2976
  """
2761
2977
  if (3 * len(q)) != len(b):
2762
- raise RuntimeError("unitary_variance_eigenval qubit and basis argument lengths do not match.")
2978
+ raise RuntimeError(
2979
+ "unitary_variance_eigenval qubit and basis argument lengths do not match."
2980
+ )
2763
2981
  if (len(q) << 1) != len(e):
2764
- raise RuntimeError("unitary_variance_eigenval qubit and eigenvalue argument lengths do not match.")
2982
+ raise RuntimeError(
2983
+ "unitary_variance_eigenval qubit and eigenvalue argument lengths do not match."
2984
+ )
2765
2985
  result = Qrack.qrack_lib.UnitaryVarianceEigenVal(
2766
- self.sid, len(q), self._ulonglong_byref(q), self._real1_byref(b), self._real1_byref(e)
2986
+ self.sid,
2987
+ len(q),
2988
+ self._ulonglong_byref(q),
2989
+ self._real1_byref(b),
2990
+ self._real1_byref(e),
2767
2991
  )
2768
2992
  self._throw_if_error()
2769
2993
  return result
@@ -2785,11 +3009,19 @@ class QrackSimulator:
2785
3009
  variance
2786
3010
  """
2787
3011
  if (len(q) << 2) != len(b):
2788
- raise RuntimeError("matrix_variance_eigenval qubit and basis argument lengths do not match.")
3012
+ raise RuntimeError(
3013
+ "matrix_variance_eigenval qubit and basis argument lengths do not match."
3014
+ )
2789
3015
  if (len(q) << 1) != len(e):
2790
- raise RuntimeError("matrix_variance_eigenval qubit and eigenvalue argument lengths do not match.")
3016
+ raise RuntimeError(
3017
+ "matrix_variance_eigenval qubit and eigenvalue argument lengths do not match."
3018
+ )
2791
3019
  result = Qrack.qrack_lib.MatrixVarianceEigenVal(
2792
- self.sid, len(q), self._ulonglong_byref(q), self._complex_byref(b), self._real1_byref(e)
3020
+ self.sid,
3021
+ len(q),
3022
+ self._ulonglong_byref(q),
3023
+ self._complex_byref(b),
3024
+ self._real1_byref(e),
2793
3025
  )
2794
3026
  self._throw_if_error()
2795
3027
  return result
@@ -2858,7 +3090,13 @@ class QrackSimulator:
2858
3090
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot phase_parity()! (Turn off just this option, in the constructor.)
2859
3091
  """
2860
3092
  if self.is_tensor_network:
2861
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot phase_parity()! (Turn off just this option, in the constructor.)")
3093
+ raise RuntimeError(
3094
+ "QrackSimulator with isTensorNetwork=True option cannot phase_parity()! (Turn off just this option, in the constructor.)"
3095
+ )
3096
+ if self.is_pure_stabilizer:
3097
+ raise RuntimeError(
3098
+ "QrackStabilizer cannot phase_parity()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
3099
+ )
2862
3100
 
2863
3101
  Qrack.qrack_lib.PhaseParity(
2864
3102
  self.sid, ctypes.c_double(la), len(q), self._ulonglong_byref(q)
@@ -2879,11 +3117,15 @@ class QrackSimulator:
2879
3117
  RuntimeError: QrackSimulator with isTensorNetwork=True option cannot phase_root_n()! (Turn off just this option, in the constructor.)
2880
3118
  """
2881
3119
  if self.is_tensor_network:
2882
- raise RuntimeError("QrackSimulator with isTensorNetwork=True option cannot phase_root_n()! (Turn off just this option, in the constructor.)")
3120
+ raise RuntimeError(
3121
+ "QrackSimulator with isTensorNetwork=True option cannot phase_root_n()! (Turn off just this option, in the constructor.)"
3122
+ )
3123
+ if self.is_pure_stabilizer:
3124
+ raise RuntimeError(
3125
+ "QrackStabilizer cannot phase_root_n()! (Create a QrackSimulator instead, also with isTensorNetwork=False.)"
3126
+ )
2883
3127
 
2884
- Qrack.qrack_lib.PhaseRootN(
2885
- self.sid, n, len(q), self._ulonglong_byref(q)
2886
- )
3128
+ Qrack.qrack_lib.PhaseRootN(self.sid, n, len(q), self._ulonglong_byref(q))
2887
3129
  self._throw_if_error()
2888
3130
 
2889
3131
  def try_separate_1qb(self, qi1):
@@ -2945,6 +3187,20 @@ class QrackSimulator:
2945
3187
  self._throw_if_error()
2946
3188
  return result
2947
3189
 
3190
+ def separate(self, qs):
3191
+ """Force manual multi-qubits seperation
3192
+
3193
+ Force separation as per `try_separate_tolerance`.
3194
+
3195
+ Args:
3196
+ qs: list of qubits to be decomposed
3197
+
3198
+ Raises:
3199
+ Runtimeerror: QrackSimulator raised an exception.
3200
+ """
3201
+ result = Qrack.qrack_lib.Separate(self.sid, len(qs), self._ulonglong_byref(qs))
3202
+ self._throw_if_error()
3203
+
2948
3204
  def get_unitary_fidelity(self):
2949
3205
  """Get fidelity estimate
2950
3206
 
@@ -3051,6 +3307,35 @@ class QrackSimulator:
3051
3307
  Qrack.qrack_lib.SetTInjection(self.sid, iti)
3052
3308
  self._throw_if_error()
3053
3309
 
3310
+ def set_noise_parameter(self, np):
3311
+ """Set noise parameter option
3312
+
3313
+ If noisy simulation is on, then this set the depolarization
3314
+ parameter per qubit per gate. (Default is 0.01.)
3315
+
3316
+ Args:
3317
+ np: depolarizing noise parameter
3318
+
3319
+ Raises:
3320
+ RuntimeError: QrackSimulator raised an exception.
3321
+ """
3322
+ Qrack.qrack_lib.SetNoiseParameter(self.sid, np)
3323
+ self._throw_if_error()
3324
+
3325
+ def normalize(self):
3326
+ """Normalize the state
3327
+
3328
+ This should never be necessary to use unless
3329
+ decompose() is "abused." ("Abusing" decompose()
3330
+ might lead to efficient entanglement-breaking
3331
+ channels, though.)
3332
+
3333
+ Raises:
3334
+ RuntimeError: QrackSimulator raised an exception.
3335
+ """
3336
+ Qrack.qrack_lib.Normalize(self.sid)
3337
+ self._throw_if_error()
3338
+
3054
3339
  def out_to_file(self, filename):
3055
3340
  """Output state to file (stabilizer only!)
3056
3341
 
@@ -3059,10 +3344,18 @@ class QrackSimulator:
3059
3344
  Args:
3060
3345
  filename: Name of file
3061
3346
  """
3062
- Qrack.qrack_lib.qstabilizer_out_to_file(self.sid, filename.encode('utf-8'))
3347
+ Qrack.qrack_lib.qstabilizer_out_to_file(self.sid, filename.encode("utf-8"))
3063
3348
  self._throw_if_error()
3064
3349
 
3065
- def in_from_file(filename, is_binary_decision_tree = False, is_paged = True, is_cpu_gpu_hybrid = True, is_opencl = True, is_host_pointer = False):
3350
+ def in_from_file(
3351
+ filename,
3352
+ is_binary_decision_tree=False,
3353
+ is_paged=True,
3354
+ is_cpu_gpu_hybrid=False,
3355
+ is_opencl=True,
3356
+ is_host_pointer=False,
3357
+ is_noisy=False,
3358
+ ):
3066
3359
  """Input state from file (stabilizer only!)
3067
3360
 
3068
3361
  Reads in a hybrid stabilizer state from file.
@@ -3083,9 +3376,10 @@ class QrackSimulator:
3083
3376
  isPaged=is_paged,
3084
3377
  isCpuGpuHybrid=is_cpu_gpu_hybrid,
3085
3378
  isOpenCL=is_opencl,
3086
- isHostPointer=is_host_pointer
3379
+ isHostPointer=is_host_pointer,
3380
+ isNoisy=is_noisy,
3087
3381
  )
3088
- Qrack.qrack_lib.qstabilizer_in_from_file(out.sid, filename.encode('utf-8'))
3382
+ Qrack.qrack_lib.qstabilizer_in_from_file(out.sid, filename.encode("utf-8"))
3089
3383
  out._throw_if_error()
3090
3384
 
3091
3385
  return out
@@ -3110,7 +3404,7 @@ class QrackSimulator:
3110
3404
  )
3111
3405
 
3112
3406
  lines = []
3113
- with open(filename, 'r') as file:
3407
+ with open(filename, "r") as file:
3114
3408
  lines = file.readlines()
3115
3409
 
3116
3410
  logical_qubits = int(lines[0])
@@ -3118,9 +3412,7 @@ class QrackSimulator:
3118
3412
 
3119
3413
  stabilizer_count = int(lines[2])
3120
3414
 
3121
- reg = QuantumRegister(stabilizer_qubits, name="q")
3122
- circ_qubits = [Qubit(reg, i) for i in range(stabilizer_qubits)]
3123
- clifford_circ = QuantumCircuit(reg)
3415
+ clifford_circ = None
3124
3416
  line_number = 3
3125
3417
  for i in range(stabilizer_count):
3126
3418
  shard_map_size = int(lines[line_number])
@@ -3132,14 +3424,10 @@ class QrackSimulator:
3132
3424
  line_number += 1
3133
3425
  shard_map[int(line[0])] = int(line[1])
3134
3426
 
3135
- sub_reg = []
3136
- for index, _ in sorted(shard_map.items(), key=lambda x: x[1]):
3137
- sub_reg.append(circ_qubits[index])
3138
-
3139
3427
  line_number += 1
3140
3428
  tableau = []
3141
3429
  row_count = shard_map_size << 1
3142
- for line in lines[line_number:(line_number + row_count)]:
3430
+ for line in lines[line_number : (line_number + row_count)]:
3143
3431
  bits = line.split()
3144
3432
  if len(bits) != (row_count + 1):
3145
3433
  raise QrackException("Invalid Qrack hybrid stabilizer file!")
@@ -3148,31 +3436,23 @@ class QrackSimulator:
3148
3436
  row.append(bool(int(bits[b])))
3149
3437
  row.append(bool((int(bits[-1]) >> 1) & 1))
3150
3438
  tableau.append(row)
3151
- line_number += (shard_map_size << 1)
3439
+ line_number += shard_map_size << 1
3152
3440
  tableau = np.array(tableau, bool)
3153
3441
 
3154
3442
  clifford = Clifford(tableau, validate=False, copy=False)
3155
- circ = clifford.to_circuit()
3156
-
3157
- for instr in circ.data:
3158
- qubits = instr.qubits
3159
- n_qubits = []
3160
- for qubit in qubits:
3161
- n_qubits.append(sub_reg[circ.find_bit(qubit)[0]])
3162
- instr.qubits = tuple(n_qubits)
3163
- clifford_circ.data.append(instr)
3164
- del circ
3443
+ clifford_circ = clifford.to_circuit()
3444
+ clifford_circ = QrackSimulator._reorder_qubits(clifford_circ, shard_map)
3165
3445
 
3166
3446
  non_clifford_gates = []
3167
3447
  g = 0
3168
3448
  for line in lines[line_number:]:
3169
3449
  i = 0
3170
3450
  tokens = line.split()
3171
- op = np.zeros((2,2), dtype=complex)
3451
+ op = np.zeros((2, 2), dtype=complex)
3172
3452
  row = []
3173
3453
  for _ in range(2):
3174
- amp = tokens[i].replace("(","").replace(")","").split(',')
3175
- row.append(float(amp[0]) + float(amp[1])*1j)
3454
+ amp = tokens[i].replace("(", "").replace(")", "").split(",")
3455
+ row.append(float(amp[0]) + float(amp[1]) * 1j)
3176
3456
  i = i + 1
3177
3457
  l = math.sqrt(np.real(row[0] * np.conj(row[0]) + row[1] * np.conj(row[1])))
3178
3458
  op[0][0] = row[0] / l
@@ -3185,8 +3465,8 @@ class QrackSimulator:
3185
3465
 
3186
3466
  row = []
3187
3467
  for _ in range(2):
3188
- amp = tokens[i].replace("(","").replace(")","").split(',')
3189
- row.append(float(amp[0]) + float(amp[1])*1j)
3468
+ amp = tokens[i].replace("(", "").replace(")", "").split(",")
3469
+ row.append(float(amp[0]) + float(amp[1]) * 1j)
3190
3470
  i = i + 1
3191
3471
  l = math.sqrt(np.real(row[0] * np.conj(row[0]) + row[1] * np.conj(row[1])))
3192
3472
  op[1][0] = row[0] / l
@@ -3205,9 +3485,27 @@ class QrackSimulator:
3205
3485
  non_clifford_gates.append(op)
3206
3486
  g = g + 1
3207
3487
 
3208
- basis_gates = ["rz", "h", "x", "y", "z", "sx", "sxdg", "sy", "sydg", "s", "sdg", "t", "tdg", "cx", "cy", "cz", "swap"]
3488
+ basis_gates = [
3489
+ "rz",
3490
+ "h",
3491
+ "x",
3492
+ "y",
3493
+ "z",
3494
+ "sx",
3495
+ "sxdg",
3496
+ "s",
3497
+ "sdg",
3498
+ "t",
3499
+ "tdg",
3500
+ "cx",
3501
+ "cy",
3502
+ "cz",
3503
+ "swap",
3504
+ ]
3209
3505
  try:
3210
- circ = transpile(clifford_circ, basis_gates=basis_gates, optimization_level=2)
3506
+ circ = transpile(
3507
+ clifford_circ, basis_gates=basis_gates, optimization_level=2
3508
+ )
3211
3509
  except:
3212
3510
  circ = clifford_circ
3213
3511
 
@@ -3222,6 +3520,35 @@ class QrackSimulator:
3222
3520
 
3223
3521
  return circ
3224
3522
 
3523
+ def _reorder_qubits(circuit, mapping):
3524
+ """
3525
+ Reorders qubits in the circuit according to the given mapping using SWAP gates.
3526
+ (Thanks to "Elara," an OpenAI GPT, for this implementation)
3527
+
3528
+ Parameters:
3529
+ - circuit (QuantumCircuit): The circuit to modify.
3530
+ - mapping (dict): Dictionary mapping internal qubit indices to logical qubit indices.
3531
+
3532
+ Returns:
3533
+ - QuantumCircuit: The modified circuit with qubits reordered.
3534
+ """
3535
+ swaps = []
3536
+
3537
+ # Determine swaps to fix the order
3538
+ for logical_index in sorted(mapping):
3539
+ internal_index = mapping[logical_index]
3540
+ if logical_index != internal_index:
3541
+ swaps.append((logical_index, internal_index))
3542
+ # Update the reverse mapping for subsequent swaps
3543
+ mapping[logical_index] = logical_index
3544
+ mapping[internal_index] = internal_index
3545
+
3546
+ # Apply the swaps to the circuit
3547
+ for qubit1, qubit2 in swaps:
3548
+ circuit.swap(qubit1, qubit2)
3549
+
3550
+ return circuit
3551
+
3225
3552
  def file_to_optimized_qiskit_circuit(filename):
3226
3553
  """Convert an output state file to a Qiskit circuit
3227
3554
 
@@ -3239,7 +3566,7 @@ class QrackSimulator:
3239
3566
  QrackCircuit, you must install Qiskit, numpy, and math!
3240
3567
  """
3241
3568
  circ = QrackSimulator.file_to_qiskit_circuit(filename)
3242
-
3569
+
3243
3570
  width = 0
3244
3571
  with open(filename, "r", encoding="utf-8") as file:
3245
3572
  width = int(file.readline())
@@ -3248,7 +3575,7 @@ class QrackSimulator:
3248
3575
  sqrt_ni = np.sqrt(-1j)
3249
3576
  sqrt1_2 = 1 / math.sqrt(2)
3250
3577
  ident = np.eye(2, dtype=np.complex128)
3251
- # passable_gates = ["unitary", "rz", "h", "x", "y", "z", "sx", "sxdg", "sy", "sydg", "s", "sdg", "t", "tdg"]
3578
+ # passable_gates = ["unitary", "rz", "h", "x", "y", "z", "sx", "sxdg", "s", "sdg", "t", "tdg"]
3252
3579
 
3253
3580
  passed_swaps = []
3254
3581
  for i in range(0, circ.width()):
@@ -3259,40 +3586,104 @@ class QrackSimulator:
3259
3586
  op = circ.data[j].operation
3260
3587
  qubits = circ.data[j].qubits
3261
3588
  if len(qubits) > 2:
3262
- raise RuntimeError("Something went wrong while optimizing circuit! (Found a gate with 3 or more qubits)")
3589
+ raise RuntimeError(
3590
+ "Something went wrong while optimizing circuit! (Found a gate with 3 or more qubits)"
3591
+ )
3263
3592
  q1 = circ.find_bit(qubits[0])[0]
3264
3593
  if (len(qubits) < 2) and (q1 == i):
3265
3594
  if op.name == "unitary":
3266
3595
  non_clifford = np.matmul(op.params[0], non_clifford)
3267
3596
  elif op.name == "rz":
3268
3597
  lm = float(op.params[0])
3269
- non_clifford = np.matmul([[np.exp(-1j * lm / 2), 0], [0, np.exp(1j * lm / 2)]], non_clifford)
3598
+ non_clifford = np.matmul(
3599
+ [[np.exp(-1j * lm / 2), 0], [0, np.exp(1j * lm / 2)]],
3600
+ non_clifford,
3601
+ )
3270
3602
  elif op.name == "h":
3271
- non_clifford = np.matmul(np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128), non_clifford)
3603
+ non_clifford = np.matmul(
3604
+ np.array(
3605
+ [[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128
3606
+ ),
3607
+ non_clifford,
3608
+ )
3272
3609
  elif op.name == "x":
3273
- non_clifford = np.matmul(np.array([[0, 1], [1, 0]], np.complex128), non_clifford)
3610
+ non_clifford = np.matmul(
3611
+ np.array([[0, 1], [1, 0]], np.complex128), non_clifford
3612
+ )
3274
3613
  elif op.name == "y":
3275
- non_clifford = np.matmul(np.array([[0, -1j], [1j, 0]], np.complex128), non_clifford)
3614
+ non_clifford = np.matmul(
3615
+ np.array([[0, -1j], [1j, 0]], np.complex128), non_clifford
3616
+ )
3276
3617
  elif op.name == "z":
3277
- non_clifford = np.matmul(np.array([[1, 0], [0, -1]], np.complex128), non_clifford)
3618
+ non_clifford = np.matmul(
3619
+ np.array([[1, 0], [0, -1]], np.complex128), non_clifford
3620
+ )
3278
3621
  elif op.name == "sx":
3279
- non_clifford = np.matmul(np.array([[(1+1j)/2, (1-1j)/2], [(1-1j)/2, (1+1j)/2]], np.complex128), non_clifford)
3622
+ non_clifford = np.matmul(
3623
+ np.array(
3624
+ [
3625
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3626
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3627
+ ],
3628
+ np.complex128,
3629
+ ),
3630
+ non_clifford,
3631
+ )
3280
3632
  elif op.name == "sxdg":
3281
- non_clifford = np.matmul(np.array([[(1-1j)/2, (1+1j)/2], [(1+1j)/2, (1-1j)/2]], np.complex128), non_clifford)
3633
+ non_clifford = np.matmul(
3634
+ np.array(
3635
+ [
3636
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3637
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3638
+ ],
3639
+ np.complex128,
3640
+ ),
3641
+ non_clifford,
3642
+ )
3282
3643
  elif op.name == "sy":
3283
- non_clifford = np.matmul(np.array([[(1+1j)/2, -(1+1j)/2], [(1+1j)/2, (1+1j)/2]], np.complex128), non_clifford)
3644
+ non_clifford = np.matmul(
3645
+ np.array(
3646
+ [
3647
+ [(1 + 1j) / 2, -(1 + 1j) / 2],
3648
+ [(1 + 1j) / 2, (1 + 1j) / 2],
3649
+ ],
3650
+ np.complex128,
3651
+ ),
3652
+ non_clifford,
3653
+ )
3284
3654
  elif op.name == "sydg":
3285
- non_clifford = np.matmul(np.array([[(1-1j)/2, (1-1j)/2], [(-1+1j)/2, (1-1j)/2]], np.complex128), non_clifford)
3655
+ non_clifford = np.matmul(
3656
+ np.array(
3657
+ [
3658
+ [(1 - 1j) / 2, (1 - 1j) / 2],
3659
+ [(-1 + 1j) / 2, (1 - 1j) / 2],
3660
+ ],
3661
+ np.complex128,
3662
+ ),
3663
+ non_clifford,
3664
+ )
3286
3665
  elif op.name == "s":
3287
- non_clifford = np.matmul(np.array([[1, 0], [0, 1j]], np.complex128), non_clifford)
3666
+ non_clifford = np.matmul(
3667
+ np.array([[1, 0], [0, 1j]], np.complex128), non_clifford
3668
+ )
3288
3669
  elif op.name == "sdg":
3289
- non_clifford = np.matmul(np.array([[1, 0], [0, -1j]], np.complex128), non_clifford)
3670
+ non_clifford = np.matmul(
3671
+ np.array([[1, 0], [0, -1j]], np.complex128), non_clifford
3672
+ )
3290
3673
  elif op.name == "t":
3291
- non_clifford = np.matmul(np.array([[1, 0], [0, sqrt_pi]], np.complex128), non_clifford)
3674
+ non_clifford = np.matmul(
3675
+ np.array([[1, 0], [0, sqrt_pi]], np.complex128),
3676
+ non_clifford,
3677
+ )
3292
3678
  elif op.name == "tdg":
3293
- non_clifford = np.matmul(np.array([[1, 0], [0, sqrt_ni]], np.complex128), non_clifford)
3679
+ non_clifford = np.matmul(
3680
+ np.array([[1, 0], [0, sqrt_ni]], np.complex128),
3681
+ non_clifford,
3682
+ )
3294
3683
  else:
3295
- raise RuntimeError("Something went wrong while optimizing circuit! (Dropped a single-qubit gate.)")
3684
+ raise RuntimeError(
3685
+ "Something went wrong while optimizing circuit! (Dropped a single-qubit gate.)"
3686
+ )
3296
3687
 
3297
3688
  del circ.data[j]
3298
3689
  continue
@@ -3308,7 +3699,7 @@ class QrackSimulator:
3308
3699
  continue
3309
3700
 
3310
3701
  if op.name == "swap":
3311
- i = (q2 if i == q1 else q1)
3702
+ i = q2 if i == q1 else q1
3312
3703
 
3313
3704
  if circ.data[j] in passed_swaps:
3314
3705
  passed_swaps.remove(circ.data[j])
@@ -3318,26 +3709,30 @@ class QrackSimulator:
3318
3709
  passed_swaps.append(circ.data[j])
3319
3710
 
3320
3711
  j += 1
3321
- continue
3712
+ continue
3322
3713
 
3323
- if (q1 == i) and ((op.name == "cx") or (op.name == "cy") or (op.name == "cz")):
3324
- if (np.isclose(np.abs(non_clifford[0][1]), 0) and np.isclose(np.abs(non_clifford[1][0]), 0)):
3714
+ if (q1 == i) and (
3715
+ (op.name == "cx") or (op.name == "cy") or (op.name == "cz")
3716
+ ):
3717
+ if np.isclose(np.abs(non_clifford[0][1]), 0) and np.isclose(
3718
+ np.abs(non_clifford[1][0]), 0
3719
+ ):
3325
3720
  # If we're not buffering anything but phase, the blocking gate has no effect, and we're safe to continue.
3326
3721
  del circ.data[j]
3327
3722
  continue
3328
3723
 
3329
- if (np.isclose(np.abs(non_clifford[0][0]), 0) and np.isclose(np.abs(non_clifford[1][1]), 0)):
3724
+ if np.isclose(np.abs(non_clifford[0][0]), 0) and np.isclose(
3725
+ np.abs(non_clifford[1][1]), 0
3726
+ ):
3330
3727
  # If we're buffering full negation (plus phase), the control qubit can be dropped.
3331
- c = QuantumCircuit(1)
3728
+ c = QuantumCircuit(circ.qubits)
3332
3729
  if op.name == "cx":
3333
- c.x(0)
3730
+ c.x(qubits[1])
3334
3731
  elif op.name == "cy":
3335
- c.y(0)
3732
+ c.y(qubits[1])
3336
3733
  else:
3337
- c.z(0)
3338
- instr = c.data[0]
3339
- instr.qubits = (qubits[1],)
3340
- circ.data[j] = copy.deepcopy(instr)
3734
+ c.z(qubits[1])
3735
+ circ.data[j] = copy.deepcopy(c.data[0])
3341
3736
 
3342
3737
  j += 1
3343
3738
  continue
@@ -3348,11 +3743,9 @@ class QrackSimulator:
3348
3743
  break
3349
3744
 
3350
3745
  # We're blocked, so we insert our buffer at this place in the circuit definition.
3351
- c = QuantumCircuit(1)
3352
- c.unitary(non_clifford, 0)
3353
- instr = c.data[0]
3354
- instr.qubits = (qubits[0],)
3355
- circ.data.insert(j, copy.deepcopy(instr))
3746
+ c = QuantumCircuit(circ.qubits)
3747
+ c.unitary(non_clifford, qubits[0])
3748
+ circ.data.insert(j, copy.deepcopy(c.data[0]))
3356
3749
 
3357
3750
  non_clifford = np.copy(ident)
3358
3751
  break
@@ -3370,40 +3763,104 @@ class QrackSimulator:
3370
3763
  op = circ.data[j].operation
3371
3764
  qubits = circ.data[j].qubits
3372
3765
  if len(qubits) > 2:
3373
- raise RuntimeError("Something went wrong while optimizing circuit! (Found a gate with 3 or more qubits.)")
3766
+ raise RuntimeError(
3767
+ "Something went wrong while optimizing circuit! (Found a gate with 3 or more qubits.)"
3768
+ )
3374
3769
  q1 = circ.find_bit(qubits[0])[0]
3375
3770
  if (len(qubits) < 2) and (q1 == i):
3376
3771
  if op.name == "unitary":
3377
3772
  non_clifford = np.matmul(non_clifford, op.params[0])
3378
3773
  elif op.name == "rz":
3379
3774
  lm = float(op.params[0])
3380
- non_clifford = np.matmul(non_clifford, [[np.exp(-1j * lm / 2), 0], [0, np.exp(1j * lm / 2)]])
3775
+ non_clifford = np.matmul(
3776
+ non_clifford,
3777
+ [[np.exp(-1j * lm / 2), 0], [0, np.exp(1j * lm / 2)]],
3778
+ )
3381
3779
  elif op.name == "h":
3382
- non_clifford = np.matmul(non_clifford, np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128))
3780
+ non_clifford = np.matmul(
3781
+ non_clifford,
3782
+ np.array(
3783
+ [[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128
3784
+ ),
3785
+ )
3383
3786
  elif op.name == "x":
3384
- non_clifford = np.matmul(non_clifford, np.array([[0, 1], [1, 0]], np.complex128))
3787
+ non_clifford = np.matmul(
3788
+ non_clifford, np.array([[0, 1], [1, 0]], np.complex128)
3789
+ )
3385
3790
  elif op.name == "y":
3386
- non_clifford = np.matmul(non_clifford, np.array([[0, -1j], [1j, 0]], np.complex128))
3791
+ non_clifford = np.matmul(
3792
+ non_clifford, np.array([[0, -1j], [1j, 0]], np.complex128)
3793
+ )
3387
3794
  elif op.name == "z":
3388
- non_clifford = np.matmul(non_clifford, np.array([[1, 0], [0, -1]], np.complex128))
3795
+ non_clifford = np.matmul(
3796
+ non_clifford, np.array([[1, 0], [0, -1]], np.complex128)
3797
+ )
3389
3798
  elif op.name == "sx":
3390
- non_clifford = np.matmul(non_clifford, np.array([[(1+1j)/2, (1-1j)/2], [(1-1j)/2, (1+1j)/2]], np.complex128))
3799
+ non_clifford = np.matmul(
3800
+ non_clifford,
3801
+ np.array(
3802
+ [
3803
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3804
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3805
+ ],
3806
+ np.complex128,
3807
+ ),
3808
+ )
3391
3809
  elif op.name == "sxdg":
3392
- non_clifford = np.matmul(non_clifford, np.array([[(1-1j)/2, (1+1j)/2], [(1+1j)/2, (1-1j)/2]], np.complex128))
3810
+ non_clifford = np.matmul(
3811
+ non_clifford,
3812
+ np.array(
3813
+ [
3814
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3815
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3816
+ ],
3817
+ np.complex128,
3818
+ ),
3819
+ )
3393
3820
  elif op.name == "sy":
3394
- non_clifford = np.matmul(non_clifford, np.array([[(1+1j)/2, -(1+1j)/2], [(1+1j)/2, (1+1j)/2]], np.complex128))
3821
+ non_clifford = np.matmul(
3822
+ non_clifford,
3823
+ np.array(
3824
+ [
3825
+ [(1 + 1j) / 2, -(1 + 1j) / 2],
3826
+ [(1 + 1j) / 2, (1 + 1j) / 2],
3827
+ ],
3828
+ np.complex128,
3829
+ ),
3830
+ )
3395
3831
  elif op.name == "sydg":
3396
- non_clifford = np.matmul(non_clifford, np.array([[(1-1j)/2, (1-1j)/2], [(-1+1j)/2, (1-1j)/2]], np.complex128))
3832
+ non_clifford = np.matmul(
3833
+ non_clifford,
3834
+ np.array(
3835
+ [
3836
+ [(1 - 1j) / 2, (1 - 1j) / 2],
3837
+ [(-1 + 1j) / 2, (1 - 1j) / 2],
3838
+ ],
3839
+ np.complex128,
3840
+ ),
3841
+ )
3397
3842
  elif op.name == "s":
3398
- non_clifford = np.matmul(non_clifford, np.array([[1, 0], [0, 1j]], np.complex128))
3843
+ non_clifford = np.matmul(
3844
+ non_clifford, np.array([[1, 0], [0, 1j]], np.complex128)
3845
+ )
3399
3846
  elif op.name == "sdg":
3400
- non_clifford = np.matmul(non_clifford, np.array([[1, 0], [0, -1j]], np.complex128))
3847
+ non_clifford = np.matmul(
3848
+ non_clifford, np.array([[1, 0], [0, -1j]], np.complex128)
3849
+ )
3401
3850
  elif op.name == "t":
3402
- non_clifford = np.matmul(non_clifford, np.array([[1, 0], [0, sqrt_pi]], np.complex128))
3851
+ non_clifford = np.matmul(
3852
+ non_clifford,
3853
+ np.array([[1, 0], [0, sqrt_pi]], np.complex128),
3854
+ )
3403
3855
  elif op.name == "tdg":
3404
- non_clifford = np.matmul(non_clifford, np.array([[1, 0], [0, sqrt_ni]], np.complex128))
3856
+ non_clifford = np.matmul(
3857
+ non_clifford,
3858
+ np.array([[1, 0], [0, sqrt_ni]], np.complex128),
3859
+ )
3405
3860
  else:
3406
- raise RuntimeError("Something went wrong while optimizing circuit! (Dropped a single-qubit gate.)")
3861
+ raise RuntimeError(
3862
+ "Something went wrong while optimizing circuit! (Dropped a single-qubit gate.)"
3863
+ )
3407
3864
 
3408
3865
  del circ.data[j]
3409
3866
  j -= 1
@@ -3420,7 +3877,7 @@ class QrackSimulator:
3420
3877
  continue
3421
3878
 
3422
3879
  if (op.name == "swap") and (q1 >= width) and (q2 >= width):
3423
- i = (q2 if i == q1 else q1)
3880
+ i = q2 if i == q1 else q1
3424
3881
  if circ.data[j] in passed_swaps:
3425
3882
  passed_swaps.remove(circ.data[j])
3426
3883
  del circ.data[j]
@@ -3430,7 +3887,14 @@ class QrackSimulator:
3430
3887
  j -= 1
3431
3888
  continue
3432
3889
 
3433
- if (q1 == i) and ((op.name == "cx") or (op.name == "cy") or (op.name == "cz")) and (np.isclose(np.abs(non_clifford[0][1]), 0) and np.isclose(np.abs(non_clifford[1][0]), 0)):
3890
+ if (
3891
+ (q1 == i)
3892
+ and ((op.name == "cx") or (op.name == "cy") or (op.name == "cz"))
3893
+ and (
3894
+ np.isclose(np.abs(non_clifford[0][1]), 0)
3895
+ and np.isclose(np.abs(non_clifford[1][0]), 0)
3896
+ )
3897
+ ):
3434
3898
  # If we're not buffering anything but phase, this commutes with control, and we're safe to continue.
3435
3899
  j -= 1
3436
3900
  continue
@@ -3439,21 +3903,18 @@ class QrackSimulator:
3439
3903
  orig_instr = circ.data[j]
3440
3904
  del circ.data[j]
3441
3905
 
3442
- h = QuantumCircuit(1)
3443
- h.h(0)
3444
- instr = h.data[0]
3445
-
3446
3906
  # We're replacing CNOT with CNOT in the opposite direction plus four H gates
3447
- instr.qubits = (qubits[0],)
3448
- circ.data.insert(j, copy.deepcopy(instr))
3449
- instr.qubits = (qubits[1],)
3450
- circ.data.insert(j, copy.deepcopy(instr))
3451
- orig_instr.qubits = (qubits[1], qubits[0])
3452
- circ.data.insert(j, copy.deepcopy(orig_instr))
3453
- instr.qubits = (qubits[0],)
3454
- circ.data.insert(j, copy.deepcopy(instr))
3455
- instr.qubits = (qubits[1],)
3456
- circ.data.insert(j, copy.deepcopy(instr))
3907
+ rep = QuantumCircuit(circ.qubits)
3908
+ rep.h(qubits[0])
3909
+ circ.data.insert(j, copy.deepcopy(rep.data[0]))
3910
+ rep.h(qubits[1])
3911
+ circ.data.insert(j, copy.deepcopy(rep.data[1]))
3912
+ rep.cx(qubits[1], qubits[0])
3913
+ circ.data.insert(j, copy.deepcopy(rep.data[2]))
3914
+ rep.h(qubits[0])
3915
+ circ.data.insert(j, copy.deepcopy(rep.data[3]))
3916
+ rep.h(qubits[1])
3917
+ circ.data.insert(j, copy.deepcopy(rep.data[4]))
3457
3918
 
3458
3919
  j += 4
3459
3920
  continue
@@ -3464,38 +3925,62 @@ class QrackSimulator:
3464
3925
  break
3465
3926
 
3466
3927
  # We're blocked, so we insert our buffer at this place in the circuit definition.
3467
- c = QuantumCircuit(1)
3468
- c.unitary(non_clifford, 0)
3469
- instr = c.data[0]
3470
- instr.qubits = (qubits[0],)
3471
- circ.data.insert(j + 1, copy.deepcopy(instr))
3928
+ c = QuantumCircuit(circ.qubits)
3929
+ c.unitary(non_clifford, qubits[0])
3930
+ circ.data.insert(j + 1, copy.deepcopy(c.data[0]))
3472
3931
 
3473
3932
  break
3474
3933
 
3475
3934
  # Re-injection branch (apply gadget to target)
3476
- to_inject = np.matmul(non_clifford, np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]]))
3935
+ to_inject = np.matmul(
3936
+ non_clifford, np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]])
3937
+ )
3477
3938
  if np.allclose(to_inject, ident):
3478
3939
  # No buffer content to write to circuit definition
3479
3940
  del circ.data[j]
3480
3941
  j -= 1
3481
3942
  continue
3482
3943
 
3483
- c = QuantumCircuit(1)
3484
- c.unitary(to_inject, 0)
3485
- instr = c.data[0]
3486
- instr.qubits = (qubits[0],)
3487
- circ.data[j] = copy.deepcopy(instr)
3944
+ c = QuantumCircuit(circ.qubits)
3945
+ c.unitary(to_inject, qubits[0])
3946
+ circ.data.insert(j, copy.deepcopy(c.data[0]))
3488
3947
  j -= 1
3489
3948
 
3490
- basis_gates=["u", "rz", "h", "x", "y", "z", "sx", "sxdg", "sy", "sydg", "s", "sdg", "t", "tdg", "cx", "cy", "cz", "swap"]
3949
+ basis_gates = [
3950
+ "u",
3951
+ "rz",
3952
+ "h",
3953
+ "x",
3954
+ "y",
3955
+ "z",
3956
+ "sx",
3957
+ "sxdg",
3958
+ "s",
3959
+ "sdg",
3960
+ "t",
3961
+ "tdg",
3962
+ "cx",
3963
+ "cy",
3964
+ "cz",
3965
+ "swap",
3966
+ ]
3491
3967
  circ = transpile(circ, basis_gates=basis_gates, optimization_level=2)
3492
3968
 
3493
- #Eliminate unused ancillae
3494
- qasm = circ.qasm()
3495
- qasm = qasm.replace("qreg q[" + str(circ.width()) + "];", "qreg q[" + str(width) + "];")
3496
- highest_index = max([int(x) for x in re.findall(r"\[(.*?)\]", qasm) if x.isdigit()])
3969
+ # Eliminate unused ancillae
3970
+ try:
3971
+ qasm = qasm3.dumps(circ)
3972
+ except:
3973
+ qasm = circ.qasm()
3974
+ qasm = qasm.replace(
3975
+ "qreg q[" + str(circ.width()) + "];", "qreg q[" + str(width) + "];"
3976
+ )
3977
+ highest_index = max(
3978
+ [int(x) for x in re.findall(r"\[(.*?)\]", qasm) if x.isdigit()]
3979
+ )
3497
3980
  if highest_index != width:
3498
- qasm = qasm.replace("qreg q[" + str(width) + "];", "qreg q[" + str(highest_index) + "];")
3981
+ qasm = qasm.replace(
3982
+ "qreg q[" + str(width) + "];", "qreg q[" + str(highest_index) + "];"
3983
+ )
3499
3984
 
3500
3985
  orig_circ = circ
3501
3986
  try:
@@ -3561,11 +4046,11 @@ class QrackSimulator:
3561
4046
  def _apply_op(self, operation):
3562
4047
  name = operation.name
3563
4048
 
3564
- if (name == 'id') or (name == 'barrier'):
4049
+ if (name == "id") or (name == "barrier"):
3565
4050
  # Skip measurement logic
3566
4051
  return
3567
4052
 
3568
- conditional = getattr(operation, 'conditional', None)
4053
+ conditional = getattr(operation, "conditional", None)
3569
4054
  if isinstance(conditional, int):
3570
4055
  conditional_bit_set = (self._classical_register >> conditional) & 1
3571
4056
  if not conditional_bit_set:
@@ -3580,154 +4065,191 @@ class QrackSimulator:
3580
4065
  if value != int(conditional.val, 16):
3581
4066
  return
3582
4067
 
3583
- if (name == 'u1') or (name == 'p'):
3584
- self._sim.u(operation.qubits[0], 0, 0, float(operation.params[0]))
3585
- elif name == 'u2':
4068
+ if (name == "u1") or (name == "p"):
4069
+ self._sim.u(operation.qubits[0]._index, 0, 0, float(operation.params[0]))
4070
+ elif name == "u2":
3586
4071
  self._sim.u(
3587
- operation.qubits[0],
4072
+ operation.qubits[0]._index,
3588
4073
  math.pi / 2,
3589
4074
  float(operation.params[0]),
3590
4075
  float(operation.params[1]),
3591
4076
  )
3592
- elif (name == 'u3') or (name == 'u'):
4077
+ elif (name == "u3") or (name == "u"):
3593
4078
  self._sim.u(
3594
- operation.qubits[0],
4079
+ operation.qubits[0]._index,
3595
4080
  float(operation.params[0]),
3596
4081
  float(operation.params[1]),
3597
4082
  float(operation.params[2]),
3598
4083
  )
3599
- elif (name == 'unitary') and (len(operation.qubits) == 1):
3600
- self._sim.mtrx(operation.params[0].flatten(), operation.qubits[0])
3601
- elif name == 'r':
4084
+ elif (name == "unitary") and (len(operation.qubits) == 1):
4085
+ self._sim.mtrx(operation.params[0].flatten(), operation.qubits[0]._index)
4086
+ elif name == "r":
3602
4087
  self._sim.u(
3603
- operation.qubits[0],
4088
+ operation.qubits[0]._index,
3604
4089
  float(operation.params[0]),
3605
4090
  float(operation.params[1]) - math.pi / 2,
3606
4091
  (-1 * float(operation.params[1])) + math.pi / 2,
3607
4092
  )
3608
- elif name == 'rx':
3609
- self._sim.r(Pauli.PauliX, float(operation.params[0]), operation.qubits[0])
3610
- elif name == 'ry':
3611
- self._sim.r(Pauli.PauliY, float(operation.params[0]), operation.qubits[0])
3612
- elif name == 'rz':
3613
- self._sim.r(Pauli.PauliZ, float(operation.params[0]), operation.qubits[0])
3614
- elif name == 'h':
3615
- self._sim.h(operation.qubits[0])
3616
- elif name == 'x':
3617
- self._sim.x(operation.qubits[0])
3618
- elif name == 'y':
3619
- self._sim.y(operation.qubits[0])
3620
- elif name == 'z':
3621
- self._sim.z(operation.qubits[0])
3622
- elif name == 's':
3623
- self._sim.s(operation.qubits[0])
3624
- elif name == 'sdg':
3625
- self._sim.adjs(operation.qubits[0])
3626
- elif name == 'sx':
4093
+ elif name == "rx":
4094
+ self._sim.r(
4095
+ Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index
4096
+ )
4097
+ elif name == "ry":
4098
+ self._sim.r(
4099
+ Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index
4100
+ )
4101
+ elif name == "rz":
4102
+ self._sim.r(
4103
+ Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index
4104
+ )
4105
+ elif name == "h":
4106
+ self._sim.h(operation.qubits[0]._index)
4107
+ elif name == "x":
4108
+ self._sim.x(operation.qubits[0]._index)
4109
+ elif name == "y":
4110
+ self._sim.y(operation.qubits[0]._index)
4111
+ elif name == "z":
4112
+ self._sim.z(operation.qubits[0]._index)
4113
+ elif name == "s":
4114
+ self._sim.s(operation.qubits[0]._index)
4115
+ elif name == "sdg":
4116
+ self._sim.adjs(operation.qubits[0]._index)
4117
+ elif name == "sx":
3627
4118
  self._sim.mtrx(
3628
4119
  [(1 + 1j) / 2, (1 - 1j) / 2, (1 - 1j) / 2, (1 + 1j) / 2],
3629
- operation.qubits[0],
4120
+ operation.qubits[0]._index,
3630
4121
  )
3631
- elif name == 'sxdg':
4122
+ elif name == "sxdg":
3632
4123
  self._sim.mtrx(
3633
4124
  [(1 - 1j) / 2, (1 + 1j) / 2, (1 + 1j) / 2, (1 - 1j) / 2],
3634
- operation.qubits[0],
4125
+ operation.qubits[0]._index,
3635
4126
  )
3636
- elif name == 't':
3637
- self._sim.t(operation.qubits[0])
3638
- elif name == 'tdg':
3639
- self._sim.adjt(operation.qubits[0])
3640
- elif name == 'cu1':
4127
+ elif name == "t":
4128
+ self._sim.t(operation.qubits[0]._index)
4129
+ elif name == "tdg":
4130
+ self._sim.adjt(operation.qubits[0]._index)
4131
+ elif name == "cu1":
3641
4132
  self._sim.mcu(
3642
- operation.qubits[0:1], operation.qubits[1], 0, 0, float(operation.params[0])
4133
+ [q._index for q in operation.qubits[0:1]],
4134
+ operation.qubits[1]._index,
4135
+ 0,
4136
+ 0,
4137
+ float(operation.params[0]),
3643
4138
  )
3644
- elif name == 'cu2':
4139
+ elif name == "cu2":
3645
4140
  self._sim.mcu(
3646
- operation.qubits[0:1],
3647
- operation.qubits[1],
4141
+ [q._index for q in operation.qubits[0:1]],
4142
+ operation.qubits[1]._index,
3648
4143
  math.pi / 2,
3649
4144
  float(operation.params[0]),
3650
4145
  float(operation.params[1]),
3651
4146
  )
3652
- elif (name == 'cu3') or (name == 'cu'):
4147
+ elif (name == "cu3") or (name == "cu"):
3653
4148
  self._sim.mcu(
3654
- operation.qubits[0:1],
3655
- operation.qubits[1],
4149
+ [q._index for q in operation.qubits[0:1]],
4150
+ operation.qubits[1]._index,
3656
4151
  float(operation.params[0]),
3657
4152
  float(operation.params[1]),
3658
4153
  float(operation.params[2]),
3659
4154
  )
3660
- elif name == 'cx':
3661
- self._sim.mcx(operation.qubits[0:1], operation.qubits[1])
3662
- elif name == 'cy':
3663
- self._sim.mcy(operation.qubits[0:1], operation.qubits[1])
3664
- elif name == 'cz':
3665
- self._sim.mcz(operation.qubits[0:1], operation.qubits[1])
3666
- elif name == 'ch':
3667
- self._sim.mch(operation.qubits[0:1], operation.qubits[1])
3668
- elif name == 'cp':
4155
+ elif name == "cx":
4156
+ self._sim.mcx(
4157
+ [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4158
+ )
4159
+ elif name == "cy":
4160
+ self._sim.mcy(
4161
+ [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4162
+ )
4163
+ elif name == "cz":
4164
+ self._sim.mcz(
4165
+ [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4166
+ )
4167
+ elif name == "ch":
4168
+ self._sim.mch(
4169
+ [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4170
+ )
4171
+ elif name == "cp":
3669
4172
  self._sim.mcmtrx(
3670
- operation.qubits[0:1],
4173
+ [q._index for q in operation.qubits[0:1]],
3671
4174
  [
3672
4175
  1,
3673
4176
  0,
3674
4177
  0,
3675
- math.cos(float(operation.params[0])) + 1j * math.sin(float(operation.params[0])),
4178
+ math.cos(float(operation.params[0]))
4179
+ + 1j * math.sin(float(operation.params[0])),
3676
4180
  ],
3677
- operation.qubits[1],
4181
+ operation.qubits[1]._index,
3678
4182
  )
3679
- elif name == 'csx':
4183
+ elif name == "csx":
3680
4184
  self._sim.mcmtrx(
3681
- operation.qubits[0:1],
4185
+ [q._index for q in operation.qubits[0:1]],
3682
4186
  [(1 + 1j) / 2, (1 - 1j) / 2, (1 - 1j) / 2, (1 + 1j) / 2],
3683
- operation.qubits[1],
4187
+ operation.qubits[1]._index,
3684
4188
  )
3685
- elif name == 'csxdg':
4189
+ elif name == "csxdg":
3686
4190
  self._sim.mcmtrx(
3687
- operation.qubits[0:1],
4191
+ [q._index for q in operation.qubits[0:1]],
3688
4192
  [(1 - 1j) / 2, (1 + 1j) / 2, (1 + 1j) / 2, (1 - 1j) / 2],
3689
- operation.qubits[1],
3690
- )
3691
- elif name == 'dcx':
3692
- self._sim.mcx(operation.qubits[0:1], operation.qubits[1])
3693
- self._sim.mcx(operation.qubits[1:2], operation.qubits[0])
3694
- elif name == 'ccx':
3695
- self._sim.mcx(operation.qubits[0:2], operation.qubits[2])
3696
- elif name == 'ccy':
3697
- self._sim.mcy(operation.qubits[0:2], operation.qubits[2])
3698
- elif name == 'ccz':
3699
- self._sim.mcz(operation.qubits[0:2], operation.qubits[2])
3700
- elif name == 'mcx':
3701
- self._sim.mcx(operation.qubits[0:-1], operation.qubits[-1])
3702
- elif name == 'mcy':
3703
- self._sim.mcy(operation.qubits[0:-1], operation.qubits[-1])
3704
- elif name == 'mcz':
3705
- self._sim.mcz(operation.qubits[0:-1], operation.qubits[-1])
3706
- elif name == 'swap':
3707
- self._sim.swap(operation.qubits[0], operation.qubits[1])
3708
- elif name == 'iswap':
3709
- self._sim.iswap(operation.qubits[0], operation.qubits[1])
3710
- elif name == 'iswap_dg':
3711
- self._sim.adjiswap(operation.qubits[0], operation.qubits[1])
3712
- elif name == 'cswap':
4193
+ operation.qubits[1]._index,
4194
+ )
4195
+ elif name == "dcx":
4196
+ self._sim.mcx(
4197
+ [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4198
+ )
4199
+ self._sim.mcx(operation.qubits[1:2]._index, operation.qubits[0]._index)
4200
+ elif name == "ccx":
4201
+ self._sim.mcx(
4202
+ [q._index for q in operation.qubits[0:2]], operation.qubits[2]._index
4203
+ )
4204
+ elif name == "ccy":
4205
+ self._sim.mcy(
4206
+ [q._index for q in operation.qubits[0:2]], operation.qubits[2]._index
4207
+ )
4208
+ elif name == "ccz":
4209
+ self._sim.mcz(
4210
+ [q._index for q in operation.qubits[0:2]], operation.qubits[2]._index
4211
+ )
4212
+ elif name == "mcx":
4213
+ self._sim.mcx(
4214
+ [q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index
4215
+ )
4216
+ elif name == "mcy":
4217
+ self._sim.mcy(
4218
+ [q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index
4219
+ )
4220
+ elif name == "mcz":
4221
+ self._sim.mcz(
4222
+ [q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index
4223
+ )
4224
+ elif name == "swap":
4225
+ self._sim.swap(operation.qubits[0]._index, operation.qubits[1]._index)
4226
+ elif name == "iswap":
4227
+ self._sim.iswap(operation.qubits[0]._index, operation.qubits[1]._index)
4228
+ elif name == "iswap_dg":
4229
+ self._sim.adjiswap(operation.qubits[0]._index, operation.qubits[1]._index)
4230
+ elif name == "cswap":
3713
4231
  self._sim.cswap(
3714
- operation.qubits[0:1], operation.qubits[1], operation.qubits[2]
4232
+ [q._index for q in operation.qubits[0:1]],
4233
+ operation.qubits[1]._index,
4234
+ operation.qubits[2]._index,
3715
4235
  )
3716
- elif name == 'mcswap':
4236
+ elif name == "mcswap":
3717
4237
  self._sim.cswap(
3718
- operation.qubits[:-2], operation.qubits[-2], operation.qubits[-1]
4238
+ [q._index for q in operation.qubits[:-2]],
4239
+ operation.qubits[-2]._index,
4240
+ operation.qubits[-1]._index,
3719
4241
  )
3720
- elif name == 'reset':
4242
+ elif name == "reset":
3721
4243
  qubits = operation.qubits
3722
4244
  for qubit in qubits:
3723
- if self._sim.m(qubit):
3724
- self._sim.x(qubit)
3725
- elif name == 'measure':
4245
+ if self._sim.m(qubit._index):
4246
+ self._sim.x(qubit._index)
4247
+ elif name == "measure":
3726
4248
  qubits = operation.qubits
3727
- clbits = operation.memory
4249
+ clbits = operation.clbits
3728
4250
  cregbits = (
3729
4251
  operation.register
3730
- if hasattr(operation, 'register')
4252
+ if hasattr(operation, "register")
3731
4253
  else len(operation.qubits) * [-1]
3732
4254
  )
3733
4255
 
@@ -3737,7 +4259,7 @@ class QrackSimulator:
3737
4259
 
3738
4260
  if not self._sample_measure:
3739
4261
  for index in range(len(qubits)):
3740
- qubit_outcome = self._sim.m(qubits[index])
4262
+ qubit_outcome = self._sim.m(qubits[index]._index)
3741
4263
 
3742
4264
  clbit = clbits[index]
3743
4265
  clmask = 1 << clbit
@@ -3754,30 +4276,30 @@ class QrackSimulator:
3754
4276
  self._classical_register & (~regbit)
3755
4277
  ) | (qubit_outcome << cregbit)
3756
4278
 
3757
- elif name == 'bfunc':
4279
+ elif name == "bfunc":
3758
4280
  mask = int(operation.mask, 16)
3759
4281
  relation = operation.relation
3760
4282
  val = int(operation.val, 16)
3761
4283
 
3762
4284
  cregbit = operation.register
3763
- cmembit = operation.memory if hasattr(operation, 'memory') else None
4285
+ cmembit = operation.memory if hasattr(operation, "memory") else None
3764
4286
 
3765
4287
  compared = (self._classical_register & mask) - val
3766
4288
 
3767
- if relation == '==':
4289
+ if relation == "==":
3768
4290
  outcome = compared == 0
3769
- elif relation == '!=':
4291
+ elif relation == "!=":
3770
4292
  outcome = compared != 0
3771
- elif relation == '<':
4293
+ elif relation == "<":
3772
4294
  outcome = compared < 0
3773
- elif relation == '<=':
4295
+ elif relation == "<=":
3774
4296
  outcome = compared <= 0
3775
- elif relation == '>':
4297
+ elif relation == ">":
3776
4298
  outcome = compared > 0
3777
- elif relation == '>=':
4299
+ elif relation == ">=":
3778
4300
  outcome = compared >= 0
3779
4301
  else:
3780
- raise QrackError('Invalid boolean function relation.')
4302
+ raise QrackError("Invalid boolean function relation.")
3781
4303
 
3782
4304
  # Store outcome in register and optionally memory slot
3783
4305
  regbit = 1 << cregbit
@@ -3811,28 +4333,30 @@ class QrackSimulator:
3811
4333
  measure_clbit = [clbit for clbit in sample_clbits]
3812
4334
 
3813
4335
  # Sample and convert to bit-strings
3814
- data = []
3815
4336
  if num_samples == 1:
3816
4337
  sample = self._sim.m_all()
3817
4338
  result = 0
3818
4339
  for index in range(len(measure_qubit)):
3819
- qubit = measure_qubit[index]
4340
+ qubit = measure_qubit[index]._index
3820
4341
  qubit_outcome = (sample >> qubit) & 1
3821
4342
  result |= qubit_outcome << index
3822
4343
  measure_results = [result]
3823
4344
  else:
3824
- measure_results = self._sim.measure_shots(measure_qubit, num_samples)
4345
+ measure_results = self._sim.measure_shots(
4346
+ [q._index for q in measure_qubit], num_samples
4347
+ )
3825
4348
 
4349
+ data = []
3826
4350
  for sample in measure_results:
3827
4351
  for index in range(len(measure_qubit)):
3828
4352
  qubit_outcome = (sample >> index) & 1
3829
- clbit = measure_clbit[index]
4353
+ clbit = measure_clbit[index]._index
3830
4354
  clmask = 1 << clbit
3831
4355
  self._classical_memory = (self._classical_memory & (~clmask)) | (
3832
4356
  qubit_outcome << clbit
3833
4357
  )
3834
4358
 
3835
- data.append(hex(int(bin(self._classical_memory)[2:], 2)))
4359
+ data.append(bin(self._classical_memory)[2:].zfill(self.num_qubits()))
3836
4360
 
3837
4361
  return data
3838
4362
 
@@ -3842,12 +4366,9 @@ class QrackSimulator:
3842
4366
  "Before trying to run_qiskit_circuit() with QrackSimulator, you must install Qiskit!"
3843
4367
  )
3844
4368
 
3845
- if isinstance(experiment, QuantumCircuit):
3846
- experiment = convert_qiskit_circuit_to_qasm_experiment(experiment)
3847
-
3848
4369
  instructions = []
3849
- if isinstance(experiment, QasmQobjExperiment):
3850
- instructions = experiment.instructions
4370
+ if isinstance(experiment, QuantumCircuit):
4371
+ instructions = experiment.data
3851
4372
  else:
3852
4373
  raise RuntimeError('Unrecognized "run_input" argument specified for run().')
3853
4374
 
@@ -3857,7 +4378,6 @@ class QrackSimulator:
3857
4378
  self._sample_cregbits = []
3858
4379
  self._sample_measure = True
3859
4380
  _data = []
3860
- shotsPerLoop = self._shots
3861
4381
  shotLoopMax = 1
3862
4382
 
3863
4383
  is_initializing = True
@@ -3866,21 +4386,21 @@ class QrackSimulator:
3866
4386
  for opcount in range(len(instructions)):
3867
4387
  operation = instructions[opcount]
3868
4388
 
3869
- if operation.name == 'id' or operation.name == 'barrier':
4389
+ if operation.name == "id" or operation.name == "barrier":
3870
4390
  continue
3871
4391
 
3872
4392
  if is_initializing and (
3873
- (operation.name == 'measure') or (operation.name == 'reset')
4393
+ (operation.name == "measure") or (operation.name == "reset")
3874
4394
  ):
3875
4395
  continue
3876
4396
 
3877
4397
  is_initializing = False
3878
4398
 
3879
- if (operation.name == 'measure') or (operation.name == 'reset'):
4399
+ if (operation.name == "measure") or (operation.name == "reset"):
3880
4400
  if boundary_start == -1:
3881
4401
  boundary_start = opcount
3882
4402
 
3883
- if (boundary_start != -1) and (operation.name != 'measure'):
4403
+ if (boundary_start != -1) and (operation.name != "measure"):
3884
4404
  shotsPerLoop = 1
3885
4405
  shotLoopMax = self._shots
3886
4406
  self._sample_measure = False
@@ -3923,7 +4443,7 @@ class QrackSimulator:
3923
4443
  self._apply_op(operation)
3924
4444
 
3925
4445
  if not self._sample_measure and (len(self._sample_qubits) > 0):
3926
- _data += [hex(int(bin(self._classical_memory)[2:], 2))]
4446
+ _data += [bin(self._classical_memory)[2:].zfill(self.num_qubits())]
3927
4447
  self._sample_qubits = []
3928
4448
  self._sample_clbits = []
3929
4449
  self._sample_cregbits = []
@@ -3936,3 +4456,43 @@ class QrackSimulator:
3936
4456
  del self._sim
3937
4457
 
3938
4458
  return _data
4459
+
4460
+ def get_qiskit_basis_gates():
4461
+ return [
4462
+ "id",
4463
+ "u",
4464
+ "u1",
4465
+ "u2",
4466
+ "u3",
4467
+ "r",
4468
+ "rx",
4469
+ "ry",
4470
+ "rz",
4471
+ "h",
4472
+ "x",
4473
+ "y",
4474
+ "z",
4475
+ "s",
4476
+ "sdg",
4477
+ "sx",
4478
+ "sxdg",
4479
+ "p",
4480
+ "t",
4481
+ "tdg",
4482
+ "cu",
4483
+ "cu1",
4484
+ "cu3",
4485
+ "cx",
4486
+ "cy",
4487
+ "cz",
4488
+ "ch",
4489
+ "cp",
4490
+ "csx",
4491
+ "ccx",
4492
+ "ccz",
4493
+ "swap",
4494
+ "iswap",
4495
+ "cswap",
4496
+ "reset",
4497
+ "measure",
4498
+ ]