Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
benchmark.py
Go to the documentation of this file.
1 import itertools
2 from unittest import result
3 from squander.partitioning.partition import PartitionCircuitQasm
4 from qiskit import QuantumCircuit
5 from squander.partitioning.tools import qiskit_to_squander_name, get_qubits, total_float_ops
6 from squander import utils
7 
8 import timeit
9 import glob
10 import os
11 
12 USE_ILP = True
13 
14 MAX_GATES_ALLOWED = 1024**2
15 
16 METHOD_NAMES = [
17  "kahn",
18  "tdag",
19  "gtqcp",
20  "qiskit",
21  "qiskit-fusion",
22  "bqskit-Quick",
23  # "bqskit-Greedy",
24  # "bqskit-Scan",
25  # "bqskit-Cluster",
26 ] + (["ilp", "ilp-fusion", #"ilp-fusion-ca"
27  ] if USE_ILP else [])
28 
29 from squander.gates import gates_Wrapper as gate
30 SUPPORTED_GATES = {x for n in dir(gate) for x in (getattr(gate, n),) if not n.startswith("_") and issubclass(x, gate.Gate) and n != "Gate"}
31 SUPPORTED_GATES_NAMES = {n for n in dir(gate) if not n.startswith("_") and issubclass(getattr(gate, n), gate.Gate) and n != "Gate"}
32 
34  """
35  Run a small symbolic experiment to study “purity” and “sparsity” of control sets.
36 
37  This routine builds a set of helper functions and 1q/2q/3q gate factories with SymPy,
38  then:
39  1) Checks which qubit subsets act as pure/sparse controls for some canonical gates
40  (e.g., U3 on 1q, CRY on 2q, CCX on 3q).
41  2) Composes short circuits (e.g., U3 on qubit i followed by CRY(0,1)) and reports
42  which control subsets remain pure/sparse after composition.
43  3) Prints the identified “pure” and “sparsity” control sets for each experiment.
44 
45  Notes:
46  - Endianness is governed by the local variable `little_endian` (default: True).
47  - `apply_to` operates on full operators (2^n × 2^n), not statevectors.
48  - Requires SymPy. The helper `apply_to` also uses `itertools.product`.
49 
50  Args:
51  None
52 
53  Returns:
54  None
55  Results are printed to stdout; the function is intended as a diagnostic/demo.
56  """
57  import sympy, functools, operator
58  from sympy.combinatorics import Permutation
59  theta, phi, lbda, gamma = sympy.Symbol("θ"), sympy.Symbol("ϕ"), sympy.Symbol("λ"), sympy.Symbol("γ")
60  alpha, theta2 = sympy.Symbol("α"), sympy.Symbol("θ2")
61  little_endian = True
62  def find_control_qubits(psi, num_qubits):
63  """
64  Identify control-qubit sets that make a unitary ‘pure-controlled’ or ‘sparse-controlled’.
65 
66  A subset S of qubits is:
67  - pure-controlled if, when control pattern S is not fully satisfied in basis index j,
68  row j of the unitary equals the corresponding computational basis row (identity row).
69  - sparse-controlled if, when control pattern S is not fully satisfied in j,
70  row j has zeros everywhere except possibly in columns k that satisfy S.
71 
72  Args:
73  psi (sympy.Matrix): 2**num_qubits × 2**num_qubits unitary matrix.
74  num_qubits (int): Total number of qubits.
75 
76  Returns:
77  tuple[list[list[int]], list[list[int]]]:
78  pure_controls: list of control index lists (little-endian by default) making psi pure-controlled.
79  sparsity_controls: list of control index lists making psi sparse-controlled.
80  """
81  pure_controls = []
82  sparsity_controls = []
83  for i in range(1, 1<<num_qubits):
84  is_pure, is_sparse = True, True
85  for j in range(1<<num_qubits):
86  if i & j != i:
87  if not all(psi[j, k] == (1 if j == k else 0) for k in range(1<<num_qubits)):
88  is_pure = False
89  if not all(psi[j, k] == 0 for k in range(1<<num_qubits) if i & k == i):
90  is_sparse = False
91  if is_pure: pure_controls.append([j if little_endian else num_qubits-1-j for j in range(num_qubits) if i & (1<<j)])
92  if is_sparse: sparsity_controls.append([j if little_endian else num_qubits-1-j for j in range(num_qubits) if i & (1<<j)])
93  return pure_controls, sparsity_controls
94  def force_euler_pairs(e):
95  # replace 1 - exp(iθ) and exp(iθ) - 1 with ±2i exp(iθ/2) sin(θ/2)
96  a = sympy.Wild('a', properties=[lambda k: k.is_real is not False])
97  e = e.replace(1 - sympy.exp(sympy.I*a), -2*sympy.I*sympy.exp(sympy.I*a/2)*sympy.sin(a/2))
98  e = e.replace(sympy.exp(sympy.I*a) - 1, 2*sympy.I*sympy.exp(sympy.I*a/2)*sympy.sin(a/2))
99  return e
100  def quantsimp(x, rewrite=False):
101  x = force_euler_pairs(sympy.sympify(x).rewrite(sympy.exp) if rewrite else sympy.sympify(x))
102  return sympy.simplify(sympy.trigsimp(sympy.exptrigsimp(sympy.together(sympy.expand_power_exp(sympy.powsimp(sympy.nsimplify(x, rational=True), force=True, deep=True)))), recursive=True)).doit()
103  def quantsimprw(x): return quantsimp(x, rewrite=True)
104  def apply_to(psi, num_qubits, gate, gate_qubits):
105  """
106  Left-apply a k-qubit gate to designated positions inside an n-qubit operator.
107 
108  Embeds the k-qubit `gate` on `gate_qubits` (indices in [0..n-1]) under the
109  current endianness, and returns (gate ⊗ I_rest) · psi with the correct qubit
110  wiring. Works for psi as a full 2^n×2^n operator (not a statevector).
111 
112  Args:
113  psi (sympy.Matrix): 2**num_qubits × 2**num_qubits operator to transform.
114  num_qubits (int): Total number of qubits n.
115  gate (sympy.Matrix): 2**k × 2**k unitary to embed.
116  gate_qubits (Iterable[int]): The k target qubit indices where `gate` acts.
117 
118  Returns:
119  sympy.Matrix: New 2**num_qubits × 2**num_qubits matrix after applying `gate`.
120  """
121  pos = [(q if little_endian else num_qubits-1-q) for q in gate_qubits]
122  k = len(gate_qubits)
123  deltas, combo = [1<<p for p in pos], []
124  for u in range (1 << k):
125  m = 0; x = u; b = 0
126  while x:
127  if x & 1: m ^= deltas[b]
128  x >>= 1; b += 1
129  combo.append(m)
130  pos_set = set(pos)
131  rest = [p for p in range(num_qubits) if not p in pos_set]
132  output = sympy.zeros(1<<num_qubits, 1<<num_qubits)
133  for rest_bits in itertools.product((0, 1), repeat=len(rest)):
134  base = 0
135  for bit, p in zip(rest_bits, rest):
136  if bit: base |= 1<<p
137  mat, idx = sympy.zeros(0, 1<<num_qubits), []
138  for u in range(1 << k):
139  idx.append(base ^ combo[u])
140  mat = mat.col_join(psi[idx[u],:])
141  prod = gate @ mat
142  for u in range(1 << k):
143  output[idx[u],:] = prod[u,:].applyfunc(quantsimp)
144  return output
145  def compile_gates(num_qubits, gates):
146  """
147  Compose a list of embedded gates into a full n-qubit unitary.
148 
149  Args:
150  num_qubits (int): Total number of qubits n.
151  gates (Iterable[tuple[sympy.Matrix, Iterable[int]]]):
152  Sequence of (gate, gate_qubits) pairs. Each `gate` is 2**k×2**k,
153  applied to the provided `gate_qubits`.
154 
155  Returns:
156  sympy.Matrix: The resulting 2**n × 2**n unitary product.
157  """
158  Umtx = sympy.eye(2**num_qubits)
159  for gate in gates: Umtx = apply_to(Umtx, num_qubits, *gate)
160  return Umtx
161  def make_controlled(gate, gate_qubits, gateother=None): #control is first qubit, gate qubits come after
162  """
163  Build a 1-control controlled version of `gate` (control is the first qubit by convention).
164 
165  Constructs block-diagonal diag(gateother or I, gate) and applies a permutation so that,
166  under little-endian, the control is the least significant qubit.
167 
168  Args:
169  gate (sympy.Matrix): 2**m × 2**m target unitary.
170  gate_qubits (int): Number of target qubits m (excluding the control qubit).
171  gateother (sympy.Matrix | None): Optional block for the control-off subspace.
172  If None, identity of size 2**m is used.
173 
174  Returns:
175  sympy.Matrix: A 2**(m+1) × 2**(m+1) controlled-gate matrix.
176  """
177  res = sympy.diag(gateother if not gateother is None else sympy.eye(1<<gate_qubits), gate)
178  P = sympy.eye(1<<(gate_qubits+1))[:, [2*x+y for y in (0, 1) for x in range(1<<gate_qubits)]]
179  return P * res * P.T if little_endian else res
180  def make_inverse(g): return g**-1
181  def make_sqrt(g): return g**(1/2)
182  def gen_I(): return sympy.eye(2)
183  def gen_Rx(theta): return sympy.exp(-sympy.I*theta/2*gen_X()).applyfunc(quantsimp)
184  def gen_Ry(theta): return sympy.exp(-sympy.I*theta/2*gen_Y()).applyfunc(quantsimp)
185  def gen_Rz(phi): return sympy.exp(-sympy.I*phi/2*gen_Z()).applyfunc(quantsimp)
186  def gen_GP(theta, qbits): return sympy.exp(theta*sympy.I)*sympy.eye(1<<qbits)
187  def gen_H(): return sympy.Matrix([[1, 1], [1, -1]])/sympy.sqrt(2)
188  def gen_X(): return sympy.Matrix([[0, 1], [1, 0]])
189  def gen_Y(): return sympy.Matrix([[0, -sympy.I], [sympy.I, 0]])
190  def gen_Z(): return sympy.Matrix([[1, 0], [0, -1]])
191  def gen_S(): return sympy.Matrix([[1, 0], [0, sympy.I]])
192  def gen_Sdg(): return make_inverse(gen_S()) #sympy.Matrix([[1, 0], [0, -sympy.I]])
193  def gen_SX(): return make_sqrt(gen_X()).applyfunc(quantsimp)
194  def gen_CZPowGate(t): return gen_CP(sympy.pi*t)
195  def gen_fSim(theta, phi): return compile_gates(2, [(gen_iSWAP_pow(-2*theta/sympy.pi), [0, 1]), (gen_CZPowGate(-phi/sympy.pi), [0, 1])])
196  def gen_SYC(): return gen_fSim(sympy.pi/2, sympy.pi/6)
197  def gen_T(): return sympy.Matrix([[1, 0], [0, sympy.exp(sympy.I*sympy.pi/4)]])
198  def gen_Tdg(): return make_inverse(gen_T())
199  def gen_P(theta): return sympy.Matrix([[1, 0], [0, sympy.exp(sympy.I*theta)]])
200  def gen_U1(theta): return gen_P(theta)
201  def gen_CH(): return sympy.Matrix(make_controlled(gen_H(), 1))
202  def gen_CX(): return sympy.Matrix(make_controlled(gen_X(), 1))
203  def gen_CNOT(): return gen_CX()
204  def gen_CY(): return sympy.Matrix(make_controlled(gen_Y(), 1))
205  def gen_CZ(): return sympy.Matrix(make_controlled(gen_Z(), 1))
206  def gen_U(theta, phi, lbda): return compile_gates(1, [(gen_Rz(phi), [0]), (gen_Ry(theta), [0]), (gen_Rz(lbda), [0]), (gen_GP((phi+lbda)/2, 1), [0])])
207  def gen_U2(phi, lbda): return gen_U(sympy.pi/2, phi, lbda)
208  def gen_U3(theta, phi, lbda): return gen_U(theta, phi, lbda)
209  def gen_R(theta, phi): return gen_U(theta, phi-sympy.pi/2, -phi+sympy.pi/2)
210  def gen_CR(theta, phi): return make_controlled(gen_R(theta, phi), 1)
211  def gen_CROT(theta, phi): return make_controlled(gen_R(theta, phi), 1, gen_R(-theta, phi))
212  def gen_CRX(theta): return make_controlled(gen_Rx(theta), 1)
213  def gen_CRY(theta): return make_controlled(gen_Ry(theta), 1)
214  def gen_CRZ(theta): return make_controlled(gen_Rz(theta), 1)
215  def gen_CCX(): return make_controlled(gen_CNOT(), 2)
216  def gen_Toffoli(): return gen_CCX()
217  def gen_CCZ(): return make_controlled(gen_CZ(), 2)
218  def gen_SWAP(): return functools.reduce(operator.add, (compile_gates(2, [(gen(), [0]), (gen(), [1])]) for gen in (gen_I, gen_X, gen_Y, gen_Z))) / 2
219  def gen_CSWAP(): return make_controlled(gen_SWAP(), 2)
220  def gen_iSWAP(): return gen_Rxy(-sympy.pi)
221  def gen_iSWAP_pow(alpha): return (gen_iSWAP()**alpha).applyfunc(quantsimprw)#.rewrite(sympy.exp)
222  def gen_CP(phi): return make_controlled(gen_P(phi), 1)
223  #def gen_CR(phi): return gen_CP(phi)
224  def gen_CS(): return make_controlled(gen_S(), 1)
225  def gen_CU1(theta): return gen_CP(theta)
226  def gen_CU(theta, phi, lbda, gamma): return make_controlled(compile_gates(1, [(gen_U(theta, phi, lbda), [0]), (gen_GP(gamma, 1), [0])]), 1)
227  def gen_CU3(theta, phi, lbda): return gen_CU(theta, phi, lbda, 0)
228  def gen_Rxx(theta): return sympy.exp(-sympy.I*theta/2*compile_gates(2, [(gen_X(), [0]), (gen_X(), [1])])).applyfunc(quantsimp) #compile_gates(2, [(sympy.cos(theta/2)*gen_I(), [0]), (gen_I(), [1])]) - compile_gates(2, [(sympy.I*sympy.sin(theta/2)*gen_X(), [0]), (gen_X(), [1])])
229  def gen_Ryy(theta): return sympy.exp(-sympy.I*theta/2*compile_gates(2, [(gen_Y(), [0]), (gen_Y(), [1])])).applyfunc(quantsimp) #compile_gates(2, [(sympy.cos(theta/2)*gen_I(), [0]), (gen_I(), [1])]) - compile_gates(2, [(sympy.I*sympy.sin(theta/2)*gen_Y(), [0]), (gen_Y(), [1])])
230  def gen_Rzz(theta): return sympy.exp(-sympy.I*theta/2*compile_gates(2, [(gen_Z(), [0]), (gen_Z(), [1])])).applyfunc(quantsimp) #compile_gates(2, [(sympy.cos(theta/2)*gen_I(), [0]), (gen_I(), [1])]) - compile_gates(2, [(sympy.I*sympy.sin(theta/2)*gen_Z(), [0]), (gen_Z(), [1])])
231  def gen_Rxy(phi): return sympy.exp(-sympy.I*phi/4*(compile_gates(2, [(gen_X(), [0]), (gen_X(), [1])]) + compile_gates(2, [(gen_Y(), [0]), (gen_Y(), [1])]))).applyfunc(quantsimprw)
232  def gen_CZ_decomp(): return compile_gates(2, [(gen_H(), [1]), (gen_CNOT(), [0, 1]), (gen_H(), [1])])
233  def gen_CY_decomp(): return compile_gates(2, [(gen_H(), [1]), (gen_S(), [1]), (gen_CNOT(), [0, 1]), (gen_Sdg(), [1]), (gen_H(), [1])])
234  def gen_CH_decomp(): return compile_gates(2, [(gen_Rz(-sympy.pi/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(-sympy.pi/2), [1]), (gen_Ry(sympy.pi/4), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(-sympy.pi/4), [1]), (gen_Rz(sympy.pi), [1]), (gen_P(sympy.pi/2), [0])])
235  def gen_CP_decomp(phi): return compile_gates(2, [(gen_Rz(phi/2), [0]), (gen_CNOT(), [0, 1]), (gen_Rz(-phi/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(phi/2), [1]), (gen_GP(phi/4, 2), [0, 1])])# @
236  def gen_CRX_decomp(theta): return compile_gates(2, [(gen_H(), [1]), (gen_Rz(theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(-theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_H(), [1])])
237  def gen_CRY_decomp(theta): return compile_gates(2, [(gen_Ry(theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(-theta/2), [1]), (gen_CNOT(), [0, 1])])
238  def gen_CRZ_decomp(theta): return compile_gates(2, [(gen_Rz(theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(-theta/2), [1]), (gen_CNOT(), [0, 1])])
239  def gen_CS_decomp(): return compile_gates(2, [(gen_Rz(sympy.pi/4), [0]), (gen_CNOT(), [0, 1]), (gen_Rz(-sympy.pi/4), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(sympy.pi/4), [1]), (gen_GP(sympy.pi/8, 2), [0, 1])])
240  def gen_CR_decomp(theta, phi): return compile_gates(2, [(gen_Rz(phi-sympy.pi/2), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(-theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(theta/2), [1]), (gen_Rz(-phi+sympy.pi/2), [1])])
241  def gen_CROT_decomp(theta, phi): return compile_gates(2, [(gen_Rz(phi), [1]), (gen_Ry(-sympy.pi/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(theta), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(sympy.pi/2), [1]), (gen_Rz(-phi), [1])])
242  def gen_Rxx_decomp(theta): return compile_gates(2, [(gen_H(), [0]), (gen_H(), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(theta), [1]), (gen_CNOT(), [0, 1]), (gen_H(), [0]), (gen_H(), [1])])
243  def gen_Ryy_decomp(theta): return compile_gates(2, [(gen_Rx(sympy.pi/2), [0]), (gen_Rx(sympy.pi/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(theta), [1]), (gen_CNOT(), [0, 1]), (gen_Rx(-sympy.pi/2), [0]), (gen_Rx(-sympy.pi/2), [1])])
244  def gen_Rzz_decomp(theta): return compile_gates(2, [(gen_CNOT(), [0, 1]), (gen_Rz(theta), [1]), (gen_CNOT(), [0, 1])])
245  def gen_Rxy_decomp(phi): return compile_gates(2, [(gen_H(), [0]), (gen_H(), [1]), (gen_CNOT(), [0,1]), (gen_Rz(phi/2), [1]), (gen_CNOT(), [0,1]), (gen_H(), [0]), (gen_H(), [1]), (gen_Sdg(), [0]), (gen_Sdg(), [1]), (gen_H(), [0]), (gen_H(), [1]), (gen_CNOT(), [0,1]), (gen_Rz(phi/2), [1]), (gen_CNOT(), [0,1]), (gen_H(), [0]), (gen_H(), [1]), (gen_S(), [0]), (gen_S(), [1])]).applyfunc(quantsimprw)
246  def gen_CZPowGate_decomp(t): return gen_CP_decomp(sympy.pi*t)
247  def gen_iSWAP_pow_decomp(alpha): return compile_gates(2, [(gen_H(), [0]), (gen_H(), [1]), (gen_CNOT(), [0,1]), (gen_Rz(-(alpha*sympy.pi)/2), [1]), (gen_CNOT(), [0,1]), (gen_H(), [0]), (gen_H(), [1]), (gen_Sdg(), [0]), (gen_Sdg(), [1]), (gen_H(), [0]), (gen_H(), [1]), (gen_CNOT(), [0,1]), (gen_Rz(-(alpha*sympy.pi)/2), [1]), (gen_CNOT(), [0,1]), (gen_H(), [0]), (gen_H(), [1]), (gen_S(), [0]), (gen_S(), [1])]).applyfunc(quantsimprw)
248  def gen_fSim_decomp(theta, phi): return compile_gates(2, [(gen_iSWAP_pow_decomp(-2*theta/sympy.pi), [0, 1]), (gen_CZPowGate_decomp(-phi/sympy.pi), [0, 1])])
249  #def gen_SYC_decomp(): return gen_fSim_decomp(sympy.pi/2, sympy.pi/6)
250  #3 CNOT SYC: https://epjquantumtechnology.springeropen.com/articles/10.1140/epjqt/s40507-024-00248-8
251  def gen_SYC_decomp(): return compile_gates(2, [(gen_Rz(-3*sympy.pi/4), [0]), (gen_Rz(sympy.pi/4), [1]), (gen_SX(), [0]), (gen_SX(), [1]), (gen_Rz(-sympy.pi), [0]), (gen_Rz(sympy.pi), [1]), (gen_SX(), [1]), (gen_Rz(5*sympy.pi/2), [1]), (gen_CNOT(), [0, 1]), (gen_SX(), [0]), (gen_Rz(-3*sympy.pi/4), [1]), (gen_SX(), [1]), (gen_Rz(sympy.pi), [1]), (gen_SX(), [1]), (gen_Rz(9*sympy.pi/4), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(sympy.pi/2), [0]), (gen_SX(), [1]), (gen_SX(), [0]), (gen_Rz(sympy.pi/2), [1]), (gen_Rz(11*sympy.pi/12), [0]), (gen_SX(), [1]), (gen_SX(), [0]), (gen_Rz(sympy.pi/2), [1]), (gen_CNOT(), [0, 1]), (gen_SX(), [0]), (gen_Rz(sympy.pi/2), [1]), (gen_Rz(sympy.pi/6), [0]), (gen_SX(), [1]), (gen_Rz(-sympy.pi/3), [1]), (gen_GP(-7*sympy.pi/24, 2), [0, 1])]).rewrite(quantsimprw)
252  def gen_CU_decomp(theta, phi, lbda, gamma): return compile_gates(2, [(gen_Rz((phi-lbda)/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(-(phi+lbda)/2), [1]), (gen_Ry(-theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(theta/2), [1]), (gen_Rz(lbda), [1]), (gen_P((lbda+phi)/2+gamma), [0])])
253  def gen_CU3_decomp(theta, phi, lbda): return compile_gates(2, [(gen_Rz((phi-lbda)/2), [1]), (gen_CNOT(), [0, 1]), (gen_Rz(-(phi+lbda)/2), [1]), (gen_Ry(-theta/2), [1]), (gen_CNOT(), [0, 1]), (gen_Ry(theta/2), [1]), (gen_Rz(lbda), [1]), (gen_P((lbda+phi)/2), [0])])
254  def gen_CCZ_decomp(): return compile_gates(3, [(gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [1]), (gen_T(), [2]), (gen_CNOT(), [0, 1]), (gen_T(), [0]), (gen_Tdg(), [1]), (gen_CNOT(), [0, 1])])
255  def gen_CCX_decomp(): return compile_gates(3, [(gen_H(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [1]), (gen_T(), [2]), (gen_H(), [2]), (gen_CNOT(), [0, 1]), (gen_T(), [0]), (gen_Tdg(), [1]), (gen_CNOT(), [0, 1])])
256  def gen_SWAP_decomp(): return compile_gates(2, [(gen_CNOT(), [0, 1]), (gen_CNOT(), [1, 0]), (gen_CNOT(), [0, 1])])
257  #7 CNOT CSWAP: https://arxiv.org/pdf/2305.18128
258  def gen_CSWAP_decomp(): return sympy.simplify(sympy.expand_complex(compile_gates(3, [(gen_S(), [1]), (gen_CNOT(), [2, 1]), (gen_Sdg(), [1]), (gen_SX(), [2]), (gen_T(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [2]), (gen_CNOT(), [1, 2]), (gen_T(), [1]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_CNOT(), [0, 1]), (gen_T(), [2]), (gen_T(), [0]), (gen_Tdg(), [1]), (gen_H(), [2]), (gen_CNOT(), [0, 1]), (gen_CNOT(), [2, 1]), (gen_GP(-sympy.pi/4, 3), [0, 1, 2])])))
259  #def gen_CSWAP_decomp(): return compile_gates(3, [(gen_CNOT(), [2, 1]), (gen_H(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [1]), (gen_T(), [2]), (gen_H(), [2]), (gen_CNOT(), [0, 1]), (gen_T(), [0]), (gen_Tdg(), [1]), (gen_CNOT(), [0, 1]), (gen_CNOT(), [2, 1])])
260  def gen_iSWAP_decomp(): return compile_gates(2, [(gen_S(), [0]), (gen_S(), [1]), (gen_H(), [0]), (gen_CNOT(), [0, 1]), (gen_CNOT(), [1, 0]), (gen_H(), [1])])
261  def gen_SX_test(): return sympy.Matrix([[1+sympy.I, 1-sympy.I], [1-sympy.I, 1+sympy.I]])/2
262  def gen_Rx_test(theta): return sympy.Matrix([[sympy.cos(theta/2), -sympy.I*sympy.sin(theta/2)], [-sympy.I*sympy.sin(theta/2), sympy.cos(theta/2)]])
263  def gen_SYC_test(): return sympy.Matrix([[1, 0, 0, 0], [0, 0, -sympy.I, 0], [0, -sympy.I, 0, 0], [0, 0, 0, sympy.exp(-sympy.I*sympy.pi/6)]])
264  #print(compile_gates(3, [(gen_H(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [2]), (gen_CNOT(), [1, 2]), (gen_Tdg(), [2]), (gen_CNOT(), [0, 2]), (gen_T(), [1]), (gen_T(), [2]), (gen_H(), [2])]))
265  #assert gen_Rxy(phi) == gen_Rxy_decomp(phi), (gen_Rxy(phi), gen_Rxy_decomp(phi))
266  #assert gen_iSWAP_pow(alpha) == gen_iSWAP_pow_decomp(alpha), (gen_iSWAP_pow(alpha), gen_iSWAP_pow_decomp(alpha))
267  assert gen_SYC() == gen_SYC_decomp(), (gen_SYC(), gen_SYC_decomp())
268  assert gen_Rx(theta) == gen_Rx_test(theta)
269  assert gen_SX() == gen_SX_test()
270  assert gen_SYC() == gen_SYC_test()
271  assert gen_CZ() == gen_CZ_decomp()
272  assert gen_CY() == gen_CY_decomp()
273  assert gen_CRX(theta) == gen_CRX_decomp(theta)
274  assert gen_CRY(theta) == gen_CRY_decomp(theta)
275  assert gen_CRZ(theta) == gen_CRZ_decomp(theta)
276  assert gen_CCZ() == gen_CCZ_decomp()
277  assert gen_CCX() == gen_CCX_decomp()
278  assert gen_CP(phi) == gen_CP_decomp(phi)
279  assert gen_CS() == gen_CS_decomp()
280  assert gen_CH() == gen_CH_decomp()
281  assert gen_CR(theta, phi) == gen_CR_decomp(theta, phi)
282  assert gen_Rxx(theta) == gen_Rxx_decomp(theta)
283  assert gen_Ryy(theta) == gen_Ryy_decomp(theta)
284  assert gen_Rzz(theta) == gen_Rzz_decomp(theta)
285  assert gen_Rxy(phi) == gen_Rxy_decomp(phi)
286  assert gen_CROT(theta, phi) == gen_CROT_decomp(theta, phi)
287  assert gen_CZPowGate(alpha) == gen_CZPowGate_decomp(alpha)
288  assert gen_iSWAP_pow(alpha) == gen_iSWAP_pow_decomp(alpha), (gen_iSWAP_pow(alpha), gen_iSWAP_pow_decomp(alpha))
289  assert gen_fSim(theta, phi) == gen_fSim_decomp(theta, phi), (gen_fSim(theta, phi), gen_fSim_decomp(theta, phi))
290  assert gen_SYC() == gen_SYC_decomp(), (gen_SYC(), gen_SYC_decomp())
291  assert gen_CU3(theta, phi, lbda) == gen_CU3_decomp(theta, phi, lbda)
292  assert gen_CU(theta, phi, lbda, gamma) == gen_CU_decomp(theta, phi, lbda, gamma)
293  assert gen_SWAP() == gen_SWAP_decomp()
294  assert gen_CSWAP() == gen_CSWAP_decomp()
295  assert gen_iSWAP() == gen_iSWAP_decomp()
296  #reverse CNOT is H-CNOT-H on both qubits
297  assert compile_gates(2, [(gen_CNOT(), [1, 0])]) == compile_gates(2, [(gen_H(), [0]), (gen_H(), [1]), (gen_CNOT(), [0, 1]), (gen_H(), [0]), (gen_H(), [1]),])
298  print(find_control_qubits(gen_U3(theta, phi, lbda), 1), find_control_qubits(gen_CRY(theta), 2), find_control_qubits(gen_CCX(), 3))
299  for i in range(3): #this proves any single qubit chain removes all purity, and converts aligning control to target
300  print(f"U3({i})@CRY(0, 1) pure, sparse control:", find_control_qubits(compile_gates(3, [(gen_U3(theta, phi, lbda), [i]), (gen_CRY(theta2), (0, 1))]), 3))
301  for i in range(3):
302  for j in range(3):
303  if i == j : continue
304  print(f"CRY({i}, {j})@CRY(0, 1) pure, sparse control:", find_control_qubits(compile_gates(3, [(gen_CRY(theta), [i, j]), (gen_CRY(theta2), (0, 1))]), 3))
305 
306 #purity_analysis(); assert False
307 
308 def projectq_import_qasm(filename, eng, initial_state=None):
309  import re, math
310  from projectq.ops import H, X, Y, Z, S, SqrtX, Sdag, T, Tdag, R, Rx, Ry, Rz, CNOT, CZ
311  def _eval_angle(expr):
312  # supports e.g. "pi/2", "3*pi/4", numeric literals
313  expr = expr.strip().lower().replace("pi", f"({math.pi})")
314  return eval(expr, {"__builtins__": {}}, {})
315  # Minimal OpenQASM-2 parser for common qelib1 gates.
316  # Ignores: creg/measure/barrier/includes/custom-gates. Extend as needed.
317  _rx = re.compile(r"rx\(([^)]+)\)\s+q\[(\d+)\];", re.I)
318  _ry = re.compile(r"ry\(([^)]+)\)\s+q\[(\d+)\];", re.I)
319  _rz = re.compile(r"rz\(([^)]+)\)\s+q\[(\d+)\];", re.I)
320  _u3 = re.compile(r"u3\(([^,]+),([^,]+),([^)]+)\)\s+q\[(\d+)\];", re.I)
321  _one = re.compile(r"(h|x|y|z|s|t|sdg|tdg|sx|r)\s+q\[(\d+)\];", re.I)
322  _cx = re.compile(r"cx\s+q\[(\d+)\],\s*q\[(\d+)\];", re.I)
323  _cz = re.compile(r"cz\s+q\[(\d+)\],\s*q\[(\d+)\];", re.I)
324  _meas = re.compile(r"measure\s+q\[(\d+)\]\s*->\s*([A-Za-z_]\w*)\[(\d+)\];", re.I)
325  _creg = re.compile(r"creg\s+([A-Za-z_]\w*)\[(\d+)\];", re.I)
326  _qreg = re.compile(r"qreg\s+q\[(\d+)\];", re.I)
327  with open(filename, "r") as fh:
328  lines = [ln.strip() for ln in fh if ln.strip() and not ln.startswith("//")]
329  nq = None
330  for ln in lines:
331  m = _qreg.search(ln)
332  if m:
333  nq = int(m.group(1)); break
334  qureg = eng.allocate_qureg(nq)
335  if not initial_state is None:
336  eng.flush()
337  eng.backend.set_wavefunction(initial_state.tolist(), qureg)
338  replay = []
339  for ln in lines:
340  if ln.lower().startswith(("openqasm", "include", "qreg", "creg", "barrier", "measure")):
341  continue
342  m = _one.match(ln)
343  if m:
344  gate, i = m.group(1).lower(), int(m.group(2))
345  replay.append(({ "h":H, "x":X, "y":Y, "z":Z, "s":S, "t":T, "sdg":Sdag, "tdg":Tdag, "sx":SqrtX, "r":R }[gate], qureg[i]))
346  continue
347  m = _rx.match(ln)
348  if m: replay.append((Rx(_eval_angle(m.group(1))), qureg[int(m.group(2))])); continue
349  m = _ry.match(ln)
350  if m: replay.append((Ry(_eval_angle(m.group(1))), qureg[int(m.group(2))])); continue
351  m = _rz.match(ln)
352  if m: replay.append((Rz(_eval_angle(m.group(1))), qureg[int(m.group(2))])); continue
353  m = _u3.match(ln)
354  if m:
355  theta, phi, lam, i = (_eval_angle(m.group(1)), _eval_angle(m.group(2)), _eval_angle(m.group(3)), int(m.group(4)))
356  # Decompose U3(θ, φ, λ) = Rz(φ) Ry(θ) Rz(λ)
357  replay.append((Rz(phi), qureg[i])); replay.append((Ry(theta), qureg[i])); replay.append((Rz(lam), qureg[i]))
358  continue
359  m = _cx.match(ln)
360  if m: replay.append((CNOT, (qureg[int(m.group(1))], qureg[int(m.group(2))]))); continue
361  m = _cz.match(ln)
362  if m: replay.append((CZ, (qureg[int(m.group(1))], qureg[int(m.group(2))]))); continue
363  # Unknown/unsupported line types are silently skipped. Add more patterns as needed.
364  return qureg, replay
365 def preprocess_qasm_angles(qasm: str) -> str:
366  import re, ast, math
367 
368  _ALLOWED_NODES = (ast.Expression, ast.BinOp, ast.UnaryOp, ast.Num, ast.Load,
369  ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow, ast.USub, ast.UAdd,
370  ast.Name, ast.Constant, ast.Call)
371  _ALLOWED_NAMES = {"pi": math.pi}
372  # If you also want 'tau' or 'e': add {"tau": 2*math.pi, "e": math.e}
373 
374  def _safe_eval(expr: str) -> float:
375  """Evaluate a simple arithmetic expression with 'pi' safely."""
376  # normalize unicode minus, strip spaces
377  expr = expr.replace("−", "-").strip()
378  node = ast.parse(expr, mode="eval")
379  for n in ast.walk(node):
380  if not isinstance(n, _ALLOWED_NODES):
381  raise ValueError(f"Disallowed token in angle expression: {type(n).__name__}")
382  if isinstance(n, ast.Name) and n.id not in _ALLOWED_NAMES:
383  raise ValueError(f"Unknown name in angle expression: {n.id}")
384  if isinstance(n, ast.Call): # disallow function calls
385  raise ValueError("Function calls not allowed in angle expressions")
386  return float(eval(compile(node, "<angle>", "eval"), {"__builtins__": {}}, _ALLOWED_NAMES))
387 
388  _angle_gate = r"(u1|u2|u3|rx|ry|rz|p|cp|r|crx|cry|crz)"
389  # Matches gate(params) with any content until the closing ')'
390  _param_pat = re.compile(rf"\b{_angle_gate}\s*\(([^)]+)\)", re.IGNORECASE)
391 
392  def _format_float(x: float) -> str:
393  # 17 digits -> round-trip for double; avoid scientific noise where possible
394  return f"{x:.17g}"
395  """Replace symbolic angle expressions (with 'pi') by numeric literals."""
396  def _repl(m: re.Match) -> str:
397  gate = m.group(1)
398  params = m.group(2)
399  # split on commas that are not nested (these are flat anyway)
400  parts = [p.strip() for p in params.split(",")]
401  new_parts = [_format_float(_safe_eval(p)) for p in parts]
402  return f"{gate}(" + ",".join(new_parts) + ")"
403  return _param_pat.sub(_repl, qasm)
404 import numpy as np
405 def normalize_state(state):
406  return state / np.linalg.norm(state)
408  """Checks if two quantum state vectors are equal up to a global phase."""
409  # Normalize to prevent issues if vectors are not exactly normalized
410  psi = normalize_state(psi)
411  phi = normalize_state(phi)
412 
413  # Compute relative phase using the first nonzero entry
414  phase_factor = np.vdot(psi, phi) # Inner product
415  phase = np.angle(phase_factor)
416 
417  # Compare after removing global phase
418  return np.allclose(psi * np.exp(-1j * phase), phi)
419 def test_simulation(max_qubits = 4, random_initial_state=True):
420  import numpy as np
421  SVSIM_METHOD_NAMES = ["SQUANDER", "Qiskit", "qulacs", "Cirq", "ProjectQ"]
422  files = glob.glob("benchmarks/partitioning/test_circuit/*.qasm")
423  print(f"Total QASM: {len(files)}, max qubits: {max_qubits}")
424  for filename in files:
425  qc = QuantumCircuit.from_qasm_file(filename)
426  qc_gates_names = {qiskit_to_squander_name(inst.operation.name) for inst in qc.data}
427  num_gates = len(qc.data)
428  fname = os.path.basename(filename)
429  if num_gates > MAX_GATES_ALLOWED or not qc_gates_names.issubset(SUPPORTED_GATES_NAMES) or filename.endswith("_qsearch.qasm") or filename.endswith("_squander.qasm"):
430  print(f"Skipping {fname}; qubits {qc.num_qubits} gates {qc_gates_names} num_gates {num_gates}")
431  continue
432  qbit_num = qc.num_qubits
433  matrix_size = 1 << qbit_num
434  print(f"Testing {fname}; qubits {qbit_num} num_gates {num_gates} matrix_size {matrix_size}")
435  if (random_initial_state ) :
436  initial_state_real = np.random.uniform(-1.0,1.0, (matrix_size,) )
437  initial_state_imag = np.random.uniform(-1.0,1.0, (matrix_size,) )
438  initial_state = initial_state_real + initial_state_imag*1j
439  initial_state = normalize_state(initial_state)
440  else:
441  initial_state = np.zeros( (matrix_size), dtype=np.complex128 )
442  initial_state[0] = 1.0 + 0j
443  res = {}
444  for method in SVSIM_METHOD_NAMES:
445  output = [None, None]
446  if method == "SQUANDER":
447  def f():
448  transformed_state = initial_state.copy()
449  circ, params, _ = PartitionCircuitQasm( filename, max_qubits, strategy="ilp-fusion" )
450  circ.set_min_fusion(14)
451  def run():
452  circ.apply_to(params, transformed_state)
453  output[0] = transformed_state
454  output[1] = run
455  elif method == "Qiskit":
456  import qiskit_aer as Aer
457  from qiskit import transpile
458  def f():
459  circuit_qiskit = QuantumCircuit.from_qasm_file(filename)
460  init = QuantumCircuit(qbit_num)
461  init.initialize( initial_state )
462  circuit_qiskit = circuit_qiskit.compose(init, front=True)
463  circuit_qiskit.save_statevector()
464  backend = Aer.AerSimulator(method='statevector', fusion_enable=True, fusion_max_qubit=max_qubits)
465  compiled_circuit = transpile(circuit_qiskit, backend)
466  def run():
467  result = backend.run(compiled_circuit).result()
468  transformed_state = result.get_statevector(compiled_circuit)
469  output[0] = np.array(transformed_state)
470  output[1] = run
471  elif method == "qulacs":
472  from qulacs import QuantumState, converter
473  import qulacs
474  def f():
475  state = QuantumState(qbit_num)
476  state.load( initial_state )
477  with open(filename, 'r') as file:
478  circuit_qulacs = converter.convert_QASM_to_qulacs_circuit([preprocess_qasm_angles(x) for x in file.readlines() if not x.startswith("creg")])
479  def run():
480  circuit_qulacs.update_quantum_state( state )
481  transformed_state = state.get_vector()
482  output[0] = transformed_state
483  output[1] = run
484  elif method == "Cirq":
485  import cirq #pip install cirq, ply, qsimcirq
486  from cirq.contrib.qasm_import import circuit_from_qasm
487  import qsimcirq
488  def f():
489  with open(filename, 'r') as file:
490  circuit_cirq = circuit_from_qasm(file.read())
491  #simulator = cirq.Simulator()
492  #initial_state_cirq = cirq.StateVectorSimulationState(initial_state=initial_state, qubits=[cirq.NamedQubit(f"q_{i}") for i in range(qbit_num-1,-1,-1)])
493  options = qsimcirq.QSimOptions(max_fused_gate_size=max_qubits, cpu_threads=os.cpu_count())
494  qs = qsimcirq.QSimSimulator(options)
495  state = initial_state.astype(np.complex64)
496  def run():
497  result = qs.simulate(circuit_cirq, qubit_order=[cirq.NamedQubit(f"q_{i}") for i in range(qbit_num-1,-1,-1)], initial_state=state)
498  #result = simulator.simulate(circuit_cirq, initial_state=initial_state_cirq)
499  transformed_state = result.final_state_vector
500  output[0] = transformed_state
501  output[1] = run
502  elif method == "ProjectQ":
503  from projectq import MainEngine
504  from projectq.backends import Simulator
505  from projectq.ops import Measure, All
506  def f():
507  eng = MainEngine(Simulator(gate_fusion=True))
508  qureg, replay = projectq_import_qasm(filename, eng, initial_state)
509  def run():
510  for op in replay: op[0] | op[1]
511  eng.flush() #Execute; ProjectQ performs fusion inside the C++ backend
512  _, psi = eng.backend.cheat()
513  transformed_state = np.array(psi, dtype=complex)
514  output[0] = transformed_state
515  All(Measure) | qureg
516  output[1] = run
517  print(method)
518  tpart = timeit.timeit(f, number=1)
519  trun = timeit.timeit(output[1], number=1)
520  res[method] = (tpart, trun, output[0])
521  #assert state_vector_equivalence(res["SQUANDER"][2], res[method][2])
522  overlap = np.abs(np.dot(res["SQUANDER"][2], np.conj(res[method][2])))
523  assert np.isclose(overlap, 1.0, atol=1e-3), overlap
524  #assert np.linalg.norm(res["SQUANDER"][2] - res[method][2]) < 1e-8
525  print({x: res[x][:2] for x in res})
526 
527 def test_partitions(max_qubits = 4):
528  """
529  Benchmark partitioning strategies on QASM circuits
530  Args:
531 
532  max_qubits: Max qubits per partition
533  """
534  files = glob.glob("benchmarks/partitioning/test_circuit/*.qasm")
535  print(f"Total QASM: {len(files)}, max qubits: {max_qubits}")
536  allfiles = {}
537  for filename in files:
538  qc = QuantumCircuit.from_qasm_file(filename)
539  qc_gates_names = {qiskit_to_squander_name(inst.operation.name) for inst in qc.data}
540  num_gates = len(qc.data)
541  fname = os.path.basename(filename)
542  if num_gates > MAX_GATES_ALLOWED or not qc_gates_names.issubset(SUPPORTED_GATES_NAMES) or filename.endswith("_qsearch.qasm") or filename.endswith("_squander.qasm"):
543  print(f"Skipping {fname}; qubits {qc.num_qubits} gates {qc_gates_names} num_gates {num_gates}")
544  continue
545  circ, _ = utils.qasm_to_squander_circuit(filename)
546  gate_dict = {i: gate for i, gate in enumerate(circ.get_Gates())}
547  gate_to_qubit = { i: get_qubits(g) for i, g in gate_dict.items() }
548  gate_to_tqubit = { i: g.get_Target_Qbit() for i, g in gate_dict.items() }
549  print(fname, qc.num_qubits, num_gates, f"k={max_qubits}",
550  "Max. float ops:", total_float_ops(qc.num_qubits, max_qubits, gate_to_qubit, None, [[i] for i in gate_dict]),
551  "Control-aware Max. float ops:", total_float_ops(qc.num_qubits, max_qubits, gate_to_qubit, gate_to_tqubit, [[i] for i in gate_dict]))
552  res = {}
553  for method in METHOD_NAMES:
554  print(method)
555  ls = []
556  def f():
557  ls.extend(PartitionCircuitQasm( filename, max_qubits, method ))
558  t = timeit.timeit(f, number=1)
559  partitioned_circuit, parameters, L = ls
560  res[method] = len(partitioned_circuit.get_Gates()), t, total_float_ops(qc.num_qubits, max_qubits, gate_to_qubit, None, L), total_float_ops(qc.num_qubits, max_qubits, gate_to_qubit, gate_to_tqubit, L)
561  allfiles[fname] = (qc.num_qubits, num_gates, res)
562  print(fname, allfiles[fname])
563  import json
564  print(json.dumps(allfiles, indent=2))
565  import matplotlib.pyplot as plt
566  sorted_items = sorted(allfiles.items(), key=lambda item: (item[1][0], item[1][1]))
567  circuits_sorted = [name for name, _ in sorted_items]
568  circuits_labels = [name.replace(".qasm", f" ({x[0]}, {x[1]})") for name, x in sorted_items]
569  print("Circuit Name & Qubit Count & Gate Count & " + " & ".join(METHOD_NAMES) + r"\\")
570  if USE_ILP:
571  ilpbest = [name for name in circuits_sorted if allfiles[name][2]["ilp"][0] != min(allfiles[name][2][method][0] for method in METHOD_NAMES if method != "ilp")]
572  ilptie = [name for name in circuits_sorted if allfiles[name][2]["ilp"][0] == min(allfiles[name][2][method][0] for method in METHOD_NAMES if method != "ilp")]
573  print("\n".join(" & ".join((name.replace(".qasm", "").replace("_", r"\_"), str(allfiles[name][0]), str(allfiles[name][1]),
574  *((r"\textbf{" if allfiles[name][2][method][0] == m else "") + str(allfiles[name][2][method][0]) + (r"}" if allfiles[name][2][method][0] == m else "") for method in METHOD_NAMES))) + r"\\" for name in ilpbest for m in (min(allfiles[name][2][method][0] for method in METHOD_NAMES),)))
575  print("\n".join(" & ".join((name.replace(".qasm", "").replace("_", r"\_"), str(allfiles[name][0]), str(allfiles[name][1]),
576  *((r"\textbf{" if allfiles[name][2][method][0] == m else "") + str(allfiles[name][2][method][0]) + (r"}" if allfiles[name][2][method][0] == m else "") for method in METHOD_NAMES))) + r"\\" for name in ilptie for m in (min(allfiles[name][2][method][0] for method in METHOD_NAMES),)))
577  else:
578  print("\n".join(" & ".join((name.replace(".qasm", "").replace("_", r"\_"), str(allfiles[name][0]), str(allfiles[name][1]),
579  *((r"\textbf{" if allfiles[name][2][method][0] == m else "") + str(allfiles[name][2][method][0]) + (r"}" if allfiles[name][2][method][0] == m else "") for method in METHOD_NAMES))) + r"\\" for name in circuits_sorted for m in (min(allfiles[name][2][method][0] for method in METHOD_NAMES),)))
580  markers = ["o", "*", "D", "s", "+", "<", ">", "v", "^"]
581  for j in range(4):
582  title = ["Total Partitions", "Runtime Performance", "Total Float Ops for Fusion", "Total Float Ops for Control-Aware Fusion"][j]
583  y_label = ["Partition Count", "Time in seconds", "Number of Float Ops", "Number of Float Ops"][j]
584  for i, strat in enumerate(METHOD_NAMES):
585  y = [allfiles[name][2][strat][j] for name in circuits_sorted]
586  plt.plot(circuits_labels, y, marker=markers[i], label=strat)
587  plt.xlabel("Circuit (sorted by qubits, gates)")
588  plt.ylabel(y_label, fontsize=6)
589  plt.title(f"{y_label} - Maximum k={max_qubits} Qubits per Partition")
590  plt.xticks(rotation=45, ha="right")
591  plt.legend()
592  plt.tight_layout()
593  plt.grid(True)
594  plt.savefig(f"{title.replace(' ', '_')}-{max_qubits}-max_qubit.svg", format="svg", transparent=True)
595  plt.clf()
596 
597 if __name__ == "__main__":
598  for max_qubits in range(3, 6):
599  pass #test_partitions(max_qubits)
600  test_simulation(5)
def purity_analysis()
Definition: benchmark.py:33
def projectq_import_qasm(filename, eng, initial_state=None)
Definition: benchmark.py:308
def get_qubits
Definition: tools.py:13
def test_simulation(max_qubits=4, random_initial_state=True)
Definition: benchmark.py:419
def PartitionCircuitQasm
Definition: partition.py:95
def preprocess_qasm_angles
Definition: benchmark.py:365
def test_partitions(max_qubits=4)
Definition: benchmark.py:527
Double-precision complex matrix (float64).
Definition: matrix.h:38
def qiskit_to_squander_name(qiskit_name)
Definition: tools.py:167
def state_vector_equivalence(psi, phi)
Definition: benchmark.py:407
def total_float_ops(num_qubit, max_qubits_per_partition, gate_to_qubit, gate_to_tqubit, allparts)
Definition: tools.py:86
def normalize_state(state)
Definition: benchmark.py:405