Source code for qiskit.synthesis.cnotdihedral.cnotdihedral_decompose_general

# This code is part of Qiskit.
# (C) Copyright IBM 2019, 2021.
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
Circuit synthesis for the CNOTDihedral class.

import numpy as np
from qiskit.exceptions import QiskitError
from qiskit.circuit import QuantumCircuit

[docs]def synth_cnotdihedral_general(elem): """Decompose a CNOTDihedral element into a QuantumCircuit. Decompose a general CNOTDihedral elements. The number of CNOT gates is not necessarily optimal. For a decomposition of a 1-qubit or 2-qubit element, call synth_cnotdihedral_two_qubits. Args: elem (CNOTDihedral): a CNOTDihedral element. Return: QuantumCircuit: a circuit implementation of the CNOTDihedral element. Raises: QiskitError: if the element could not be decomposed into a circuit. Reference: 1. Andrew W. Cross, Easwar Magesan, Lev S. Bishop, John A. Smolin and Jay M. Gambetta, *Scalable randomised benchmarking of non-Clifford gates*, npj Quantum Inf 2, 16012 (2016). """ num_qubits = elem.num_qubits circuit = QuantumCircuit(num_qubits) # Make a copy of the CNOTDihedral element as we are going to # reduce it to an identity elem_cpy = elem.copy() if not np.allclose((np.linalg.det(elem_cpy.linear) % 2), 1): raise QiskitError("Linear part is not invertible.") # Do x gate for each qubit i where shift[i]=1 for i in range(num_qubits): if elem.shift[i]: circuit.x(i) elem_cpy._append_x(i) # Do Gauss elimination on the linear part by adding cx gates for i in range(num_qubits): # set i-th element to be 1 if not elem_cpy.linear[i][i]: for j in range(i + 1, num_qubits): if elem_cpy.linear[j][i]: # swap qubits i and j, i), j), i) elem_cpy._append_cx(j, i) elem_cpy._append_cx(i, j) elem_cpy._append_cx(j, i) break # make all the other elements in column i zero for j in range(num_qubits): if j != i: if elem_cpy.linear[j][i]:, j) elem_cpy._append_cx(i, j) if ( not (elem_cpy.shift == np.zeros(num_qubits)).all() or not (elem_cpy.linear == np.eye(num_qubits)).all() ): raise QiskitError("Cannot do Gauss elimination on linear part.") # Initialize new_elem to an identity CNOTDihderal element new_elem = elem_cpy.copy() new_elem.poly.weight_0 = 0 new_elem.poly.weight_1 = np.zeros(num_qubits, dtype=np.int8) new_elem.poly.weight_2 = np.zeros(int(num_qubits * (num_qubits - 1) / 2), dtype=np.int8) new_elem.poly.weight_3 = np.zeros( int(num_qubits * (num_qubits - 1) * (num_qubits - 2) / 6), dtype=np.int8 ) new_circuit = QuantumCircuit(num_qubits) # Do cx and phase gates to construct all monomials of weight 3 for i in range(num_qubits): for j in range(i + 1, num_qubits): for k in range(j + 1, num_qubits): if elem_cpy.poly.get_term([i, j, k]) != 0: new_elem._append_cx(i, k) new_elem._append_cx(j, k) new_elem._append_phase(1, k) new_elem._append_cx(i, k) new_elem._append_cx(j, k), k), k) new_circuit.p((np.pi / 4), [k]), k), k) # Do cx and phase gates to construct all monomials of weight 2 for i in range(num_qubits): for j in range(i + 1, num_qubits): tpow1 = elem_cpy.poly.get_term([i, j]) tpow2 = new_elem.poly.get_term([i, j]) tpow = ((tpow2 - tpow1) / 2) % 4 if tpow != 0: new_elem._append_cx(i, j) new_elem._append_phase(tpow, j) new_elem._append_cx(i, j), j) new_circuit.p((tpow * np.pi / 4), [j]), j) # Do phase gates to construct all monomials of weight 1 for i in range(num_qubits): tpow1 = elem_cpy.poly.get_term([i]) tpow2 = new_elem.poly.get_term([i]) tpow = (tpow1 - tpow2) % 8 if tpow != 0: new_elem._append_phase(tpow, i) new_circuit.p((tpow * np.pi / 4), [i]) if elem.poly != new_elem.poly: raise QiskitError("Could not recover phase polynomial.") inv_circuit = circuit.inverse() return new_circuit.compose(inv_circuit)