pyqrack-cpu-complex128 2.22.4__tar.gz → 2.24.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.
Files changed (30) hide show
  1. {pyqrack_cpu_complex128-2.22.4/pyqrack_cpu_complex128.egg-info → pyqrack_cpu_complex128-2.24.0}/PKG-INFO +1 -1
  2. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_ace_backend.py +309 -15
  3. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_simulator.py +3 -0
  4. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0/pyqrack_cpu_complex128.egg-info}/PKG-INFO +1 -1
  5. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/setup.py +1 -1
  6. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/LICENSE +0 -0
  7. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/MANIFEST.in +0 -0
  8. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/Makefile +0 -0
  9. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/README.md +0 -0
  10. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyproject.toml +0 -0
  11. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/__init__.py +0 -0
  12. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/neuron_activation_fn.py +0 -0
  13. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/pauli.py +0 -0
  14. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_circuit.py +0 -0
  15. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
  16. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_neuron.py +0 -0
  17. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  18. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_stabilizer.py +0 -0
  19. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_system/__init__.py +0 -0
  20. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/qrack_system/qrack_system.py +0 -0
  21. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/quimb_circuit_type.py +0 -0
  22. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/stats/__init__.py +0 -0
  23. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/stats/load_quantized_data.py +0 -0
  24. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack/stats/quantize_by_range.py +0 -0
  25. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack_cpu_complex128.egg-info/SOURCES.txt +0 -0
  26. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack_cpu_complex128.egg-info/dependency_links.txt +0 -0
  27. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack_cpu_complex128.egg-info/not-zip-safe +0 -0
  28. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack_cpu_complex128.egg-info/requires.txt +0 -0
  29. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/pyqrack_cpu_complex128.egg-info/top_level.txt +0 -0
  30. {pyqrack_cpu_complex128-2.22.4 → pyqrack_cpu_complex128-2.24.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cpu-complex128
3
- Version: 2.22.4
3
+ Version: 2.24.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -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)
@@ -1741,7 +1750,6 @@ class QrackAceBackend:
1741
1750
  # logical value moved, the same way a repetition code's
1742
1751
  # decoded value doesn't change just because one physical
1743
1752
  # bit needed fixing.
1744
- is_flipped = False
1745
1753
  n_flipped = sum(1 for b in bits if b)
1746
1754
  if (n_flipped << 1) > len(ancs):
1747
1755
  is_flipped = True
@@ -1971,46 +1979,174 @@ class QrackAceBackend:
1971
1979
  self._cpauli(lq1, lq2, True, Pauli.PauliZ)
1972
1980
 
1973
1981
  def mcx(self, lq1, lq2):
1974
- if len(lq1) > 1:
1982
+ if len(lq1) == 1:
1983
+ self.cx(lq1[0], lq2)
1984
+ return
1985
+
1986
+ hq = self._unpack(lq2)
1987
+ if len(hq) > 1:
1975
1988
  raise RuntimeError(
1976
1989
  "QrackAceBackend.mcx() is provided for syntax convenience and only supports 1 control qubit!"
1977
1990
  )
1978
- self.cx(lq1[0], lq2)
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)
1979
2004
 
1980
2005
  def mcy(self, lq1, lq2):
1981
- if len(lq1) > 1:
2006
+ if len(lq1) == 1:
2007
+ self.cy(lq1[0], lq2)
2008
+ return
2009
+
2010
+ hq = self._unpack(lq2)
2011
+ if len(hq) > 1:
1982
2012
  raise RuntimeError(
1983
2013
  "QrackAceBackend.mcy() is provided for syntax convenience and only supports 1 control qubit!"
1984
2014
  )
1985
- self.cy(lq1[0], lq2)
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)
1986
2028
 
1987
2029
  def mcz(self, lq1, lq2):
1988
- if len(lq1) > 1:
2030
+ if len(lq1) == 1:
2031
+ self.cz(lq1[0], lq2)
2032
+ return
2033
+
2034
+ hq = self._unpack(lq2)
2035
+ if len(hq) > 1:
1989
2036
  raise RuntimeError(
1990
2037
  "QrackAceBackend.mcz() is provided for syntax convenience and only supports 1 control qubit!"
1991
2038
  )
1992
- self.cz(lq1[0], lq2)
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)
1993
2052
 
1994
2053
  def macx(self, lq1, lq2):
1995
- if len(lq1) > 1:
2054
+ if len(lq1) == 1:
2055
+ self.acx(lq1[0], lq2)
2056
+ return
2057
+
2058
+ hq = self._unpack(lq2)
2059
+ if len(hq) > 1:
1996
2060
  raise RuntimeError(
1997
2061
  "QrackAceBackend.macx() is provided for syntax convenience and only supports 1 control qubit!"
1998
2062
  )
1999
- self.acx(lq1[0], lq2)
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)
2000
2076
 
2001
2077
  def macy(self, lq1, lq2):
2002
- if len(lq1) > 1:
2078
+ if len(lq1) == 1:
2079
+ self.acy(lq1[0], lq2)
2080
+ return
2081
+
2082
+ hq = self._unpack(lq2)
2083
+ if len(hq) > 1:
2003
2084
  raise RuntimeError(
2004
2085
  "QrackAceBackend.macy() is provided for syntax convenience and only supports 1 control qubit!"
2005
2086
  )
2006
- self.acy(lq1[0], lq2)
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)
2007
2100
 
2008
2101
  def macz(self, lq1, lq2):
2009
- if len(lq1) > 1:
2102
+ if len(lq1) == 1:
2103
+ self.acz(lq1[0], lq2)
2104
+ return
2105
+
2106
+ hq = self._unpack(lq2)
2107
+ if len(hq) > 1:
2010
2108
  raise RuntimeError(
2011
2109
  "QrackAceBackend.macz() is provided for syntax convenience and only supports 1 control qubit!"
2012
2110
  )
2013
- self.acz(lq1[0], lq2)
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)
2014
2150
 
2015
2151
  def swap(self, lq1, lq2):
2016
2152
  hq1 = self._unpack(lq1)
@@ -2436,11 +2572,23 @@ class QrackAceBackend:
2436
2572
  self._sim.cy(operation.qubits[0]._index, operation.qubits[1]._index)
2437
2573
  elif name == "cz":
2438
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)
2439
2585
  elif name == "dcx":
2440
2586
  self._sim.mcx(operation.qubits[0]._index, operation.qubits[1]._index)
2441
2587
  self._sim.mcx(operation.qubits[1]._index, operation.qubits[0]._index)
2442
2588
  elif name == "swap":
2443
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)
2444
2592
  elif name == "iswap":
2445
2593
  self._sim.iswap(operation.qubits[0]._index, operation.qubits[1]._index)
2446
2594
  elif name == "iswap_dg":
@@ -2680,7 +2828,10 @@ class QrackAceBackend:
2680
2828
  "cx",
2681
2829
  "cy",
2682
2830
  "cz",
2831
+ "ccx",
2832
+ "ccz",
2683
2833
  "swap",
2834
+ "cswap",
2684
2835
  "iswap",
2685
2836
  "reset",
2686
2837
  "measure",
@@ -2723,6 +2874,149 @@ class QrackAceBackend:
2723
2874
 
2724
2875
  return self._coupling_map
2725
2876
 
2877
+ # Moved here from AceQasmSimulator (qiskit-qrack-provider), so a
2878
+ # QrackAceBackend can be transpiled against directly with plain Qiskit,
2879
+ # without needing the separate Qiskit BackendV2 wrapper at all. The
2880
+ # Qiskit backend now calls this instead of duplicating the logic.
2881
+ #
2882
+ # One correction made in the move: the original 1-qubit "boundary
2883
+ # penalty hint" set was approximated from get_logical_coupling_map()
2884
+ # (any qubit appearing in ANY coupling pair) -- but that map includes
2885
+ # ordinary bulk-bulk pairs within the same patch too, so it actually
2886
+ # flagged nearly every qubit as "boundary." Here, with direct access to
2887
+ # self._unpack(), the real criterion (len(self._unpack(q)) > 1) is used
2888
+ # instead.
2889
+ def get_target(self, description=None, error_2q=0.5):
2890
+ """Build a qiskit Target describing this backend's gate set and
2891
+ connectivity, so it can be used with qiskit.transpile() directly --
2892
+ with or without the separate Qiskit BackendV2 wrapper.
2893
+
2894
+ IMPORTANT, verified directly rather than assumed: qiskit's
2895
+ transpile() does NOT reliably enforce the qubit-tuple-level
2896
+ InstructionProperties restrictions declared here for 3+-qubit gates
2897
+ (ccx/ccz/cswap), at any optimization_level that doesn't outright
2898
+ crash. Confirmed: the default optimization_level raises
2899
+ TranspilerError("target contains multi-qubit operations") on ANY
2900
+ Target containing 3+-qubit gates, valid placement or not (Qiskit's
2901
+ default SabreLayout routing pass doesn't support them at all).
2902
+ optimization_level=0 or 1 avoid that crash, but ALSO transpiled a
2903
+ deliberately-invalid (boundary-qubit) ccx placement successfully in
2904
+ testing -- i.e., they don't validate 3-qubit placements against the
2905
+ target either, they just don't crash on them.
2906
+ The Target's 3-qubit entries are still meaningful as a description
2907
+ of what's actually valid (useful for introspection, or for a
2908
+ transpiler pass that does check placements properly), but they are
2909
+ NOT currently a transpile-time safety guarantee the way the 2-qubit
2910
+ coupling map is. The actual, reliable enforcement is the runtime
2911
+ RuntimeError in mcx()/mcy()/mcz()/cswap() themselves, which fire
2912
+ correctly regardless of what the transpiler allowed through.
2913
+ """
2914
+ if not _IS_QISKIT_AVAILABLE:
2915
+ raise RuntimeError(
2916
+ "QrackAceBackend.get_target() requires Qiskit to be installed!"
2917
+ )
2918
+
2919
+ n = self.num_qubits()
2920
+ tgt = Target(num_qubits=n, description=description)
2921
+
2922
+ # Parameters for parameterised gates
2923
+ theta = Parameter("theta")
2924
+ phi = Parameter("phi")
2925
+ lam = Parameter("lam")
2926
+
2927
+ # --- single-qubit gates: all qubits ---
2928
+ boundary = {q for q in range(n) if len(self._unpack(q)) > 1}
2929
+
2930
+ def _1q_props():
2931
+ return {
2932
+ (q,): InstructionProperties()
2933
+ for q in range(n)
2934
+ }
2935
+
2936
+ tgt.add_instruction(IGate(), _1q_props())
2937
+ tgt.add_instruction(UGate(theta, phi, lam), _1q_props())
2938
+ tgt.add_instruction(U3Gate(theta, phi, lam), _1q_props())
2939
+ tgt.add_instruction(U2Gate(phi, lam), _1q_props())
2940
+ tgt.add_instruction(U1Gate(lam), _1q_props())
2941
+ tgt.add_instruction(RXGate(theta), _1q_props())
2942
+ tgt.add_instruction(RYGate(theta), _1q_props())
2943
+ tgt.add_instruction(RZGate(theta), _1q_props())
2944
+ tgt.add_instruction(HGate(), _1q_props())
2945
+ tgt.add_instruction(XGate(), _1q_props())
2946
+ tgt.add_instruction(YGate(), _1q_props())
2947
+ tgt.add_instruction(ZGate(), _1q_props())
2948
+ tgt.add_instruction(SGate(), _1q_props())
2949
+ tgt.add_instruction(SdgGate(), _1q_props())
2950
+ tgt.add_instruction(TGate(), _1q_props())
2951
+ tgt.add_instruction(TdgGate(), _1q_props())
2952
+
2953
+ # --- two-qubit gates: coupled pairs (includes boundary-crossing
2954
+ # pairs -- those are supported, via the noisy-coupler mechanism) ---
2955
+ bulk = []
2956
+ for q in range(n):
2957
+ hq = self._unpack(q)
2958
+ if len(hq) == 1:
2959
+ bulk.append(q)
2960
+ coupling_map = self.get_logical_coupling_map()
2961
+ if coupling_map:
2962
+ pair_props = {
2963
+ (a, b): InstructionProperties() if (a in bulk) and (b in bulk) else InstructionProperties(error=error_2q) for a, b in coupling_map
2964
+ }
2965
+ else:
2966
+ pair_props = {
2967
+ (a, b): InstructionProperties()
2968
+ for a in range(n)
2969
+ for b in range(n)
2970
+ if a != b
2971
+ }
2972
+
2973
+ tgt.add_instruction(CXGate(), pair_props)
2974
+ tgt.add_instruction(CYGate(), pair_props)
2975
+ tgt.add_instruction(CZGate(), pair_props)
2976
+ tgt.add_instruction(SwapGate(), pair_props)
2977
+ tgt.add_instruction(iSwapGate(), pair_props)
2978
+
2979
+ # --- 3-qubit gates: intra-patch, all-bulk triples only ---
2980
+ # ccx/ccz/cswap (and, by extension, mcx/mcy/mcz beyond a single
2981
+ # control) are only valid when every involved qubit is bulk and
2982
+ # shares the same patch -- see mcx()/cswap() above, which raise
2983
+ # otherwise.
2984
+ #
2985
+ # CCXGate/CCZGate and CSwapGate do NOT share a qubit-ordering
2986
+ # convention (verified directly against their gate definitions):
2987
+ # CCXGate puts its target on qubit index 2, with 0-1 as the two
2988
+ # (interchangeable) controls; CSwapGate puts its control on qubit
2989
+ # index 0, with 1-2 as the (interchangeable) swapped pair. They
2990
+ # need separately-ordered InstructionProperties dicts.
2991
+ bulk_by_patch = {}
2992
+ for q in range(n):
2993
+ hq = self._unpack(q)
2994
+ if len(hq) == 1:
2995
+ bulk_by_patch.setdefault(hq[0][0], []).append(q)
2996
+
2997
+ ccx_props = {}
2998
+ cswap_props = {}
2999
+ for qubits_here in bulk_by_patch.values():
3000
+ for pivot in qubits_here:
3001
+ others = [q for q in qubits_here if q != pivot]
3002
+ for i in range(len(others)):
3003
+ for j in range(len(others)):
3004
+ if i == j:
3005
+ continue
3006
+ ccx_props[(others[i], others[j], pivot)] = InstructionProperties()
3007
+ cswap_props[(pivot, others[i], others[j])] = InstructionProperties()
3008
+ if ccx_props:
3009
+ tgt.add_instruction(CCXGate(), ccx_props)
3010
+ tgt.add_instruction(CCZGate(), ccx_props)
3011
+ if cswap_props:
3012
+ tgt.add_instruction(CSwapGate(), cswap_props)
3013
+
3014
+ # --- measure / reset: all qubits ---
3015
+ tgt.add_instruction(Measure(), {(q,): InstructionProperties() for q in range(n)})
3016
+ tgt.add_instruction(Reset(), {(q,): InstructionProperties() for q in range(n)})
3017
+
3018
+ return tgt
3019
+
2726
3020
  # Designed by Dan, Elara (ChatGPT), and (Anthropic) Claude:
2727
3021
  def create_noise_model(self, x=0.5, y=0.5):
2728
3022
  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":
@@ -4569,6 +4571,7 @@ class QrackSimulator:
4569
4571
  "ccx",
4570
4572
  "ccz",
4571
4573
  "swap",
4574
+ "cswap",
4572
4575
  "iswap",
4573
4576
  "cswap",
4574
4577
  "reset",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cpu-complex128
3
- Version: 2.22.4
3
+ Version: 2.24.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -7,7 +7,7 @@ from setuptools import setup
7
7
  from setuptools.command.build_py import build_py
8
8
 
9
9
 
10
- VERSION = "2.22.4"
10
+ VERSION = "2.24.0"
11
11
 
12
12
  # Read long description from README.
13
13
  README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md')