def test_substituting_node_preserves_args_condition(self, inplace):
        """Verify args and condition are preserved by a substitution."""
        dag = DAGCircuit()
        qr = QuantumRegister(2)
        cr = ClassicalRegister(1)
        dag.add_qreg(qr)
        dag.add_creg(cr)
        dag.apply_operation_back(HGate(), [qr[1]])
        cx_gate = CXGate()
        cx_gate.condition = (cr, 1)
        node_to_be_replaced = dag.apply_operation_back(cx_gate, [qr[1], qr[0]])

        dag.apply_operation_back(HGate(), [qr[1]])

        replacement_node = dag.substitute_node(node_to_be_replaced,
                                               CZGate(),
                                               inplace=inplace)

        raise_if_dagcircuit_invalid(dag)
        self.assertEqual(replacement_node.name, 'cz')
        self.assertEqual(replacement_node.qargs, [qr[1], qr[0]])
        self.assertEqual(replacement_node.cargs, [])
        self.assertEqual(replacement_node.condition, (cr, 1))

        self.assertEqual(replacement_node is node_to_be_replaced, inplace)
 def test_dag_collect_runs(self):
     """Test the collect_runs method with 3 different gates."""
     self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0])
     self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0])
     self.dag.apply_operation_back(U1Gate(3.14), [self.qubit0])
     self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1])
     self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2])
     self.dag.apply_operation_back(HGate(), [self.qubit2])
     collected_runs = self.dag.collect_runs(['u1', 'cx', 'h'])
     self.assertEqual(len(collected_runs), 3)
     for run in collected_runs:
         if run[0].name == 'cx':
             self.assertEqual(len(run), 2)
             self.assertEqual(['cx'] * 2, [x.name for x in run])
             self.assertEqual(
                 [[self.qubit2, self.qubit1], [self.qubit1, self.qubit2]],
                 [x.qargs for x in run])
         elif run[0].name == 'h':
             self.assertEqual(len(run), 1)
             self.assertEqual(['h'], [x.name for x in run])
             self.assertEqual([[self.qubit2]], [x.qargs for x in run])
         elif run[0].name == 'u1':
             self.assertEqual(len(run), 3)
             self.assertEqual(['u1'] * 3, [x.name for x in run])
             self.assertEqual([[self.qubit0], [self.qubit0], [self.qubit0]],
                              [x.qargs for x in run])
         else:
             self.fail('Unknown run encountered')
Exemple #3
0
    def test_topological_nodes(self):
        """The topological_nodes() method"""
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], [])
        self.dag.apply_operation_back(HGate(), [self.qubit2], [])

        named_nodes = self.dag.topological_nodes()

        expected = [('qr[0]', []),
                    ('qr[1]', []),
                    ('cx', [self.qubit0, self.qubit1]),
                    ('h', [self.qubit0]),
                    ('qr[2]', []),
                    ('cx', [self.qubit2, self.qubit1]),
                    ('cx', [self.qubit0, self.qubit2]),
                    ('h', [self.qubit2]),
                    ('qr[0]', []),
                    ('qr[1]', []),
                    ('qr[2]', []),
                    ('cr[0]', []),
                    ('cr[0]', []),
                    ('cr[1]', []),
                    ('cr[1]', [])]
        self.assertEqual(expected, [(i.name, i.qargs) for i in named_nodes])
Exemple #4
0
    def _multiplex(self, target_gate, list_of_angles, last_cnot=True):
        """
        Return a recursive implementation of a multiplexor circuit,
        where each instruction itself has a decomposition based on
        smaller multiplexors.

        The LSB is the multiplexor "data" and the other bits are multiplexor "select".

        Args:
            target_gate (Gate): Ry or Rz gate to apply to target qubit, multiplexed
                over all other "select" qubits
            list_of_angles (list[float]): list of rotation angles to apply Ry and Rz
            last_cnot (bool): add the last cnot if last_cnot = True

        Returns:
            DAGCircuit: the circuit implementing the multiplexor's action
        """
        list_len = len(list_of_angles)
        local_num_qubits = int(math.log2(list_len)) + 1

        q = QuantumRegister(local_num_qubits)
        circuit = QuantumCircuit(q, name="multiplex" + local_num_qubits.__str__())

        lsb = q[0]
        msb = q[local_num_qubits - 1]

        # case of no multiplexing: base case for recursion
        if local_num_qubits == 1:
            circuit.append(target_gate(list_of_angles[0]), [q[0]])
            return circuit

        # calc angle weights, assuming recursion (that is the lower-level
        # requested angles have been correctly implemented by recursion
        angle_weight = np.kron([[0.5, 0.5], [0.5, -0.5]],
                               np.identity(2 ** (local_num_qubits - 2)))

        # calc the combo angles
        list_of_angles = angle_weight.dot(np.array(list_of_angles)).tolist()

        # recursive step on half the angles fulfilling the above assumption
        multiplex_1 = self._multiplex(target_gate, list_of_angles[0:(list_len // 2)], False)
        circuit.append(multiplex_1.to_instruction(), q[0:-1])

        # attach CNOT as follows, thereby flipping the LSB qubit
        circuit.append(CXGate(), [msb, lsb])

        # implement extra efficiency from the paper of cancelling adjacent
        # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the
        # second lower-level multiplex)
        multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2):], False)
        if list_len > 1:
            circuit.append(multiplex_2.to_instruction().reverse_ops(), q[0:-1])
        else:
            circuit.append(multiplex_2.to_instruction(), q[0:-1])

        # attach a final CNOT
        if last_cnot:
            circuit.append(CXGate(), [msb, lsb])

        return circuit
    def test_remove_op_node_longer(self):
        """Test remove_op_node method in a "longer" dag"""
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1])
        self.dag.apply_operation_back(HGate(), [self.qubit0])
        self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2])
        self.dag.apply_operation_back(HGate(), [self.qubit2])

        op_nodes = list(self.dag.topological_op_nodes())
        self.dag.remove_op_node(op_nodes[0])

        expected = [('h', [self.qubit0]), ('cx', [self.qubit2, self.qubit1]),
                    ('cx', [self.qubit0, self.qubit2]), ('h', [self.qubit2])]
        self.assertEqual(expected, [(i.name, i.qargs)
                                    for i in self.dag.topological_op_nodes()])
    def test_layers_basic(self):
        """The layers() method returns a list of layers, each of them with a list of nodes."""
        qreg = QuantumRegister(2, 'qr')
        creg = ClassicalRegister(2, 'cr')
        qubit0 = qreg[0]
        qubit1 = qreg[1]
        clbit0 = creg[0]
        clbit1 = creg[1]
        x_gate = XGate()
        x_gate.condition = (creg, 3)
        dag = DAGCircuit()
        dag.add_qreg(qreg)
        dag.add_creg(creg)
        dag.apply_operation_back(HGate(), [qubit0], [])
        dag.apply_operation_back(CXGate(), [qubit0, qubit1], [])
        dag.apply_operation_back(Measure(), [qubit1, clbit1], [])
        dag.apply_operation_back(x_gate, [qubit1], [])
        dag.apply_operation_back(Measure(), [qubit0, clbit0], [])
        dag.apply_operation_back(Measure(), [qubit1, clbit1], [])

        layers = list(dag.layers())
        self.assertEqual(5, len(layers))

        name_layers = [[
            node.op.name for node in layer["graph"].nodes()
            if node.type == "op"
        ] for layer in layers]

        self.assertEqual(
            [['h'], ['cx'], ['measure'], ['x'], ['measure', 'measure']],
            name_layers)
    def test_instructions_equal(self):
        """Test equality of two instructions."""
        hop1 = Instruction('h', 1, 0, [])
        hop2 = Instruction('s', 1, 0, [])
        hop3 = Instruction('h', 1, 0, [])

        self.assertFalse(hop1 == hop2)
        self.assertTrue(hop1 == hop3)

        uop1 = Instruction('u', 1, 0, [0.4, 0.5, 0.5])
        uop2 = Instruction('u', 1, 0, [0.4, 0.6, 0.5])
        uop3 = Instruction('v', 1, 0, [0.4, 0.5, 0.5])
        uop4 = Instruction('u', 1, 0, [0.4, 0.5, 0.5])

        self.assertFalse(uop1 == uop2)
        self.assertTrue(uop1 == uop4)
        self.assertFalse(uop1 == uop3)

        self.assertTrue(HGate() == HGate())
        self.assertFalse(HGate() == CXGate())
        self.assertFalse(hop1 == HGate())

        eop1 = Instruction('kraus', 1, 0, [np.array([[1, 0], [0, 1]])])
        eop2 = Instruction('kraus', 1, 0, [np.array([[0, 1], [1, 0]])])
        eop3 = Instruction('kraus', 1, 0, [np.array([[1, 0], [0, 1]])])
        eop4 = Instruction('kraus', 1, 0, [np.eye(4)])

        self.assertTrue(eop1 == eop3)
        self.assertFalse(eop1 == eop2)
        self.assertFalse(eop1 == eop4)
    def test_quantum_successors(self):
        """The method dag.quantum_successors() returns successors connected by quantum edges"""

        # q_0: |0>─────■───|0>─
        #         ┌─┐┌─┴─┐
        # q_1: |0>┤M├┤ X ├─────
        #         └╥┘└───┘
        #  c_0: 0 ═╬═══════════
        #          ║
        #  c_1: 0 ═╩═══════════

        self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1],
                                      [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])

        successor_measure = self.dag.quantum_successors(
            self.dag.named_nodes('measure').pop())
        cnot_node = next(successor_measure)
        with self.assertRaises(StopIteration):
            next(successor_measure)

        self.assertIsInstance(cnot_node.op, CXGate)

        successor_cnot = self.dag.quantum_successors(cnot_node)
        # Ordering between Reset and out[q1] is indeterminant.

        successor1 = next(successor_cnot)
        successor2 = next(successor_cnot)
        with self.assertRaises(StopIteration):
            next(successor_cnot)

        self.assertTrue(
            (successor1.type == 'out' and isinstance(successor2.op, Reset))
            or (successor2.type == 'out' and isinstance(successor1.op, Reset)))
Exemple #9
0
 def _process_cnot(self, node):
     """Process a CNOT gate node."""
     id0 = self._process_bit_id(node.children[0])
     id1 = self._process_bit_id(node.children[1])
     if not (len(id0) == len(id1) or len(id0) == 1 or len(id1) == 1):
         raise QiskitError("internal error: qreg size mismatch",
                           "line=%s" % node.line, "file=%s" % node.file)
     maxidx = max([len(id0), len(id1)])
     for idx in range(maxidx):
         cx_gate = CXGate()
         cx_gate.condition = self.condition
         if len(id0) > 1 and len(id1) > 1:
             self.dag.apply_operation_back(cx_gate, [id0[idx], id1[idx]], [])
         elif len(id0) > 1:
             self.dag.apply_operation_back(cx_gate, [id0[idx], id1[0]], [])
         else:
             self.dag.apply_operation_back(cx_gate, [id0[0], id1[idx]], [])
    def test_get_named_nodes(self):
        """The get_named_nodes(AName) method returns all the nodes with name AName"""
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit2, self.qubit1], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit2], [])
        self.dag.apply_operation_back(HGate(), [self.qubit2], [])

        # The ordering is not assured, so we only compare the output (unordered) sets.
        # We use tuples because lists aren't hashable.
        named_nodes = self.dag.named_nodes('cx')

        node_qargs = {tuple(node.qargs) for node in named_nodes}

        expected_qargs = {(self.qubit0, self.qubit1),
                          (self.qubit2, self.qubit1),
                          (self.qubit0, self.qubit2)}
        self.assertEqual(expected_qargs, node_qargs)
    def test_front_layer(self):
        """The method dag.front_layer() returns first layer"""
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])

        op_nodes = self.dag.front_layer()
        self.assertEqual(len(op_nodes), 1)
        self.assertIsInstance(op_nodes[0].op, HGate)
Exemple #12
0
    def test_substituting_node_with_wrong_width_node_raises(self):
        """Verify replacing a node with one of a different shape raises."""
        dag = DAGCircuit()
        qr = QuantumRegister(2)
        dag.add_qreg(qr)
        node_to_be_replaced = dag.apply_operation_back(CXGate(), [qr[0], qr[1]])

        with self.assertRaises(DAGCircuitError) as _:
            dag.substitute_node(node_to_be_replaced, Measure())
Exemple #13
0
 def test_apply_operation_back(self):
     """The apply_operation_back() method."""
     self.dag.apply_operation_back(HGate(), [self.qubit0], [], condition=None)
     self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [], condition=None)
     self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None)
     self.dag.apply_operation_back(XGate(), [self.qubit1], [], condition=self.condition)
     self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], [], condition=None)
     self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None)
     self.assertEqual(len(list(self.dag.nodes())), 16)
     self.assertEqual(len(list(self.dag.edges())), 17)
    def _define(self):
        """
        gate xx_minus_yy(theta, beta) a, b {
            rz(-beta) b;
            rz(-pi/2) a;
            sx a;
            rz(pi/2) a;
            s b;
            cx a, b;
            ry(theta/2) a;
            ry(-theta/2) b;
            cx a, b;
            sdg b;
            rz(-pi/2) a;
            sxdg a;
            rz(pi/2) a;
            rz(beta) b;
        }
        """
        theta, beta = self.params
        register = QuantumRegister(2, "q")
        circuit = QuantumCircuit(register, name=self.name)
        a, b = register
        rules = [
            (RZGate(-beta), [b], []),
            (RZGate(-pi / 2), [a], []),
            (SXGate(), [a], []),
            (RZGate(pi / 2), [a], []),
            (SGate(), [b], []),
            (CXGate(), [a, b], []),
            (RYGate(theta / 2), [a], []),
            (RYGate(-theta / 2), [b], []),
            (CXGate(), [a, b], []),
            (SdgGate(), [b], []),
            (RZGate(-pi / 2), [a], []),
            (SXdgGate(), [a], []),
            (RZGate(pi / 2), [a], []),
            (RZGate(beta), [b], []),
        ]
        for instr, qargs, cargs in rules:
            circuit._append(instr, qargs, cargs)

        self.definition = circuit
    def test_dag_nodes_on_wire_multiple_successors(self):
        """
        Test that if a DAGNode has multiple successors in the DAG along one wire, they are all
        retrieved in order. This could be the case for a circuit such as

                q0_0: |0>──■─────────■──
                         ┌─┴─┐┌───┐┌─┴─┐
                q0_1: |0>┤ X ├┤ H ├┤ X ├
                         └───┘└───┘└───┘
        Both the 2nd CX gate and the H gate follow the first CX gate in the DAG, so they
        both must be returned but in the correct order.
        """
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(HGate(), [self.qubit1], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])

        nodes = self.dag.nodes_on_wire(self.dag.qubits[1], only_ops=True)
        node_names = [nd.name for nd in nodes]

        self.assertEqual(node_names, ['cx', 'h', 'cx'])
    def test_get_op_nodes_all(self):
        """The method dag.op_nodes() returns all op nodes"""
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])

        op_nodes = self.dag.op_nodes()
        self.assertEqual(len(op_nodes), 3)

        for node in op_nodes:
            self.assertIsInstance(node.op, Instruction)
Exemple #17
0
    def test_two_q_gates(self):
        """The method dag.two_qubit_ops() returns all 2Q gate operation nodes"""
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Barrier(2), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])

        op_nodes = self.dag.two_qubit_ops()
        self.assertEqual(len(op_nodes), 1)

        op_node = op_nodes.pop()
        self.assertIsInstance(op_node.op, Gate)
        self.assertEqual(len(op_node.qargs), 2)
Exemple #18
0
 def test_edges(self):
     """Test that DAGCircuit.edges() behaves as expected with ops."""
     self.dag.apply_operation_back(HGate(), [self.qubit0], [], condition=None)
     self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [], condition=None)
     self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None)
     self.dag.apply_operation_back(XGate(), [self.qubit1], [], condition=self.condition)
     self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], [], condition=None)
     self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [], condition=None)
     out_edges = self.dag.edges(self.dag.output_map.values())
     self.assertEqual(list(out_edges), [])
     in_edges = self.dag.edges(self.dag.input_map.values())
     # number of edges for input nodes should be the same as number of wires
     self.assertEqual(len(list(in_edges)), 5)
    def test_get_gates_nodes(self):
        """The method dag.gate_nodes() returns all gate nodes"""
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])

        op_nodes = self.dag.gate_nodes()
        self.assertEqual(len(op_nodes), 2)

        op_node_1 = op_nodes.pop()
        op_node_2 = op_nodes.pop()

        self.assertIsInstance(op_node_1.op, Gate)
        self.assertIsInstance(op_node_2.op, Gate)
Exemple #20
0
 def apply(self, dag_circuit: DAGCircuit, front_layer: QuantumLayer,
           initial_mapping: ty.Dict[Qubit, int],
           trans_mapping: ty.Dict[Qubit, int]):
     dag_circuit.apply_operation_back(CXGate(), [self.left, self.middle])
     dag_circuit.apply_operation_back(CXGate(), [self.middle, self.right])
     dag_circuit.apply_operation_back(CXGate(), [self.left, self.middle])
     dag_circuit.apply_operation_back(CXGate(), [self.middle, self.right])
     # dag_circuit.apply_operation_back(
     #     _BridgeGate(), [self.left, self.middle, self.right]
     # )
     # Do not forget to remove the CNOT gate from self.left to self.right from the
     # front layer.
     op_to_remove: ty.Optional[DAGNode] = None
     for op in front_layer.ops:
         q1, q2 = initial_mapping[op.qargs[0]], initial_mapping[op.qargs[1]]
         if (len(op.qargs) == 2 and q1 == trans_mapping[self.left]
                 and q2 == trans_mapping[self.right]):
             op_to_remove = op
     if op_to_remove is None:
         logger.warning(
             "Could not find a corresponding CNOT gate to remove with "
             "Bridge usage. Resulting circuit will likely be wrong.")
     else:
         front_layer.remove_operation(op_to_remove)
Exemple #21
0
    def test_dag_nodes_on_wire(self):
        """Test that listing the gates on a qubit/classical bit gets the correct gates"""
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])

        qbit = self.dag.qubits[0]
        self.assertEqual([0, 10, 11, 1], [i._node_id for i in self.dag.nodes_on_wire(qbit)])
        self.assertEqual([10, 11],
                         [i._node_id for i in self.dag.nodes_on_wire(qbit, only_ops=True)])

        cbit = self.dag.clbits[0]
        self.assertEqual([6, 7], [i._node_id for i in self.dag.nodes_on_wire(cbit)])
        self.assertEqual([], [i._node_id for i in self.dag.nodes_on_wire(cbit, only_ops=True)])

        with self.assertRaises(DAGCircuitError):
            next(self.dag.nodes_on_wire((qbit.register, 7)))
Exemple #22
0
    def test_substitute_circuit_one_middle(self):
        """The method substitute_node_with_dag() replaces a in-the-middle node with a DAG."""
        cx_node = self.dag.op_nodes(op=CXGate).pop()

        flipped_cx_circuit = DAGCircuit()
        v = QuantumRegister(2, "v")
        flipped_cx_circuit.add_qreg(v)
        flipped_cx_circuit.apply_operation_back(HGate(), [v[0]], [])
        flipped_cx_circuit.apply_operation_back(HGate(), [v[1]], [])
        flipped_cx_circuit.apply_operation_back(CXGate(), [v[1], v[0]], [])
        flipped_cx_circuit.apply_operation_back(HGate(), [v[0]], [])
        flipped_cx_circuit.apply_operation_back(HGate(), [v[1]], [])

        self.dag.substitute_node_with_dag(cx_node, flipped_cx_circuit, wires=[v[0], v[1]])

        self.assertEqual(self.dag.count_ops()['h'], 5)
Exemple #23
0
    def setUp(self):
        self.dag = DAGCircuit()
        qreg = QuantumRegister(3, 'qr')
        creg = ClassicalRegister(2, 'cr')
        self.dag.add_qreg(qreg)
        self.dag.add_creg(creg)

        self.qubit0 = qreg[0]
        self.qubit1 = qreg[1]
        self.qubit2 = qreg[2]
        self.clbit0 = creg[0]
        self.clbit1 = creg[1]
        self.condition = (creg, 3)

        self.dag.apply_operation_back(HGate(), [self.qubit0], [])
        self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], [])
        self.dag.apply_operation_back(XGate(), [self.qubit1], [])
                               inplace=True)
        return_circuit.compose(decomposition_euler[2 * best_nbasis + 1],
                               [q[1]],
                               inplace=True)

        return return_circuit

    def num_basis_gates(self, unitary):
        """ Computes the number of basis gates needed in
        a decomposition of input unitary
        """
        if hasattr(unitary, 'to_operator'):
            unitary = unitary.to_operator().data
        if hasattr(unitary, 'to_matrix'):
            unitary = unitary.to_matrix()
        unitary = np.asarray(unitary, dtype=complex)
        a, b, c = weyl_coordinates(unitary)[:]
        traces = [
            4 * (np.cos(a) * np.cos(b) * np.cos(c) +
                 1j * np.sin(a) * np.sin(b) * np.sin(c)), 4 *
            (np.cos(np.pi / 4 - a) * np.cos(self.basis.b - b) * np.cos(c) + 1j
             * np.sin(np.pi / 4 - a) * np.sin(self.basis.b - b) * np.sin(c)),
            4 * np.cos(c), 4
        ]
        return np.argmax([
            trace_to_fid(traces[i]) * self.basis_fidelity**i for i in range(4)
        ])


two_qubit_cnot_decompose = TwoQubitBasisDecomposer(CXGate())