Example #1
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.get_op_nodes(op=CnotGate).pop()

        flipped_cx_circuit = DAGCircuit()
        v = QuantumRegister(2, "v")
        flipped_cx_circuit.add_qreg(v)
        flipped_cx_circuit.add_basis_element("cx", 2)
        flipped_cx_circuit.add_basis_element("h", 1)
        flipped_cx_circuit.apply_operation_back(HGate(v[0]))
        flipped_cx_circuit.apply_operation_back(HGate(v[1]))
        flipped_cx_circuit.apply_operation_back(CnotGate(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)
Example #2
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(CnotGate(), [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 test_instructions_equal(self):
        """Test equality of two instructions.
        """
        qr = QuantumRegister(3)
        cr = ClassicalRegister(3)
        hop1 = Instruction('h', [], qr, cr)
        hop2 = Instruction('s', [], qr, cr)
        hop3 = Instruction('h', [], qr, cr)

        uop1 = Instruction('u', [0.4, 0.5, 0.5], qr, cr)
        uop2 = Instruction('u', [0.4, 0.6, 0.5], qr, cr)
        uop3 = Instruction('v', [0.4, 0.5, 0.5], qr, cr)
        uop4 = Instruction('u', [0.4, 0.5, 0.5], qr, cr)
        self.assertFalse(hop1 == hop2)
        self.assertTrue(hop1 == hop3)
        self.assertFalse(uop1 == uop2)
        self.assertTrue(uop1 == uop4)
        self.assertFalse(uop1 == uop3)
        self.assertTrue(HGate(qr[0]) == HGate(qr[1]))
        self.assertFalse(HGate(qr[0]) == CnotGate(qr[0], qr[1]))
        self.assertFalse(hop1 == HGate(qr[2]))
Example #4
0
    def test_quantum_predecessors(self):
        """The method dag.quantum_predecessors() returns predecessors connected by quantum edges"""
        self.dag.apply_operation_back(Reset(), [self.qubit0], [])
        self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1],
                                      [])
        self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1],
                                      [])

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

        self.assertIsInstance(cnot_node.op, CnotGate)

        predecessor_cnot = self.dag.quantum_predecessors(cnot_node)
        self.assertIsInstance(next(predecessor_cnot).op, Reset)
        self.assertEqual(next(predecessor_cnot).type, 'in')
        with self.assertRaises(StopIteration):
            next(predecessor_cnot)
Example #5
0
 def _define(self):
     """
     gate ch a,b {
         s b;
         h b;
         t b;
         cx a, b;
         tdg b;
         h b;
         sdg b;
     }
     """
     definition = []
     q = QuantumRegister(2, "q")
     rule = [(SGate(), [q[1]], []), (HGate(), [q[1]], []),
             (TGate(), [q[1]], []), (CnotGate(), [q[0], q[1]], []),
             (TdgGate(), [q[1]], []), (HGate(), [q[1]], []),
             (SdgGate(), [q[1]], [])]
     for inst in rule:
         definition.append(inst)
     self.definition = definition
Example #6
0
    def __call__(self, target, basis_fidelity=None):
        """Decompose a two-qubit unitary over fixed basis + SU(2) using the best approximation given
        that each basis application has a finite fidelity.
        """
        basis_fidelity = basis_fidelity or self.basis_fidelity
        if hasattr(target, 'to_operator'):
            # If input is a BaseOperator subclass this attempts to convert
            # the object to an Operator so that we can extract the underlying
            # numpy matrix from `Operator.data`.
            target = target.to_operator().data
        if hasattr(target, 'to_matrix'):
            # If input is Gate subclass or some other class object that has
            # a to_matrix method this will call that method.
            target = target.to_matrix()
        # Convert to numpy array incase not already an array
        target = np.asarray(target, dtype=complex)
        # Check input is a 2-qubit unitary
        if target.shape != (4, 4):
            raise QiskitError("TwoQubitBasisDecomposer: expected 4x4 matrix for target")
        if not is_unitary_matrix(target):
            raise QiskitError("TwoQubitBasisDecomposer: target matrix is not unitary.")

        target_decomposed = TwoQubitWeylDecomposition(target)
        traces = self.traces(target_decomposed)
        expected_fidelities = [trace_to_fid(traces[i]) * basis_fidelity**i for i in range(4)]

        best_nbasis = np.argmax(expected_fidelities)
        decomposition = self.decomposition_fns[best_nbasis](target_decomposed)
        decomposition_angles = [euler_angles_1q(x) for x in decomposition]

        q = QuantumRegister(2)
        return_circuit = QuantumCircuit(q)
        for i in range(best_nbasis):
            return_circuit.append(U3Gate(*decomposition_angles[2*i]), [q[0]])
            return_circuit.append(U3Gate(*decomposition_angles[2*i+1]), [q[1]])
            return_circuit.append(CnotGate(), [q[0], q[1]])
        return_circuit.append(U3Gate(*decomposition_angles[2*best_nbasis]), [q[0]])
        return_circuit.append(U3Gate(*decomposition_angles[2*best_nbasis+1]), [q[1]])

        return return_circuit
Example #7
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(CnotGate(), [self.qubit0, self.qubit1],
                                      [])
        self.dag.apply_operation_back(HGate(), [self.qubit0], [])

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

        cbit = self.dag.clbits()[0]
        self.assertEqual([7, 8],
                         [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)))
    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]
        condition = (creg, 3)
        dag = DAGCircuit()
        dag.add_basis_element('h', 1, 0, 0)
        dag.add_basis_element('cx', 2, 0, 0)
        dag.add_basis_element('x', 1, 0, 0)
        dag.add_basis_element('measure', 1, 1, 0)
        dag.add_qreg(qreg)
        dag.add_creg(creg)
        dag.apply_operation_back(HGate(qubit0))
        dag.apply_operation_back(CnotGate(qubit0, qubit1), condition=None)
        dag.apply_operation_back(Measure(qubit1, clbit1), condition=None)
        dag.apply_operation_back(XGate(qubit1), condition=condition)
        dag.apply_operation_back(Measure(qubit0, clbit0), condition=None)
        dag.apply_operation_back(Measure(qubit1, clbit1), condition=None)

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

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

        self.assertEqual([
            ['h'],
            ['cx'],
            ['measure'],
            ['x'],
            ['measure', 'measure']
        ], name_layers)
Example #9
0
    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]])
        node_to_be_replaced = dag.apply_operation_back(CnotGate(), [qr[1], qr[0]],
                                                       condition=(cr, 1))

        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 setUp(self):
        self.dag = DAGCircuit()
        qreg = QuantumRegister(3, 'qr')
        creg = ClassicalRegister(2, 'cr')
        self.dag.add_qreg(qreg)
        self.dag.add_creg(creg)
        self.dag.add_basis_element(name='h', number_qubits=1,
                                   number_classical=0, number_parameters=0)
        self.dag.add_basis_element('cx', 2, 0, 0)
        self.dag.add_basis_element('x', 1, 0, 0)
        self.dag.add_basis_element('measure', 1, 1, 0)
        self.dag.add_basis_element('reset', 1, 0, 0)

        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(CnotGate(self.qubit0, self.qubit1))
        self.dag.apply_operation_back(XGate(self.qubit1))
Example #11
0
 def _define(self):
     """
     gate ccx a,b,c
     {
     h c; cx b,c; tdg c; cx a,c;
     t c; cx b,c; tdg c; cx a,c;
     t b; t c; h c; cx a,b;
     t a; tdg b; cx a,b;}
     """
     definition = []
     q = QuantumRegister(3, "q")
     rule = [(HGate(), [q[2]], []), (CnotGate(), [q[1], q[2]], []),
             (TdgGate(), [q[2]], []), (CnotGate(), [q[0], q[2]], []),
             (TGate(), [q[2]], []), (CnotGate(), [q[1], q[2]], []),
             (TdgGate(), [q[2]], []), (CnotGate(), [q[0], q[2]], []),
             (TGate(), [q[1]], []), (TGate(), [q[2]], []),
             (HGate(), [q[2]], []), (CnotGate(), [q[0], q[1]], []),
             (TGate(), [q[0]], []), (TdgGate(), [q[1]], []),
             (CnotGate(), [q[0], q[1]], [])]
     for inst in rule:
         definition.append(inst)
     self.definition = definition
Example #12
0
 def _define_decompositions(self):
     """
     gate ccx a,b,c
     {
     h c; cx b,c; tdg c; cx a,c;
     t c; cx b,c; tdg c; cx a,c;
     t b; t c; h c; cx a,b;
     t a; tdg b; cx a,b;}
     """
     decomposition = DAGCircuit()
     q = QuantumRegister(3, "q")
     decomposition.add_qreg(q)
     decomposition.add_basis_element("h", 1, 0, 0)
     decomposition.add_basis_element("cx", 2, 0, 0)
     decomposition.add_basis_element("t", 1, 0, 0)
     decomposition.add_basis_element("tdg", 1, 0, 0)
     decomposition.add_basis_element("s", 1, 0, 0)
     decomposition.add_basis_element("sdg", 1, 0, 0)
     rule = [
         HGate(q[2]),
         CnotGate(q[1], q[2]),
         TdgGate(q[2]),
         CnotGate(q[0], q[2]),
         TGate(q[2]),
         CnotGate(q[1], q[2]),
         TdgGate(q[2]),
         CnotGate(q[0], q[2]),
         TGate(q[1]),
         TGate(q[2]),
         HGate(q[2]),
         CnotGate(q[0], q[1]),
         TGate(q[0]),
         TdgGate(q[1]),
         CnotGate(q[0], q[1])
     ]
     for inst in rule:
         decomposition.apply_operation_back(inst)
     self._decompositions = [decomposition]
Example #13
0
    def _multiplex(self, bottom_gate, bottom_qubit_index, list_of_angles):
        """
        Internal recursive method to create gates to perform rotations on the
        imaginary qubits: works by rotating LSB (and hence ALL imaginary
        qubits) by combo angle and then flipping sign (by flipping the bit,
        hence moving the complex amplitudes) of half the imaginary qubits
        (CNOT) followed by another combo angle on LSB, therefore executing
        conditional (on MSB) rotations, thereby disentangling LSB.
        """
        list_len = len(list_of_angles)
        target_qubit = self.nth_qubit_from_least_sig_qubit(bottom_qubit_index)

        # Case of no multiplexing = base case for recursion
        if list_len == 1:
            return bottom_gate(list_of_angles[0], target_qubit)

        local_num_qubits = int(math.log2(list_len)) + 1
        control_qubit = self.nth_qubit_from_least_sig_qubit(local_num_qubits -
                                                            1 +
                                                            bottom_qubit_index)

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

        # calc the combo angles
        list_of_angles = (
            angle_weight *
            numpy.matrix(list_of_angles).transpose()).reshape(-1).tolist()[0]

        combine_composite_gates = CompositeGate(
            "multiplex" + local_num_qubits.__str__(), [], self.arg)

        # recursive step on half the angles fulfilling the above assumption
        combine_composite_gates._attach(
            self._multiplex(bottom_gate, bottom_qubit_index,
                            list_of_angles[0:(list_len // 2)]))

        # combine_composite_gates.cx(control_qubit,target_qubit) -> does not
        # work as expected because checks circuit
        # so attach CNOT as follows, thereby flipping the LSB qubit
        combine_composite_gates._attach(CnotGate(control_qubit, target_qubit))

        # implement extra efficiency from the paper of cancelling adjacent
        # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the
        # second lower-level multiplex)
        sub_gate = self._multiplex(bottom_gate, bottom_qubit_index,
                                   list_of_angles[(list_len // 2):])
        if isinstance(sub_gate, CompositeGate):
            combine_composite_gates._attach(sub_gate.reverse())
        else:
            combine_composite_gates._attach(sub_gate)

        # outer multiplex keeps final CNOT, because no adjacent CNOT to cancel
        # with
        if self.num_qubits == local_num_qubits + bottom_qubit_index:
            combine_composite_gates._attach(
                CnotGate(control_qubit, target_qubit))

        return combine_composite_gates
        return_circuit.append(U3Gate(*decomposition_angles[2 * best_nbasis]),
                              [q[0]])
        return_circuit.append(
            U3Gate(*decomposition_angles[2 * best_nbasis + 1]), [q[1]])

        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(CnotGate())
Example #15
0
    def create_dag_op(self, name, args, qubits):
        """Create a DAG op node.
        """
        if name == "u0":
            op = U0Gate(args[0], qubits[0])
        elif name == "u1":
            op = U1Gate(args[0], qubits[0])
        elif name == "u2":
            op = U2Gate(args[0], args[1], qubits[0])
        elif name == "u3":
            op = U3Gate(args[0], args[1], args[2], qubits[0])
        elif name == "x":
            op = XGate(qubits[0])
        elif name == "y":
            op = YGate(qubits[0])
        elif name == "z":
            op = ZGate(qubits[0])
        elif name == "t":
            op = TGate(qubits[0])
        elif name == "tdg":
            op = TdgGate(qubits[0])
        elif name == "s":
            op = SGate(qubits[0])
        elif name == "sdg":
            op = SdgGate(qubits[0])
        elif name == "swap":
            op = SwapGate(qubits[0], qubits[1])
        elif name == "rx":
            op = RXGate(args[0], qubits[0])
        elif name == "ry":
            op = RYGate(args[0], qubits[0])
        elif name == "rz":
            op = RZGate(args[0], qubits[0])
        elif name == "rzz":
            op = RZZGate(args[0], qubits[0], qubits[1])
        elif name == "id":
            op = IdGate(qubits[0])
        elif name == "h":
            op = HGate(qubits[0])
        elif name == "cx":
            op = CnotGate(qubits[0], qubits[1])
        elif name == "cy":
            op = CyGate(qubits[0], qubits[1])
        elif name == "cz":
            op = CzGate(qubits[0], qubits[1])
        elif name == "ch":
            op = CHGate(qubits[0], qubits[1])
        elif name == "crz":
            op = CrzGate(args[0], qubits[0], qubits[1])
        elif name == "cu1":
            op = Cu1Gate(args[0], qubits[0], qubits[1])
        elif name == "cu3":
            op = Cu3Gate(args[0], args[1], args[2], qubits[0], qubits[1])
        elif name == "ccx":
            op = ToffoliGate(qubits[0], qubits[1], qubits[2])
        elif name == "cswap":
            op = FredkinGate(qubits[0], qubits[1], qubits[2])
        else:
            raise BackendError("unknown operation for name ast node name %s" %
                               name)

        self.circuit.add_basis_element(op.name, len(op.qargs), len(op.cargs),
                                       len(op.param))
        self.start_gate(op)
        self.end_gate(op)
Example #16
0
    def _multiplex(self, target_gate, list_of_angles):
        """
        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

        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 = scipy.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)])
        circuit.append(multiplex_1.to_instruction(), q[0:-1])

        # attach CNOT as follows, thereby flipping the LSB qubit
        circuit.append(CnotGate(), [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):])
        if list_len > 1:
            circuit.append(multiplex_2.to_instruction().mirror(), q[0:-1])
        else:
            circuit.append(multiplex_2.to_instruction(), q[0:-1])

        # attach a final CNOT
        circuit.append(CnotGate(), [msb, lsb])

        return circuit
Example #17
0
classical_r = ClassicalRegister(4, "cr")
circuit = QuantumCircuit(quantum_r, classical_r)

circuit.h(quantum_r[0])

circuit.rx(0, quantum_r[0])

circuit.cx(quantum_r[0], quantum_r[1])
circuit.cx(quantum_r[0], quantum_r[1])

circuit.h(quantum_r[0])

circuit.cx(quantum_r[0], quantum_r[1])
composite_gate_1 = CompositeGate("composite1", [],
                                 [quantum_r[x] for x in range(4)])
composite_gate_1._attach(CnotGate(quantum_r[0], quantum_r[1]))
circuit._attach(composite_gate_1)

circuit.h(quantum_r[0])

composite_gate_2 = CompositeGate("composite2", [],
                                 [quantum_r[x] for x in range(4)])
composite_gate_2._attach(CnotGate(quantum_r[0], quantum_r[1]))
circuit._attach(composite_gate_2)
circuit.cx(quantum_r[0], quantum_r[1])

circuit.h(quantum_r[0])

composite_gate_3 = CompositeGate("composite3", [],
                                 [quantum_r[x] for x in range(4)])
composite_gate_3._attach(CnotGate(quantum_r[0], quantum_r[1]))
Example #18
0
def two_qubit_kak(unitary):
    """Decompose a two-qubit gate over SU(2)+CNOT using the KAK decomposition.

    Args:
        unitary (Operator): a 4x4 unitary operator to decompose.

    Returns:
        QuantumCircuit: a circuit implementing the unitary over SU(2)+CNOT

    Raises:
        QiskitError: input not a unitary, or error in KAK decomposition.
    """
    if hasattr(unitary, 'to_operator'):
        # If input is a BaseOperator subclass this attempts to convert
        # the object to an Operator so that we can extract the underlying
        # numpy matrix from `Operator.data`.
        unitary = unitary.to_operator().data
    if hasattr(unitary, 'to_matrix'):
        # If input is Gate subclass or some other class object that has
        # a to_matrix method this will call that method.
        unitary = unitary.to_matrix()
    # Convert to numpy array incase not already an array
    unitary_matrix = np.array(unitary, dtype=complex)
    # Check input is a 2-qubit unitary
    if unitary_matrix.shape != (4, 4):
        raise QiskitError("two_qubit_kak: Expected 4x4 matrix")
    if not is_unitary_matrix(unitary_matrix):
        raise QiskitError("Input matrix is not unitary.")
    phase = la.det(unitary_matrix)**(-1.0 / 4.0)
    # Make it in SU(4), correct phase at the end
    U = phase * unitary_matrix
    # B changes to the Bell basis
    B = (1.0 / math.sqrt(2)) * np.array(
        [[1, 1j, 0, 0], [0, 0, 1j, 1], [0, 0, 1j, -1], [1, -1j, 0, 0]],
        dtype=complex)

    # We also need B.conj().T below
    Bdag = B.conj().T
    # U' = Bdag . U . B
    Uprime = Bdag.dot(U.dot(B))
    # M^2 = trans(U') . U'
    M2 = Uprime.T.dot(Uprime)

    # Diagonalize M2
    # Must use diagonalization routine which finds a real orthogonal matrix P
    # when M2 is real.
    D, P = la.eig(M2)
    D = np.diag(D)
    # If det(P) == -1 then in O(4), apply a swap to make P in SO(4)
    if abs(la.det(P) + 1) < 1e-5:
        swap = np.array(
            [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]],
            dtype=complex)
        P = P.dot(swap)
        D = swap.dot(D.dot(swap))

    Q = np.sqrt(D)  # array from elementwise sqrt
    # Want to take square root so that Q has determinant 1
    if abs(la.det(Q) + 1) < 1e-5:
        Q[0, 0] = -Q[0, 0]

    # Q^-1*P.T = P' -> QP' = P.T (solve for P' using Ax=b)
    Pprime = la.solve(Q, P.T)
    # K' now just U' * P * P'
    Kprime = Uprime.dot(P.dot(Pprime))

    K1 = B.dot(Kprime.dot(P.dot(Bdag)))
    A = B.dot(Q.dot(Bdag))
    K2 = B.dot(P.T.dot(Bdag))
    # KAK = K1 * A * K2
    KAK = K1.dot(A.dot(K2))

    # Verify decomp matches input unitary.
    if la.norm(KAK - U) > 1e-6:
        raise QiskitError("two_qubit_kak: KAK decomposition " +
                          "does not return input unitary.")

    # Compute parameters alpha, beta, gamma so that
    # A = exp(i * (alpha * XX + beta * YY + gamma * ZZ))
    xx = np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]],
                  dtype=complex)

    yy = np.array([[0, 0, 0, -1], [0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0]],
                  dtype=complex)

    zz = np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]],
                  dtype=complex)

    A_real_tr = A.real.trace()
    alpha = math.atan2(A.dot(xx).imag.trace(), A_real_tr)
    beta = math.atan2(A.dot(yy).imag.trace(), A_real_tr)
    gamma = math.atan2(A.dot(zz).imag.trace(), A_real_tr)

    # K1 = kron(U1, U2) and K2 = kron(V1, V2)
    # Find the matrices U1, U2, V1, V2

    # Find a block in K1 where U1_ij * [U2] is not zero
    L = K1[0:2, 0:2]
    if la.norm(L) < 1e-9:
        L = K1[0:2, 2:4]
        if la.norm(L) < 1e-9:
            L = K1[2:4, 2:4]
    # Remove the U1_ij prefactor
    Q = L.dot(L.conj().T)
    U2 = L / math.sqrt(Q[0, 0].real)

    # Now grab U1 given we know U2
    R = K1.dot(np.kron(np.identity(2), U2.conj().T))
    U1 = np.zeros((2, 2), dtype=complex)
    U1[0, 0] = R[0, 0]
    U1[0, 1] = R[0, 2]
    U1[1, 0] = R[2, 0]
    U1[1, 1] = R[2, 2]

    # Repeat K1 routine for K2
    L = K2[0:2, 0:2]
    if la.norm(L) < 1e-9:
        L = K2[0:2, 2:4]
        if la.norm(L) < 1e-9:
            L = K2[2:4, 2:4]
    Q = np.dot(L, np.transpose(L.conjugate()))
    V2 = L / np.sqrt(Q[0, 0])
    R = np.dot(K2, np.kron(np.identity(2), np.transpose(V2.conjugate())))

    V1 = np.zeros_like(U1)
    V1[0, 0] = R[0, 0]
    V1[0, 1] = R[0, 2]
    V1[1, 0] = R[2, 0]
    V1[1, 1] = R[2, 2]

    if la.norm(np.kron(U1, U2) - K1) > 1e-4:
        raise QiskitError("two_qubit_kak: K1 != U1 x U2")
    if la.norm(np.kron(V1, V2) - K2) > 1e-4:
        raise QiskitError("two_qubit_kak: K2 != V1 x V2")

    test = la.expm(1j * (alpha * xx + beta * yy + gamma * zz))
    if la.norm(A - test) > 1e-4:
        raise QiskitError("two_qubit_kak: " +
                          "Matrix A does not match xx,yy,zz decomposition.")

    # Circuit that implements K1 * A * K2 (up to phase), using
    # Vatan and Williams Fig. 6 of quant-ph/0308006v3
    # Include prefix and suffix single-qubit gates into U2, V1 respectively.

    V2 = np.array([[np.exp(1j * np.pi / 4), 0], [0, np.exp(-1j * np.pi / 4)]],
                  dtype=complex).dot(V2)
    U1 = U1.dot(
        np.array([[np.exp(-1j * np.pi / 4), 0], [0, np.exp(1j * np.pi / 4)]],
                 dtype=complex))

    # Corrects global phase: exp(ipi/4)*phase'
    U1 = U1.dot(
        np.array([[np.exp(1j * np.pi / 4), 0], [0, np.exp(1j * np.pi / 4)]],
                 dtype=complex))
    U1 = phase.conjugate() * U1

    # Test
    g1 = np.kron(V1, V2)
    g2 = np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]],
                  dtype=complex)

    theta = 2 * gamma - np.pi / 2

    Ztheta = np.array(
        [[np.exp(1j * theta / 2), 0], [0, np.exp(-1j * theta / 2)]],
        dtype=complex)

    kappa = np.pi / 2 - 2 * alpha
    Ykappa = np.array(
        [[math.cos(kappa / 2), math.sin(kappa / 2)],
         [-math.sin(kappa / 2), math.cos(kappa / 2)]],
        dtype=complex)
    g3 = np.kron(Ztheta, Ykappa)
    g4 = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]],
                  dtype=complex)

    zeta = 2 * beta - np.pi / 2
    Yzeta = np.array(
        [[math.cos(zeta / 2), math.sin(zeta / 2)],
         [-math.sin(zeta / 2), math.cos(zeta / 2)]],
        dtype=complex)
    g5 = np.kron(np.identity(2), Yzeta)
    g6 = g2
    g7 = np.kron(U1, U2)

    V = g2.dot(g1)
    V = g3.dot(V)
    V = g4.dot(V)
    V = g5.dot(V)
    V = g6.dot(V)
    V = g7.dot(V)

    if la.norm(V - U * phase.conjugate()) > 1e-6:
        raise QiskitError("two_qubit_kak: " +
                          "sequence incorrect, unknown error")

    v1_param = euler_angles_1q(V1)
    v2_param = euler_angles_1q(V2)
    u1_param = euler_angles_1q(U1)
    u2_param = euler_angles_1q(U2)

    v1_gate = U3Gate(v1_param[0], v1_param[1], v1_param[2])
    v2_gate = U3Gate(v2_param[0], v2_param[1], v2_param[2])
    u1_gate = U3Gate(u1_param[0], u1_param[1], u1_param[2])
    u2_gate = U3Gate(u2_param[0], u2_param[1], u2_param[2])

    q = QuantumRegister(2)
    return_circuit = QuantumCircuit(q)

    return_circuit.append(v1_gate, [q[1]])

    return_circuit.append(v2_gate, [q[0]])

    return_circuit.append(CnotGate(), [q[0], q[1]])

    gate = U3Gate(0.0, 0.0, -2.0 * gamma + np.pi / 2.0)
    return_circuit.append(gate, [q[1]])

    gate = U3Gate(-np.pi / 2.0 + 2.0 * alpha, 0.0, 0.0)
    return_circuit.append(gate, [q[0]])

    return_circuit.append(CnotGate(), [q[1], q[0]])

    gate = U3Gate(-2.0 * beta + np.pi / 2.0, 0.0, 0.0)
    return_circuit.append(gate, [q[0]])

    return_circuit.append(CnotGate(), [q[0], q[1]])

    return_circuit.append(u1_gate, [q[1]])

    return_circuit.append(u2_gate, [q[0]])

    return return_circuit