pyqrack-cpu-complex128 2.22.3__tar.gz → 2.23.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyqrack_cpu_complex128-2.22.3/pyqrack_cpu_complex128.egg-info → pyqrack_cpu_complex128-2.23.0}/PKG-INFO +1 -1
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_ace_backend.py +371 -21
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_simulator.py +6 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0/pyqrack_cpu_complex128.egg-info}/PKG-INFO +1 -1
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/setup.py +1 -1
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/LICENSE +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/MANIFEST.in +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/Makefile +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/README.md +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyproject.toml +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/__init__.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/pauli.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack_cpu_complex128.egg-info/SOURCES.txt +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack_cpu_complex128.egg-info/dependency_links.txt +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack_cpu_complex128.egg-info/not-zip-safe +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack_cpu_complex128.egg-info/requires.txt +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack_cpu_complex128.egg-info/top_level.txt +0 -0
- {pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/setup.cfg +0 -0
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_ace_backend.py
RENAMED
|
@@ -18,6 +18,17 @@ try:
|
|
|
18
18
|
from qiskit.circuit.quantumcircuit import QuantumCircuit
|
|
19
19
|
from qiskit.compiler import transpile
|
|
20
20
|
from qiskit.quantum_info.operators.symplectic.clifford import Clifford
|
|
21
|
+
from qiskit.transpiler import Target, InstructionProperties
|
|
22
|
+
from qiskit.circuit import Parameter
|
|
23
|
+
from qiskit.circuit.library import (
|
|
24
|
+
IGate, UGate, U3Gate, U2Gate, U1Gate,
|
|
25
|
+
XGate, YGate, ZGate, HGate,
|
|
26
|
+
RXGate, RYGate, RZGate,
|
|
27
|
+
SGate, SdgGate, TGate, TdgGate,
|
|
28
|
+
CXGate, CYGate, CZGate, SwapGate, iSwapGate,
|
|
29
|
+
CCXGate, CCZGate, CSwapGate,
|
|
30
|
+
Measure, Reset,
|
|
31
|
+
)
|
|
21
32
|
except ImportError:
|
|
22
33
|
_IS_QISKIT_AVAILABLE = False
|
|
23
34
|
|
|
@@ -803,7 +814,6 @@ class QrackAceBackend:
|
|
|
803
814
|
|
|
804
815
|
sim.mtrx([m00, m01, m10, m11], q)
|
|
805
816
|
|
|
806
|
-
@staticmethod
|
|
807
817
|
def _get_lhv_bloch_angles(sim):
|
|
808
818
|
z = sim.bloch[2]
|
|
809
819
|
x = sim.bloch[0]
|
|
@@ -812,7 +822,6 @@ class QrackAceBackend:
|
|
|
812
822
|
azimuth = math.atan2(y, x)
|
|
813
823
|
return azimuth, inclination
|
|
814
824
|
|
|
815
|
-
@staticmethod
|
|
816
825
|
def _rotate_lhv_to_bloch(sim, delta_azimuth, delta_inclination):
|
|
817
826
|
cosA = math.cos(delta_azimuth)
|
|
818
827
|
sinA = math.sin(delta_azimuth)
|
|
@@ -1713,16 +1722,68 @@ class QrackAceBackend:
|
|
|
1713
1722
|
else:
|
|
1714
1723
|
bits = [self.force_m(a, False) for a in ancs]
|
|
1715
1724
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1725
|
+
# Map each participant to the SPECIFIC physical replica
|
|
1726
|
+
# of hq1 its own syndrome actually covers -- anc1 is
|
|
1727
|
+
# patch A (hq1[0]), anc1c is always the crossbar
|
|
1728
|
+
# (hq1[2], by construction), and anc1b is whichever of
|
|
1729
|
+
# hq1's replicas lives in hq2's own patch (found by sim
|
|
1730
|
+
# id rather than assumed to be a fixed index, since
|
|
1731
|
+
# corner qubits have patch replicas at indices 1, 3, and
|
|
1732
|
+
# 4). This can legitimately be absent: hq1 and hq2 may
|
|
1733
|
+
# share no common patch at all for an arbitrary (non-
|
|
1734
|
+
# adjacent) logical coupling -- confirmed directly
|
|
1735
|
+
# (hq1=[(2,1),(3,0),(4,2)] coupled to hq2=[(1,1)], no
|
|
1736
|
+
# overlap) -- in which case there is no single physical
|
|
1737
|
+
# replica to target for that participant specifically.
|
|
1738
|
+
replicas = [hq1[0]]
|
|
1739
|
+
if anc1b is not None:
|
|
1740
|
+
replicas.append(next((r for r in hq1 if r[0] == hq2[0][0]), None))
|
|
1741
|
+
if anc1c is not None:
|
|
1742
|
+
replicas.append(hq1[2])
|
|
1743
|
+
|
|
1744
|
+
# Majority vote decides whether the LOGICAL value itself
|
|
1745
|
+
# genuinely flipped (updating the LHV shadow, same as the
|
|
1746
|
+
# transversal self.x(lq1) used to do unconditionally);
|
|
1747
|
+
# any minority participant is still individually
|
|
1748
|
+
# corrected below regardless, since ITS replica is wrong
|
|
1749
|
+
# either way -- it just isn't treated as evidence the
|
|
1750
|
+
# logical value moved, the same way a repetition code's
|
|
1751
|
+
# decoded value doesn't change just because one physical
|
|
1752
|
+
# bit needed fixing.
|
|
1753
|
+
n_flipped = sum(1 for b in bits if b)
|
|
1754
|
+
if (n_flipped << 1) > len(ancs):
|
|
1755
|
+
is_flipped = True
|
|
1756
|
+
lhv = self._lhv.get(lq1)
|
|
1757
|
+
if lhv is not None:
|
|
1758
|
+
lhv.x()
|
|
1759
|
+
|
|
1718
1760
|
for i, a in enumerate(ancs):
|
|
1719
1761
|
if bits[i]:
|
|
1720
1762
|
self.x(a)
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1763
|
+
# Targeted, single-replica correction rather than
|
|
1764
|
+
# a transversal self.x(lq1): these replicas are a
|
|
1765
|
+
# pseudo-repetition code for lq1, and a
|
|
1766
|
+
# participant flipping means THAT PHYSICAL COPY
|
|
1767
|
+
# is the one in error, not the logical value as a
|
|
1768
|
+
# whole. Flipping every replica transversally is
|
|
1769
|
+
# only ever correct by coincidence when all
|
|
1770
|
+
# participants agree -- on a genuine minority
|
|
1771
|
+
# disagreement it would "fix" the actual error
|
|
1772
|
+
# while simultaneously breaking the (correct)
|
|
1773
|
+
# majority replicas that were never wrong. Fixing
|
|
1774
|
+
# per-replica is correct in the agreeing case too
|
|
1775
|
+
# (each flipped participant's own replica still
|
|
1776
|
+
# gets corrected), so this applies unconditionally,
|
|
1777
|
+
# not just when a disagreement is detected. Falls
|
|
1778
|
+
# back to the transversal correction only when no
|
|
1779
|
+
# single hq1 replica corresponds to this
|
|
1780
|
+
# participant at all (see the no-overlap case
|
|
1781
|
+
# above).
|
|
1782
|
+
b = replicas[i]
|
|
1783
|
+
if b is not None:
|
|
1784
|
+
self.sim[b[0]].x(b[1])
|
|
1785
|
+
else:
|
|
1786
|
+
self.x(lq1)
|
|
1726
1787
|
else:
|
|
1727
1788
|
anc_sim, anc_idx = self._qubits[anc1][0]
|
|
1728
1789
|
p = self.sim[anc_sim].prob(anc_idx)
|
|
@@ -1918,46 +1979,174 @@ class QrackAceBackend:
|
|
|
1918
1979
|
self._cpauli(lq1, lq2, True, Pauli.PauliZ)
|
|
1919
1980
|
|
|
1920
1981
|
def mcx(self, lq1, lq2):
|
|
1921
|
-
if len(lq1)
|
|
1982
|
+
if len(lq1) == 1:
|
|
1983
|
+
self.cx(lq1[0], lq2)
|
|
1984
|
+
return
|
|
1985
|
+
|
|
1986
|
+
hq = self._unpack(lq2)
|
|
1987
|
+
if len(hq) > 1:
|
|
1922
1988
|
raise RuntimeError(
|
|
1923
1989
|
"QrackAceBackend.mcx() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1924
1990
|
)
|
|
1925
|
-
|
|
1991
|
+
s = hq[0][0]
|
|
1992
|
+
t = hq[0][1]
|
|
1993
|
+
c = []
|
|
1994
|
+
for l in lq1:
|
|
1995
|
+
hq = self._unpack(l)
|
|
1996
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
1997
|
+
raise RuntimeError(
|
|
1998
|
+
"QrackAceBackend.mcx() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1999
|
+
)
|
|
2000
|
+
c.append(hq[0][1])
|
|
2001
|
+
|
|
2002
|
+
# We happen to be able to accommodate this:
|
|
2003
|
+
self.sim[s].mcx(c, t)
|
|
1926
2004
|
|
|
1927
2005
|
def mcy(self, lq1, lq2):
|
|
1928
|
-
if len(lq1)
|
|
2006
|
+
if len(lq1) == 1:
|
|
2007
|
+
self.cy(lq1[0], lq2)
|
|
2008
|
+
return
|
|
2009
|
+
|
|
2010
|
+
hq = self._unpack(lq2)
|
|
2011
|
+
if len(hq) > 1:
|
|
1929
2012
|
raise RuntimeError(
|
|
1930
2013
|
"QrackAceBackend.mcy() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1931
2014
|
)
|
|
1932
|
-
|
|
2015
|
+
s = hq[0][0]
|
|
2016
|
+
t = hq[0][1]
|
|
2017
|
+
c = []
|
|
2018
|
+
for l in lq1:
|
|
2019
|
+
hq = self._unpack(l)
|
|
2020
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2021
|
+
raise RuntimeError(
|
|
2022
|
+
"QrackAceBackend.mcy() is provided for syntax convenience and only supports 1 control qubit!"
|
|
2023
|
+
)
|
|
2024
|
+
c.append(hq[0][1])
|
|
2025
|
+
|
|
2026
|
+
# We happen to be able to accommodate this:
|
|
2027
|
+
self.sim[s].mcy(c, t)
|
|
1933
2028
|
|
|
1934
2029
|
def mcz(self, lq1, lq2):
|
|
1935
|
-
if len(lq1)
|
|
2030
|
+
if len(lq1) == 1:
|
|
2031
|
+
self.cz(lq1[0], lq2)
|
|
2032
|
+
return
|
|
2033
|
+
|
|
2034
|
+
hq = self._unpack(lq2)
|
|
2035
|
+
if len(hq) > 1:
|
|
1936
2036
|
raise RuntimeError(
|
|
1937
2037
|
"QrackAceBackend.mcz() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1938
2038
|
)
|
|
1939
|
-
|
|
2039
|
+
s = hq[0][0]
|
|
2040
|
+
t = hq[0][1]
|
|
2041
|
+
c = []
|
|
2042
|
+
for l in lq1:
|
|
2043
|
+
hq = self._unpack(l)
|
|
2044
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2045
|
+
raise RuntimeError(
|
|
2046
|
+
"QrackAceBackend.mcz() is provided for syntax convenience and only supports 1 control qubit!"
|
|
2047
|
+
)
|
|
2048
|
+
c.append(hq[0][1])
|
|
2049
|
+
|
|
2050
|
+
# We happen to be able to accommodate this:
|
|
2051
|
+
self.sim[s].mcz(c, t)
|
|
1940
2052
|
|
|
1941
2053
|
def macx(self, lq1, lq2):
|
|
1942
|
-
if len(lq1)
|
|
2054
|
+
if len(lq1) == 1:
|
|
2055
|
+
self.acx(lq1[0], lq2)
|
|
2056
|
+
return
|
|
2057
|
+
|
|
2058
|
+
hq = self._unpack(lq2)
|
|
2059
|
+
if len(hq) > 1:
|
|
1943
2060
|
raise RuntimeError(
|
|
1944
2061
|
"QrackAceBackend.macx() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1945
2062
|
)
|
|
1946
|
-
|
|
2063
|
+
s = hq[0][0]
|
|
2064
|
+
t = hq[0][1]
|
|
2065
|
+
c = []
|
|
2066
|
+
for l in lq1:
|
|
2067
|
+
hq = self._unpack(l)
|
|
2068
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2069
|
+
raise RuntimeError(
|
|
2070
|
+
"QrackAceBackend.macx() is provided for syntax convenience and only supports 1 control qubit!"
|
|
2071
|
+
)
|
|
2072
|
+
c.append(hq[0][1])
|
|
2073
|
+
|
|
2074
|
+
# We happen to be able to accommodate this:
|
|
2075
|
+
self.sim[s].macx(c, t)
|
|
1947
2076
|
|
|
1948
2077
|
def macy(self, lq1, lq2):
|
|
1949
|
-
if len(lq1)
|
|
2078
|
+
if len(lq1) == 1:
|
|
2079
|
+
self.acy(lq1[0], lq2)
|
|
2080
|
+
return
|
|
2081
|
+
|
|
2082
|
+
hq = self._unpack(lq2)
|
|
2083
|
+
if len(hq) > 1:
|
|
1950
2084
|
raise RuntimeError(
|
|
1951
2085
|
"QrackAceBackend.macy() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1952
2086
|
)
|
|
1953
|
-
|
|
2087
|
+
s = hq[0][0]
|
|
2088
|
+
t = hq[0][1]
|
|
2089
|
+
c = []
|
|
2090
|
+
for l in lq1:
|
|
2091
|
+
hq = self._unpack(l)
|
|
2092
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2093
|
+
raise RuntimeError(
|
|
2094
|
+
"QrackAceBackend.macy() is provided for syntax convenience and only supports 1 control qubit!"
|
|
2095
|
+
)
|
|
2096
|
+
c.append(hq[0][1])
|
|
2097
|
+
|
|
2098
|
+
# We happen to be able to accommodate this:
|
|
2099
|
+
self.sim[s].macy(c, t)
|
|
1954
2100
|
|
|
1955
2101
|
def macz(self, lq1, lq2):
|
|
1956
|
-
if len(lq1)
|
|
2102
|
+
if len(lq1) == 1:
|
|
2103
|
+
self.acz(lq1[0], lq2)
|
|
2104
|
+
return
|
|
2105
|
+
|
|
2106
|
+
hq = self._unpack(lq2)
|
|
2107
|
+
if len(hq) > 1:
|
|
1957
2108
|
raise RuntimeError(
|
|
1958
2109
|
"QrackAceBackend.macz() is provided for syntax convenience and only supports 1 control qubit!"
|
|
1959
2110
|
)
|
|
1960
|
-
|
|
2111
|
+
s = hq[0][0]
|
|
2112
|
+
t = hq[0][1]
|
|
2113
|
+
c = []
|
|
2114
|
+
for l in lq1:
|
|
2115
|
+
hq = self._unpack(l)
|
|
2116
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2117
|
+
raise RuntimeError(
|
|
2118
|
+
"QrackAceBackend.macz() is provided for syntax convenience and only supports 1 control qubit!"
|
|
2119
|
+
)
|
|
2120
|
+
c.append(hq[0][1])
|
|
2121
|
+
|
|
2122
|
+
# We happen to be able to accommodate this:
|
|
2123
|
+
self.sim[s].macz(c, t)
|
|
2124
|
+
|
|
2125
|
+
def cswap(self, lq1, lq2, lq3):
|
|
2126
|
+
hq = self._unpack(lq2)
|
|
2127
|
+
if len(hq) > 1:
|
|
2128
|
+
raise RuntimeError(
|
|
2129
|
+
"QrackAceBackend.cswap() is provided for syntax convenience and only supports intra-patch!"
|
|
2130
|
+
)
|
|
2131
|
+
s = hq[0][0]
|
|
2132
|
+
t1 = hq[0][1]
|
|
2133
|
+
hq = self._unpack(lq3)
|
|
2134
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2135
|
+
raise RuntimeError(
|
|
2136
|
+
"QrackAceBackend.cswap() is provided for syntax convenience and only supports intra-patch!"
|
|
2137
|
+
)
|
|
2138
|
+
t2 = hq[0][1]
|
|
2139
|
+
c = []
|
|
2140
|
+
for l in lq1:
|
|
2141
|
+
hq = self._unpack(l)
|
|
2142
|
+
if (len(hq) > 1) or (s != hq[0][0]):
|
|
2143
|
+
raise RuntimeError(
|
|
2144
|
+
"QrackAceBackend.cswap() is provided for syntax convenience and only supports intra-patch!"
|
|
2145
|
+
)
|
|
2146
|
+
c.append(hq[0][1])
|
|
2147
|
+
|
|
2148
|
+
# We happen to be able to accommodate this:
|
|
2149
|
+
self.sim[s].cswap(c, t1, t2)
|
|
1961
2150
|
|
|
1962
2151
|
def swap(self, lq1, lq2):
|
|
1963
2152
|
hq1 = self._unpack(lq1)
|
|
@@ -2383,11 +2572,23 @@ class QrackAceBackend:
|
|
|
2383
2572
|
self._sim.cy(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
2384
2573
|
elif name == "cz":
|
|
2385
2574
|
self._sim.cz(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
2575
|
+
elif name == "ccx":
|
|
2576
|
+
self._sim.mcx([operation.qubits[0]._index, operation.qubits[1]._index], operation.qubits[2]._index)
|
|
2577
|
+
elif name == "ccz":
|
|
2578
|
+
self._sim.mcz([operation.qubits[0]._index, operation.qubits[1]._index], operation.qubits[2]._index)
|
|
2579
|
+
elif name == "mcx":
|
|
2580
|
+
self._sim.mcx([q._index for q in operation.qubits[:-1]], operation.qubits[-1]._index)
|
|
2581
|
+
elif name == "mcy":
|
|
2582
|
+
self._sim.mcy([q._index for q in operation.qubits[:-1]], operation.qubits[-1]._index)
|
|
2583
|
+
elif name == "mcz":
|
|
2584
|
+
self._sim.mcz([q._index for q in operation.qubits[:-1]], operation.qubits[-1]._index)
|
|
2386
2585
|
elif name == "dcx":
|
|
2387
2586
|
self._sim.mcx(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
2388
2587
|
self._sim.mcx(operation.qubits[1]._index, operation.qubits[0]._index)
|
|
2389
2588
|
elif name == "swap":
|
|
2390
2589
|
self._sim.swap(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
2590
|
+
elif name == "cswap":
|
|
2591
|
+
self._sim.cswap([q._index for q in operation.qubits[:-2]], operation.qubits[-2]._index, operation.qubits[-1]._index)
|
|
2391
2592
|
elif name == "iswap":
|
|
2392
2593
|
self._sim.iswap(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
2393
2594
|
elif name == "iswap_dg":
|
|
@@ -2627,7 +2828,13 @@ class QrackAceBackend:
|
|
|
2627
2828
|
"cx",
|
|
2628
2829
|
"cy",
|
|
2629
2830
|
"cz",
|
|
2831
|
+
"ccx",
|
|
2832
|
+
"ccz",
|
|
2833
|
+
"mcx",
|
|
2834
|
+
"mcy",
|
|
2835
|
+
"mcz",
|
|
2630
2836
|
"swap",
|
|
2837
|
+
"cswap",
|
|
2631
2838
|
"iswap",
|
|
2632
2839
|
"reset",
|
|
2633
2840
|
"measure",
|
|
@@ -2670,6 +2877,149 @@ class QrackAceBackend:
|
|
|
2670
2877
|
|
|
2671
2878
|
return self._coupling_map
|
|
2672
2879
|
|
|
2880
|
+
# Moved here from AceQasmSimulator (qiskit-qrack-provider), so a
|
|
2881
|
+
# QrackAceBackend can be transpiled against directly with plain Qiskit,
|
|
2882
|
+
# without needing the separate Qiskit BackendV2 wrapper at all. The
|
|
2883
|
+
# Qiskit backend now calls this instead of duplicating the logic.
|
|
2884
|
+
#
|
|
2885
|
+
# One correction made in the move: the original 1-qubit "boundary
|
|
2886
|
+
# penalty hint" set was approximated from get_logical_coupling_map()
|
|
2887
|
+
# (any qubit appearing in ANY coupling pair) -- but that map includes
|
|
2888
|
+
# ordinary bulk-bulk pairs within the same patch too, so it actually
|
|
2889
|
+
# flagged nearly every qubit as "boundary." Here, with direct access to
|
|
2890
|
+
# self._unpack(), the real criterion (len(self._unpack(q)) > 1) is used
|
|
2891
|
+
# instead.
|
|
2892
|
+
def get_target(self, description=None, error_2q=0.5):
|
|
2893
|
+
"""Build a qiskit Target describing this backend's gate set and
|
|
2894
|
+
connectivity, so it can be used with qiskit.transpile() directly --
|
|
2895
|
+
with or without the separate Qiskit BackendV2 wrapper.
|
|
2896
|
+
|
|
2897
|
+
IMPORTANT, verified directly rather than assumed: qiskit's
|
|
2898
|
+
transpile() does NOT reliably enforce the qubit-tuple-level
|
|
2899
|
+
InstructionProperties restrictions declared here for 3+-qubit gates
|
|
2900
|
+
(ccx/ccz/cswap), at any optimization_level that doesn't outright
|
|
2901
|
+
crash. Confirmed: the default optimization_level raises
|
|
2902
|
+
TranspilerError("target contains multi-qubit operations") on ANY
|
|
2903
|
+
Target containing 3+-qubit gates, valid placement or not (Qiskit's
|
|
2904
|
+
default SabreLayout routing pass doesn't support them at all).
|
|
2905
|
+
optimization_level=0 or 1 avoid that crash, but ALSO transpiled a
|
|
2906
|
+
deliberately-invalid (boundary-qubit) ccx placement successfully in
|
|
2907
|
+
testing -- i.e., they don't validate 3-qubit placements against the
|
|
2908
|
+
target either, they just don't crash on them.
|
|
2909
|
+
The Target's 3-qubit entries are still meaningful as a description
|
|
2910
|
+
of what's actually valid (useful for introspection, or for a
|
|
2911
|
+
transpiler pass that does check placements properly), but they are
|
|
2912
|
+
NOT currently a transpile-time safety guarantee the way the 2-qubit
|
|
2913
|
+
coupling map is. The actual, reliable enforcement is the runtime
|
|
2914
|
+
RuntimeError in mcx()/mcy()/mcz()/cswap() themselves, which fire
|
|
2915
|
+
correctly regardless of what the transpiler allowed through.
|
|
2916
|
+
"""
|
|
2917
|
+
if not _IS_QISKIT_AVAILABLE:
|
|
2918
|
+
raise RuntimeError(
|
|
2919
|
+
"QrackAceBackend.get_target() requires Qiskit to be installed!"
|
|
2920
|
+
)
|
|
2921
|
+
|
|
2922
|
+
n = self.num_qubits()
|
|
2923
|
+
tgt = Target(num_qubits=n, description=description)
|
|
2924
|
+
|
|
2925
|
+
# Parameters for parameterised gates
|
|
2926
|
+
theta = Parameter("theta")
|
|
2927
|
+
phi = Parameter("phi")
|
|
2928
|
+
lam = Parameter("lam")
|
|
2929
|
+
|
|
2930
|
+
# --- single-qubit gates: all qubits ---
|
|
2931
|
+
boundary = {q for q in range(n) if len(self._unpack(q)) > 1}
|
|
2932
|
+
|
|
2933
|
+
def _1q_props():
|
|
2934
|
+
return {
|
|
2935
|
+
(q,): InstructionProperties()
|
|
2936
|
+
for q in range(n)
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
tgt.add_instruction(IGate(), _1q_props())
|
|
2940
|
+
tgt.add_instruction(UGate(theta, phi, lam), _1q_props())
|
|
2941
|
+
tgt.add_instruction(U3Gate(theta, phi, lam), _1q_props())
|
|
2942
|
+
tgt.add_instruction(U2Gate(phi, lam), _1q_props())
|
|
2943
|
+
tgt.add_instruction(U1Gate(lam), _1q_props())
|
|
2944
|
+
tgt.add_instruction(RXGate(theta), _1q_props())
|
|
2945
|
+
tgt.add_instruction(RYGate(theta), _1q_props())
|
|
2946
|
+
tgt.add_instruction(RZGate(theta), _1q_props())
|
|
2947
|
+
tgt.add_instruction(HGate(), _1q_props())
|
|
2948
|
+
tgt.add_instruction(XGate(), _1q_props())
|
|
2949
|
+
tgt.add_instruction(YGate(), _1q_props())
|
|
2950
|
+
tgt.add_instruction(ZGate(), _1q_props())
|
|
2951
|
+
tgt.add_instruction(SGate(), _1q_props())
|
|
2952
|
+
tgt.add_instruction(SdgGate(), _1q_props())
|
|
2953
|
+
tgt.add_instruction(TGate(), _1q_props())
|
|
2954
|
+
tgt.add_instruction(TdgGate(), _1q_props())
|
|
2955
|
+
|
|
2956
|
+
# --- two-qubit gates: coupled pairs (includes boundary-crossing
|
|
2957
|
+
# pairs -- those are supported, via the noisy-coupler mechanism) ---
|
|
2958
|
+
bulk = []
|
|
2959
|
+
for q in range(n):
|
|
2960
|
+
hq = self._unpack(q)
|
|
2961
|
+
if len(hq) == 1:
|
|
2962
|
+
bulk.append(q)
|
|
2963
|
+
coupling_map = self.get_logical_coupling_map()
|
|
2964
|
+
if coupling_map:
|
|
2965
|
+
pair_props = {
|
|
2966
|
+
(a, b): InstructionProperties() if (a in bulk) and (b in bulk) else InstructionProperties(error=error_2q) for a, b in coupling_map
|
|
2967
|
+
}
|
|
2968
|
+
else:
|
|
2969
|
+
pair_props = {
|
|
2970
|
+
(a, b): InstructionProperties()
|
|
2971
|
+
for a in range(n)
|
|
2972
|
+
for b in range(n)
|
|
2973
|
+
if a != b
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
tgt.add_instruction(CXGate(), pair_props)
|
|
2977
|
+
tgt.add_instruction(CYGate(), pair_props)
|
|
2978
|
+
tgt.add_instruction(CZGate(), pair_props)
|
|
2979
|
+
tgt.add_instruction(SwapGate(), pair_props)
|
|
2980
|
+
tgt.add_instruction(iSwapGate(), pair_props)
|
|
2981
|
+
|
|
2982
|
+
# --- 3-qubit gates: intra-patch, all-bulk triples only ---
|
|
2983
|
+
# ccx/ccz/cswap (and, by extension, mcx/mcy/mcz beyond a single
|
|
2984
|
+
# control) are only valid when every involved qubit is bulk and
|
|
2985
|
+
# shares the same patch -- see mcx()/cswap() above, which raise
|
|
2986
|
+
# otherwise.
|
|
2987
|
+
#
|
|
2988
|
+
# CCXGate/CCZGate and CSwapGate do NOT share a qubit-ordering
|
|
2989
|
+
# convention (verified directly against their gate definitions):
|
|
2990
|
+
# CCXGate puts its target on qubit index 2, with 0-1 as the two
|
|
2991
|
+
# (interchangeable) controls; CSwapGate puts its control on qubit
|
|
2992
|
+
# index 0, with 1-2 as the (interchangeable) swapped pair. They
|
|
2993
|
+
# need separately-ordered InstructionProperties dicts.
|
|
2994
|
+
# bulk_by_patch = {}
|
|
2995
|
+
# for q in range(n):
|
|
2996
|
+
# hq = self._unpack(q)
|
|
2997
|
+
# if len(hq) == 1:
|
|
2998
|
+
# bulk_by_patch.setdefault(hq[0][0], []).append(q)
|
|
2999
|
+
#
|
|
3000
|
+
# ccx_props = {}
|
|
3001
|
+
# cswap_props = {}
|
|
3002
|
+
# for qubits_here in bulk_by_patch.values():
|
|
3003
|
+
# for pivot in qubits_here:
|
|
3004
|
+
# others = [q for q in qubits_here if q != pivot]
|
|
3005
|
+
# for i in range(len(others)):
|
|
3006
|
+
# for j in range(len(others)):
|
|
3007
|
+
# if i == j:
|
|
3008
|
+
# continue
|
|
3009
|
+
# ccx_props[(others[i], others[j], pivot)] = InstructionProperties()
|
|
3010
|
+
# cswap_props[(pivot, others[i], others[j])] = InstructionProperties()
|
|
3011
|
+
# if ccx_props:
|
|
3012
|
+
# tgt.add_instruction(CCXGate(), ccx_props)
|
|
3013
|
+
# tgt.add_instruction(CCZGate(), ccx_props)
|
|
3014
|
+
# if cswap_props:
|
|
3015
|
+
# tgt.add_instruction(CSwapGate(), cswap_props)
|
|
3016
|
+
|
|
3017
|
+
# --- measure / reset: all qubits ---
|
|
3018
|
+
tgt.add_instruction(Measure(), {(q,): InstructionProperties() for q in range(n)})
|
|
3019
|
+
tgt.add_instruction(Reset(), {(q,): InstructionProperties() for q in range(n)})
|
|
3020
|
+
|
|
3021
|
+
return tgt
|
|
3022
|
+
|
|
2673
3023
|
# Designed by Dan, Elara (ChatGPT), and (Anthropic) Claude:
|
|
2674
3024
|
def create_noise_model(self, x=0.5, y=0.5):
|
|
2675
3025
|
if not _IS_QISKIT_AER_AVAILABLE:
|
|
@@ -4311,6 +4311,8 @@ class QrackSimulator:
|
|
|
4311
4311
|
self._sim.mcz([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
|
|
4312
4312
|
elif name == "swap":
|
|
4313
4313
|
self._sim.swap(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
4314
|
+
elif name == "cswap":
|
|
4315
|
+
self._sim.cswap([q._index for q in operation.qubits[:-2]], operation.qubits[-2]._index, operation.qubits[-1]._index)
|
|
4314
4316
|
elif name == "iswap":
|
|
4315
4317
|
self._sim.iswap(operation.qubits[0]._index, operation.qubits[1]._index)
|
|
4316
4318
|
elif name == "iswap_dg":
|
|
@@ -4568,7 +4570,11 @@ class QrackSimulator:
|
|
|
4568
4570
|
"csx",
|
|
4569
4571
|
"ccx",
|
|
4570
4572
|
"ccz",
|
|
4573
|
+
"mcx",
|
|
4574
|
+
"mcy",
|
|
4575
|
+
"mcz",
|
|
4571
4576
|
"swap",
|
|
4577
|
+
"cswap",
|
|
4572
4578
|
"iswap",
|
|
4573
4579
|
"cswap",
|
|
4574
4580
|
"reset",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/neuron_activation_fn.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_neuron_torch_layer.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_system/__init__.py
RENAMED
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/qrack_system/qrack_system.py
RENAMED
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/quimb_circuit_type.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/stats/load_quantized_data.py
RENAMED
|
File without changes
|
{pyqrack_cpu_complex128-2.22.3 → pyqrack_cpu_complex128-2.23.0}/pyqrack/stats/quantize_by_range.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|