Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
utils.py
Go to the documentation of this file.
1 
3 """
4 Created on Tue Jun 30 15:44:26 2020
5 Copyright 2020 Peter Rakyta, Ph.D.
6 
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10 
11  http://www.apache.org/licenses/LICENSE-2.0
12 
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see http://www.gnu.org/licenses/.
21 
22 @author: Peter Rakyta, Ph.D.
23 """
24 
25 
27 
28 
29 import numpy as np
30 from squander.IO_interfaces import Qiskit_IO
31 from squander.gates.qgd_Circuit import qgd_Circuit as Circuit
32 from qiskit import QuantumCircuit
33 
34 import qiskit
35 
36 qiskit_version = qiskit.version.get_version_info()
37 
38 if qiskit_version[0] == "0":
39  from qiskit import Aer
40  from qiskit import execute
41 
42  if int(qiskit_version[2]) > 3:
43  from qiskit.quantum_info import Operator
44 else:
45  import qiskit_aer as Aer
46  from qiskit import transpile
47  from qiskit.quantum_info import Operator
48 
49 
50 def _tensor_perm_from_logical_to_physical(mapping, invert=False):
51  """Return tensor-axis permutation for a logical-to-physical qubit map."""
52  if invert:
53  from squander.synthesis.qgd_SABRE import qgd_SABRE
54 
55  mapping = qgd_SABRE.get_inverse_pi(mapping)
56 
57  qbit_num = len(mapping)
58  return [qbit_num - 1 - p for p in reversed(mapping)]
59 
60 
61 
63 def get_unitary_from_qiskit_circuit(circuit: QuantumCircuit):
64  """
65  Call to extract a unitary from Qiskit circuit
66 
67  Args:
68 
69  circuit (QuantumCircuit) A Qiskit circuit
70 
71  Return:
72 
73  Returns with the generated unitary
74 
75  """
76 
77  if qiskit_version[0] == "0":
78  backend = Aer.get_backend("aer_simulator")
79  circuit.save_unitary()
80 
81  # job execution and getting the result as an object
82  job = execute(circuit, backend)
83 
84  # the result of the Qiskit job
85  result = job.result()
86 
87  else:
88 
89  circuit.save_unitary()
90  backend = Aer.AerSimulator(method="unitary")
91 
92  compiled_circuit = transpile(circuit, backend)
93  result = backend.run(compiled_circuit).result()
94 
95  return np.asarray(result.get_unitary(circuit))
96 
97 
98 def get_unitary_from_qiskit_circuit_operator(circuit: QuantumCircuit):
99  """
100  Call to extract a unitary from Qiskit circuit using qiskit.quantum_info.Operator support
101 
102  Args:
103 
104  circuit (QuantumCircuit) A Qiskit circuit
105 
106  Return:
107 
108  Returns with the generated unitary
109 
110  """
111 
112  if qiskit_version[0] == "0" and int(qiskit_version[2]) < 4:
113 
114  print(
115  "Currently installed version of qiskit does not support extracting the unitary of a circuit via Operator. Using get_unitary_from_qiskit_circuit function instead."
116  )
117  return get_unitary_from_qiskit_circuit(circuit)
118 
119  return Operator(circuit).to_matrix()
120 
121 
122 def qasm_to_squander_circuit(filename: str, return_transpiled=False):
123  """
124  Converts a QASM file to a SQUANDER circuit
125 
126  Args:
127 
128  filename (str) The path to the QASM file
129 
130  Return:
131 
132  Returns with the SQUANDER circuit, the array of the corresponding parameters, and the transpiled Qiskit circuit
133  (None if not transpiled)
134  """
135 
136  qc = qiskit.QuantumCircuit.from_qasm_file(filename)
137  from squander.gates import gates_Wrapper as gate
138 
139  SUPPORTED_GATES_NAMES = {
140  n.lower().replace("cnot", "cx")
141  for n in dir(gate)
142  if not n.startswith("_")
143  and issubclass(getattr(gate, n), gate.Gate)
144  and n not in ("Gate", "CROT", "CR", "SYC")
145  }
146  if any(gate.operation.name not in SUPPORTED_GATES_NAMES for gate in qc.data):
147  qc_transpiled = qiskit.transpile(
148  qc, basis_gates=SUPPORTED_GATES_NAMES, optimization_level=0
149  )
150  else:
151  qc_transpiled = qc
152 
153  circuit_squander, circut_parameters = Qiskit_IO.convert_Qiskit_to_Squander(
154  qc_transpiled
155  )
156 
157  if return_transpiled:
158  return circuit_squander, circut_parameters, qc_transpiled
159 
160  return circuit_squander, circut_parameters, None
161 
162 
163 def CompareCircuits(
164  circ1: Circuit,
165  parameters1: np.ndarray,
166  circ2: Circuit,
167  parameters2: np.ndarray,
168  parallel: int = 1,
169  tolerance: float = 1e-10,
170  initial_mapping=None,
171  final_mapping=None,
172  is_f32: bool = False,
173 ):
174  """
175  Call to test if the two circuits give the same state transformation upon a random input state
176 
177 
178  Args:
179 
180  circ1 ( Circuit ) A circuit
181 
182  parameters1 ( np.ndarray ) A parameter array associated with the input circuit
183 
184  circ2 ( Circuit ) A circuit
185 
186  parameters2 ( np.ndarray ) A parameter array associated with the input circuit
187 
188  parallel (int, optional) Set 0 for sequential evaluation, 1 for using TBB parallelism or 2 for using openMP
189 
190  tolerance ( float, optional) The tolerance of the comparision when the inner product of the resulting states is matched to unity.
191 
192  is_f32 ( bool, optional) Use float32/complex64 state evolution.
193 
194 
195  Return:
196 
197  Returns with True if the two circuits give identical results.
198  """
199 
200  qbit_num1 = circ1.get_Qbit_Num()
201  qbit_num2 = circ2.get_Qbit_Num()
202 
203  if qbit_num1 != qbit_num2:
204  raise Exception(
205  "The two compared circuits should have the same number of qubits."
206  )
207 
208  if qbit_num1 > 31:
209  return # skip comparison for large qubit numbers, as the current implementation of Gates_block only supports up to 31 qubits. This is a temporary workaround and should be removed once the support for more qubits is implemented in Gates_block.
210 
211  matrix_size = 1 << qbit_num1
212  initial_state_real = np.random.uniform(-1.0, 1.0, (matrix_size,))
213  initial_state_imag = np.random.uniform(-1.0, 1.0, (matrix_size,))
214  initial_state = initial_state_real + initial_state_imag * 1j
215  norm = np.sum(
216  initial_state_real * initial_state_real
217  + initial_state_imag * initial_state_imag
218  )
219  initial_state = initial_state / np.sqrt(norm)
220  initial_state = initial_state.astype(np.complex64 if is_f32 else np.complex128)
221 
222  parameters1 = np.asarray(parameters1, dtype=np.float32 if is_f32 else np.float64)
223  parameters2 = np.asarray(parameters2, dtype=np.float32 if is_f32 else np.float64)
224 
225  transformed_state_1 = initial_state.copy()
226  transformed_state_2 = initial_state
227 
228  circ1.apply_to(
229  parameters1,
230  transformed_state_1,
231  parallel=parallel,
232  is_f32=is_f32,
233  )
234  if initial_mapping is not None:
236  initial_mapping,
237  invert=True,
238  )
239  transformed_state_2 = (
240  transformed_state_2.reshape([2] * qbit_num2)
241  .transpose(tensor_perm)
242  .copy()
243  .reshape((matrix_size,))
244  )
245  circ2.apply_to(
246  parameters2,
247  transformed_state_2,
248  parallel=parallel,
249  is_f32=is_f32,
250  )
251  if final_mapping is not None:
252  tensor_perm = _tensor_perm_from_logical_to_physical(final_mapping)
253  transformed_state_2 = (
254  transformed_state_2.reshape([2] * qbit_num2)
255  .transpose(tensor_perm)
256  .copy()
257  .reshape((matrix_size,))
258  )
259 
260  overlap = np.sum(transformed_state_1.conj() * transformed_state_2)
261  print("Circuit overlap: ", np.abs(overlap))
262 
263  assert (1 - np.abs(overlap)) < tolerance, 1 - np.abs(overlap)
264 
265 
266 def invert_circuit(circ: Circuit, parameters: np.ndarray):
267  """
268  Return the inverse (adjoint) of a SQUANDER circuit as a new SQUANDER circuit.
269 
270  All gate types are handled natively without going through Qiskit, including
271  CR, CROT, and SYC which are unsupported by Qiskit export.
272 
273  SYC has no parametric inverse in native form; it is first decomposed into the
274  CNOT basis and the resulting primitive circuit is then inverted.
275 
276  Args:
277 
278  circ ( Circuit ) A SQUANDER circuit to invert.
279  parameters ( np.ndarray ) Parameter array associated with the circuit.
280 
281  Return:
282 
283  Returns with a tuple (inv_circuit, inv_parameters) representing the
284  adjoint circuit and its parameter array.
285  """
286  from squander.gates.gates_Wrapper import (
287  H, X, Y, Z, S, Sdg, T, Tdg, SX, SXdg,
288  CH, CZ, CNOT, SWAP, CCX, CSWAP,
289  R, RX, RY, RZ, U1, U2, U3,
290  CRY, CRZ, CRX, CP, CU,
291  RXX, RYY, RZZ,
292  CR, CROT, SYC,
293  )
294 
295  inv_circuit = Circuit(circ.get_Qbit_Num())
296  inv_params = []
297 
298  gates = circ.get_Gates()
299 
300  for gate in reversed(gates):
301  gate_params = parameters[
302  gate.get_Parameter_Start_Index():
303  gate.get_Parameter_Start_Index() + gate.get_Parameter_Num()
304  ]
305 
306  # ------------------------------------------------------------------ #
307  # Sub-circuit: recurse and inline #
308  # ------------------------------------------------------------------ #
309  if isinstance(gate, Circuit):
310  sub_inv, sub_inv_params = invert_circuit(gate, gate_params)
311  for sub_gate in sub_inv.get_Gates():
312  sub_g_params = sub_inv_params[
313  sub_gate.get_Parameter_Start_Index():
314  sub_gate.get_Parameter_Start_Index() + sub_gate.get_Parameter_Num()
315  ]
316  inv_circuit.add_Gate(sub_gate)
317  if sub_gate.get_Parameter_Num() > 0:
318  inv_params.append(sub_g_params)
319 
320  # ------------------------------------------------------------------ #
321  # Self-inverse gates (no parameter change needed) #
322  # ------------------------------------------------------------------ #
323  elif isinstance(gate, CNOT):
324  inv_circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
325  elif isinstance(gate, CZ):
326  inv_circuit.add_CZ(gate.get_Target_Qbit(), gate.get_Control_Qbit())
327  elif isinstance(gate, CH):
328  inv_circuit.add_CH(gate.get_Target_Qbit(), gate.get_Control_Qbit())
329  elif isinstance(gate, H):
330  inv_circuit.add_H(gate.get_Target_Qbit())
331  elif isinstance(gate, X):
332  inv_circuit.add_X(gate.get_Target_Qbit())
333  elif isinstance(gate, Y):
334  inv_circuit.add_Y(gate.get_Target_Qbit())
335  elif isinstance(gate, Z):
336  inv_circuit.add_Z(gate.get_Target_Qbit())
337  elif isinstance(gate, SWAP):
338  inv_circuit.add_SWAP(gate.get_Target_Qbits())
339  elif isinstance(gate, CCX):
340  inv_circuit.add_CCX(gate.get_Target_Qbit(), gate.get_Control_Qbits())
341  elif isinstance(gate, CSWAP):
342  inv_circuit.add_CSWAP(gate.get_Target_Qbits(), gate.get_Control_Qbits())
343 
344  # ------------------------------------------------------------------ #
345  # Gates whose inverse swaps S↔Sdg, T↔Tdg, SX↔SXdg #
346  # ------------------------------------------------------------------ #
347  elif isinstance(gate, S):
348  inv_circuit.add_Sdg(gate.get_Target_Qbit())
349  elif isinstance(gate, Sdg):
350  inv_circuit.add_S(gate.get_Target_Qbit())
351  elif isinstance(gate, T):
352  inv_circuit.add_Tdg(gate.get_Target_Qbit())
353  elif isinstance(gate, Tdg):
354  inv_circuit.add_T(gate.get_Target_Qbit())
355  elif isinstance(gate, SX):
356  inv_circuit.add_SXdg(gate.get_Target_Qbit())
357  elif isinstance(gate, SXdg):
358  inv_circuit.add_SX(gate.get_Target_Qbit())
359 
360  # ------------------------------------------------------------------ #
361  # Rotation gates: negate the rotation angle (first parameter) #
362  # ------------------------------------------------------------------ #
363  elif isinstance(gate, R):
364  inv_circuit.add_R(gate.get_Target_Qbit())
365  inv_params.append(np.array([-gate_params[0], gate_params[1]], dtype=gate_params.dtype))
366  elif isinstance(gate, RX):
367  inv_circuit.add_RX(gate.get_Target_Qbit())
368  inv_params.append(-gate_params)
369  elif isinstance(gate, RY):
370  inv_circuit.add_RY(gate.get_Target_Qbit())
371  inv_params.append(-gate_params)
372  elif isinstance(gate, RZ):
373  inv_circuit.add_RZ(gate.get_Target_Qbit())
374  inv_params.append(-gate_params)
375  elif isinstance(gate, U1):
376  inv_circuit.add_U1(gate.get_Target_Qbit())
377  inv_params.append(-gate_params)
378  elif isinstance(gate, U2):
379  # U2(φ,λ)† = U2(-λ-π, -φ+π) (up to global phase U2 inverse is U(-π/2,-λ,-φ))
380  inv_circuit.add_U2(gate.get_Target_Qbit())
381  inv_params.append(np.array([-gate_params[1] - np.pi, -gate_params[0] + np.pi], dtype=gate_params.dtype))
382  elif isinstance(gate, U3):
383  # U3(θ,φ,λ)† = U3(-θ,-λ,-φ)
384  inv_circuit.add_U3(gate.get_Target_Qbit())
385  inv_params.append(np.array([-gate_params[0], -gate_params[2], -gate_params[1]], dtype=gate_params.dtype))
386  elif isinstance(gate, CU):
387  # CU(θ,φ,λ,γ)† = CU(-θ,-λ,-φ,-γ)
388  inv_circuit.add_CU(gate.get_Target_Qbit(), gate.get_Control_Qbit())
389  inv_params.append(np.array([-gate_params[0], -gate_params[2], -gate_params[1], -gate_params[3]], dtype=gate_params.dtype))
390  elif isinstance(gate, CRY):
391  inv_circuit.add_CRY(gate.get_Target_Qbit(), gate.get_Control_Qbit())
392  inv_params.append(-gate_params)
393  elif isinstance(gate, CRZ):
394  inv_circuit.add_CRZ(gate.get_Target_Qbit(), gate.get_Control_Qbit())
395  inv_params.append(-gate_params)
396  elif isinstance(gate, CRX):
397  inv_circuit.add_CRX(gate.get_Target_Qbit(), gate.get_Control_Qbit())
398  inv_params.append(-gate_params)
399  elif isinstance(gate, CP):
400  inv_circuit.add_CP(gate.get_Target_Qbit(), gate.get_Control_Qbit())
401  inv_params.append(-gate_params)
402  elif isinstance(gate, RXX):
403  inv_circuit.add_RXX(gate.get_Target_Qbits())
404  inv_params.append(-gate_params)
405  elif isinstance(gate, RYY):
406  inv_circuit.add_RYY(gate.get_Target_Qbits())
407  inv_params.append(-gate_params)
408  elif isinstance(gate, RZZ):
409  inv_circuit.add_RZZ(gate.get_Target_Qbits())
410  inv_params.append(-gate_params)
411 
412  # ------------------------------------------------------------------ #
413  # CR and CROT: inverse is the same gate with negated theta #
414  # CR†(θ,φ) = CR(-θ,φ) #
415  # CROT†(θ,φ) = CROT(-θ,φ) #
416  # ------------------------------------------------------------------ #
417  elif isinstance(gate, CR):
418  inv_circuit.add_CR(gate.get_Target_Qbit(), gate.get_Control_Qbit())
419  inv_params.append(np.array([-gate_params[0], gate_params[1]], dtype=gate_params.dtype))
420  elif isinstance(gate, CROT):
421  inv_circuit.add_CROT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
422  inv_params.append(np.array([-gate_params[0], gate_params[1]], dtype=gate_params.dtype))
423 
424  # ------------------------------------------------------------------ #
425  # SYC: no parametric form for SYC†; decompose into CNOT basis then #
426  # invert the primitive decomposition, inlining the resulting gates #
427  # ------------------------------------------------------------------ #
428  elif isinstance(gate, SYC):
429  syc_single = Circuit(circ.get_Qbit_Num())
430  syc_single.add_SYC(gate.get_Target_Qbit(), gate.get_Control_Qbit())
431  syc_cnot, syc_cnot_params = circuit_to_CNOT_basis(syc_single, np.array([]))
432  syc_inv, syc_inv_params = invert_circuit(syc_cnot, syc_cnot_params)
433  # Inline all gates from the inverted decomposition
434  for sub_gate in syc_inv.get_Gates():
435  sub_params = syc_inv_params[
436  sub_gate.get_Parameter_Start_Index():
437  sub_gate.get_Parameter_Start_Index() + sub_gate.get_Parameter_Num()
438  ]
439  inv_circuit.add_Gate(sub_gate)
440  if sub_gate.get_Parameter_Num() > 0:
441  inv_params.append(sub_params)
442 
443  else:
444  raise ValueError(f"invert_circuit: unsupported gate type {type(gate).__name__}")
445 
446  if inv_params:
447  inv_parameters = np.concatenate([
448  p if isinstance(p, np.ndarray) else np.asarray(p) for p in inv_params
449  ])
450  else:
451  inv_parameters = np.array([], dtype=parameters.dtype if len(parameters) > 0 else np.float64)
452 
453  return inv_circuit, inv_parameters
454 
455 
456 def circuit_to_CNOT_basis(circ: Circuit, parameters: np.ndarray):
457  """
458  Call to transpile a SQUANDER circuit to CNOT basis
459 
460 
461  Args:
462 
463  circ ( Circuit ) A circuit
464 
465  parameters ( np.ndarray ) A parameter array associated with the input circuit
466 
467 
468  Return:
469 
470  Returns with the transpiled circuit and the associated parameters
471  """
472  from squander.gates.gates_Wrapper import (
473  CH,
474  CZ,
475  SYC,
476  CRY,
477  CU,
478  CR,
479  CROT,
480  CCX,
481  CSWAP,
482  SWAP,
483  CRX,
484  CRZ,
485  CP,
486  RXX,
487  RYY,
488  RZZ,
489  )
490 
491  gates = circ.get_Gates()
492  circuit = Circuit(circ.get_Qbit_Num())
493  params = []
494  for gate in gates:
495  if isinstance(gate, Circuit):
496  subcircuit, subparams = circuit_to_CNOT_basis(
497  gate,
498  parameters[
499  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
500  + gate.get_Parameter_Num()
501  ],
502  )
503  circuit.add_Gate(subcircuit)
504  params.append(subparams)
505  elif isinstance(gate, CH):
506  circuit.add_RY(gate.get_Target_Qbit())
507  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
508  circuit.add_RY(gate.get_Target_Qbit())
509  params.append([np.pi / 4 / 2, -np.pi / 4 / 2])
510  elif isinstance(gate, CZ):
511  circuit.add_H(gate.get_Target_Qbit())
512  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
513  circuit.add_H(gate.get_Target_Qbit())
514  params.append([])
515  elif isinstance(gate, SYC):
516  circuit.add_U1(gate.get_Target_Qbit())
517  circuit.add_U1(gate.get_Control_Qbit())
518  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
519  circuit.add_U1(gate.get_Target_Qbit())
520  circuit.add_CNOT(gate.get_Control_Qbit(), gate.get_Target_Qbit())
521  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
522  params.append([-np.pi / 12, -np.pi / 12, -5 * np.pi / 12])
523  elif isinstance(gate, CRY):
524  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
525  circuit.add_RY(gate.get_Target_Qbit())
526  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
527  circuit.add_RY(gate.get_Target_Qbit())
528  (theta,) = parameters[
529  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
530  + gate.get_Parameter_Num()
531  ]
532  params.append([-theta / 2, theta / 2])
533  elif isinstance(gate, CU):
534  circuit.add_U1(gate.get_Control_Qbit())
535  circuit.add_RZ(gate.get_Target_Qbit())
536  circuit.add_RY(gate.get_Target_Qbit())
537  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
538  circuit.add_RY(gate.get_Target_Qbit())
539  circuit.add_RZ(gate.get_Target_Qbit())
540  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
541  circuit.add_RZ(gate.get_Target_Qbit())
542  theta, phi, lbda, gamma = parameters[
543  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
544  + gate.get_Parameter_Num()
545  ]
546  params.append(
547  [
548  (lbda + phi) / 2 + gamma,
549  lbda / 2,
550  theta / 2,
551  -theta / 2,
552  -(phi + lbda) / 2 / 2,
553  (phi - lbda) / 2 / 2,
554  ]
555  )
556  elif isinstance(gate, CR):
557  circuit.add_RZ(gate.get_Target_Qbit())
558  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
559  circuit.add_RY(gate.get_Target_Qbit())
560  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
561  circuit.add_RY(gate.get_Target_Qbit())
562  circuit.add_RZ(gate.get_Target_Qbit())
563  theta, phi = parameters[
564  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
565  + gate.get_Parameter_Num()
566  ]
567  params.append(
568  [(-phi + np.pi / 2) / 2, -theta / 2, theta / 2, (phi - np.pi / 2) / 2]
569  )
570  elif isinstance(gate, CROT):
571  circuit.add_RZ(gate.get_Target_Qbit())
572  circuit.add_RY(gate.get_Target_Qbit())
573  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
574  circuit.add_RZ(gate.get_Target_Qbit())
575  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
576  circuit.add_RY(gate.get_Target_Qbit())
577  circuit.add_RZ(gate.get_Target_Qbit())
578  theta, phi = parameters[
579  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
580  + gate.get_Parameter_Num()
581  ]
582  params.append([-phi / 2, np.pi / 2 / 2, -theta, -np.pi / 2 / 2, phi / 2])
583  elif isinstance(gate, CRX):
584  circuit.add_H(gate.get_Target_Qbit())
585  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
586  circuit.add_RZ(gate.get_Target_Qbit())
587  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
588  circuit.add_RZ(gate.get_Target_Qbit())
589  circuit.add_H(gate.get_Target_Qbit())
590  (theta,) = parameters[
591  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
592  + gate.get_Parameter_Num()
593  ]
594  params.append([-theta / 2, theta / 2])
595  elif isinstance(gate, CRZ):
596  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
597  circuit.add_RZ(gate.get_Target_Qbit())
598  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
599  circuit.add_RZ(gate.get_Target_Qbit())
600  (theta,) = parameters[
601  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
602  + gate.get_Parameter_Num()
603  ]
604  params.append([-theta / 2, theta / 2])
605  elif isinstance(gate, CP):
606  circuit.add_U1(gate.get_Target_Qbit())
607  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
608  circuit.add_U1(gate.get_Target_Qbit())
609  circuit.add_CNOT(gate.get_Target_Qbit(), gate.get_Control_Qbit())
610  circuit.add_U1(gate.get_Control_Qbit())
611  (phi,) = parameters[
612  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
613  + gate.get_Parameter_Num()
614  ]
615  params.append([phi / 2, -phi / 2, phi / 2])
616  elif isinstance(gate, CCX):
617  c1, c2 = gate.get_Control_Qbits()
618  circuit.add_CNOT(c1, c2)
619  circuit.add_Tdg(c1)
620  circuit.add_T(c2)
621  circuit.add_CNOT(c1, c2)
622  circuit.add_H(gate.get_Target_Qbit())
623  circuit.add_T(gate.get_Target_Qbit())
624  circuit.add_T(c1)
625  circuit.add_CNOT(gate.get_Target_Qbit(), c2)
626  circuit.add_Tdg(gate.get_Target_Qbit())
627  circuit.add_CNOT(gate.get_Target_Qbit(), c1)
628  circuit.add_T(gate.get_Target_Qbit())
629  circuit.add_CNOT(gate.get_Target_Qbit(), c2)
630  circuit.add_Tdg(gate.get_Target_Qbit())
631  circuit.add_CNOT(gate.get_Target_Qbit(), c1)
632  circuit.add_H(gate.get_Target_Qbit())
633  params.append([])
634  elif isinstance(gate, CSWAP):
635  t1, t2 = gate.get_Target_Qbits()
636  (c,) = gate.get_Control_Qbits()
637  circuit.add_CNOT(t2, t1)
638  circuit.add_CNOT(t2, c)
639  circuit.add_H(t1)
640  circuit.add_Tdg(t2)
641  circuit.add_T(c)
642  circuit.add_T(t1)
643  circuit.add_CNOT(t2, c)
644  circuit.add_CNOT(t1, c)
645  circuit.add_Tdg(t1)
646  circuit.add_T(t2)
647  circuit.add_CNOT(t1, t2)
648  circuit.add_T(t1)
649  circuit.add_CNOT(t1, c)
650  circuit.add_T(t1)
651  circuit.add_SX(t1)
652  circuit.add_Sdg(t2)
653  circuit.add_CNOT(t2, t1)
654  circuit.add_S(t2)
655  """
656  circuit.add_CNOT(t2, t1)
657  circuit.add_CNOT(t2, c)
658  circuit.add_Tdg(t2)
659  circuit.add_T(c)
660  circuit.add_CNOT(t2, c)
661  circuit.add_H(t1)
662  circuit.add_T(t1)
663  circuit.add_T(t2)
664  circuit.add_CNOT(t1, c)
665  circuit.add_Tdg(t1)
666  circuit.add_CNOT(t1, t2)
667  circuit.add_T(t1)
668  circuit.add_CNOT(t1, c)
669  circuit.add_Tdg(t1)
670  circuit.add_CNOT(t1, t2)
671  circuit.add_H(t1)
672  circuit.add_CNOT(t2, t1)
673  """
674  params.append([])
675  elif isinstance(gate, SWAP):
676  t1, t2 = gate.get_Target_Qbits()
677  circuit.add_CNOT(t1, t2)
678  circuit.add_CNOT(t2, t1)
679  circuit.add_CNOT(t1, t2)
680  params.append([])
681  elif isinstance(gate, RXX):
682  t1, t2 = gate.get_Target_Qbits()
683  circuit.add_CNOT(t1, t2)
684  circuit.add_RX(t2)
685  circuit.add_CNOT(t1, t2)
686  (theta,) = parameters[
687  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
688  + gate.get_Parameter_Num()
689  ]
690  params.append([theta])
691  elif isinstance(gate, RYY):
692  t1, t2 = gate.get_Target_Qbits()
693  circuit.add_RX(t1)
694  circuit.add_RX(t2)
695  circuit.add_CNOT(t1, t2)
696  circuit.add_RZ(t1)
697  circuit.add_CNOT(t1, t2)
698  circuit.add_RX(t1)
699  circuit.add_RX(t2)
700  (theta,) = parameters[
701  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
702  + gate.get_Parameter_Num()
703  ]
704  params.append([np.pi/2/2, np.pi/2/2, theta, -np.pi/2/2, -np.pi/2/2])
705  elif isinstance(gate, RZZ):
706  t1, t2 = gate.get_Target_Qbits()
707  circuit.add_CNOT(t1, t2)
708  circuit.add_RZ(t1)
709  circuit.add_CNOT(t1, t2)
710  (theta,) = parameters[
711  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
712  + gate.get_Parameter_Num()
713  ]
714  params.append([theta])
715  else:
716  circuit.add_Gate(gate)
717  params.append(
718  parameters[
719  gate.get_Parameter_Start_Index() : gate.get_Parameter_Start_Index()
720  + gate.get_Parameter_Num()
721  ]
722  )
723 
724  return circuit, np.concatenate(params)
725 
726 
728  circ1 = Circuit(2)
729  circ1.add_CH(0, 1)
730  circ1.add_CZ(0, 1)
731  circ1.add_CRY(0, 1)
732  circ1.add_SYC(0, 1)
733  circ1.add_CR(0, 1)
734  circ1.add_CROT(0, 1)
735  circ1.add_CU(0, 1)
736  circ1.add_CP(0, 1)
737  circ1.add_CRX(0, 1)
738  circ1.add_CRZ(0, 1)
739  circ1.add_SWAP([0, 1])
740  circ1.add_RXX(0, 1)
741  circ1.add_RYY(0, 1)
742  circ1.add_RZZ(0, 1)
743 
744  paramcount1 = 0 + 0 + 1 + 0 + 2 + 2 + 4 + 1 + 1 + 1 + 0 + 1 + 1 + 1
745  circ2 = Circuit(3)
746  circ2.add_CCX(0, [1, 2])
747  circ2.add_CSWAP([0, 1], 2)
748  paramcount2 = 0 + 0
749  for circ, paramcount in [(circ1, paramcount1), (circ2, paramcount2)]:
750  params = np.random.rand(paramcount) * 2 * np.pi
751  newcirc, newparams = circuit_to_CNOT_basis(circ, params)
752  Umat = np.eye(1 << circ.get_Qbit_Num(), dtype=np.complex128)
753  Umatnew = np.eye(1 << newcirc.get_Qbit_Num(), dtype=np.complex128)
754  circ.apply_to(params, Umat)
755  newcirc.apply_to(newparams, Umatnew)
756  # phase = np.angle(np.linalg.det(Umat @ np.linalg.inv(Umatnew)))
757  phase = np.angle((Umatnew @ Umat.conj().T)[0, 0])
758  # Normalize one matrix
759  Umatnew = Umatnew * np.exp(-1j * phase)
760  # Check closeness
761  assert np.allclose(Umat, Umatnew), (Umat, Umatnew)
762 
763 
764 # test_circuit_to_CNOT_basis()
def qasm_to_squander_circuit
Definition: utils.py:122
def get_unitary_from_qiskit_circuit_operator
Definition: utils.py:98
def _tensor_perm_from_logical_to_physical(mapping, invert=False)
Definition: utils.py:50
def invert_circuit
Definition: utils.py:266
def get_unitary_from_qiskit_circuit
Call to retrieve the unitary from QISKIT circuit.
Definition: utils.py:63
def circuit_to_CNOT_basis
Definition: utils.py:456
def test_circuit_to_CNOT_basis()
Definition: utils.py:727
def CompareCircuits
Definition: utils.py:164