def one_and_two_body_interaction_reverse_order(p, q, a,
                                                b) -> cirq.OP_TREE:
     yield rot11(rads=-self.hamiltonian.two_body[p, q] * time).on(a, b)
     yield Ryxxy(0.5 * self.hamiltonian.one_body[p, q].imag * time).on(
         a, b)
     yield Rxxyy(0.5 * self.hamiltonian.one_body[p, q].real * time).on(
         a, b)
def _ops_from_givens_rotations_circuit_description(
        qubits: Sequence[cirq.QubitId],
        circuit_description: Iterable[Iterable[
            Union[str, Tuple[int, int, float, float]]]]) -> cirq.OP_TREE:
    """Yield operations from a Givens rotations circuit obtained from
    OpenFermion.
    """
    for parallel_ops in circuit_description:
        for op in parallel_ops:
            if op == 'pht':
                yield cirq.X(qubits[-1])
            else:
                i, j, theta, phi = cast(Tuple[int, int, float, float], op)
                yield Ryxxy(theta).on(qubits[i], qubits[j])
                yield cirq.Z(qubits[j]) ** (phi / numpy.pi)
def optimal_givens_decomposition(
        qubits: Sequence[cirq.Qid],
        unitary: numpy.ndarray) -> Iterable[cirq.Operation]:
    r"""
    Implement a circuit that provides the unitary that is generated by
    single-particle fermion generators

    .. math::

        U(v) = exp(log(v)_{p,q}(a_{p}^{\dagger}a_{q} - a_{q}^{\dagger}a_{p})

    This can be used for implementing an exact single-body basis rotation

    Args:
        qubits: Sequence of qubits to apply the operations over.  The qubits
                should be ordered in linear physical order.
        unitary:
    """
    N = unitary.shape[0]
    right_rotations = []
    left_rotations = []
    for i in range(1, N):
        if i % 2 == 1:
            for j in range(0, i):
                # eliminate U[N - j, i - j] by mixing U[N - j, i - j],
                # U[N - j, i - j - 1] by right multiplication
                # of a givens rotation matrix in column [i - j, i - j + 1]
                gmat = givens_matrix_elements(unitary[N - j - 1, i - j - 1],
                                              unitary[N - j - 1,
                                                      i - j - 1 + 1],
                                              which='left')
                right_rotations.append((gmat.T, (i - j - 1, i - j)))
                givens_rotate(unitary,
                              gmat.conj(),
                              i - j - 1,
                              i - j,
                              which='col')
        else:
            for j in range(1, i + 1):
                # elimination of U[N + j - i, j] by mixing U[N + j - i, j] and
                # U[N + j - i - 1, j] by left multiplication
                # of a givens rotation that rotates row space
                # [N + j - i - 1, N + j - i
                gmat = givens_matrix_elements(unitary[N + j - i - 1 - 1,
                                                      j - 1],
                                              unitary[N + j - i - 1, j - 1],
                                              which='right')
                left_rotations.append((gmat, (N + j - i - 2, N + j - i - 1)))
                givens_rotate(unitary,
                              gmat,
                              N + j - i - 2,
                              N + j - i - 1,
                              which='row')

    new_left_rotations = []
    for (left_gmat, (i, j)) in reversed(left_rotations):
        phase_matrix = numpy.diag([unitary[i, i], unitary[j, j]])
        matrix_to_decompose = left_gmat.conj().T.dot(phase_matrix)
        new_givens_matrix = givens_matrix_elements(matrix_to_decompose[1, 0],
                                                   matrix_to_decompose[1, 1],
                                                   which='left')
        new_phase_matrix = matrix_to_decompose.dot(new_givens_matrix.T)

        # check if T_{m,n}^{-1}D  = D T.
        # coverage: ignore
        if not numpy.allclose(new_phase_matrix.dot(new_givens_matrix.conj()),
                              matrix_to_decompose):
            raise GivensTranspositionError("Failed to shift the phase matrix "
                                           "from right to left")
        # coverage: ignore

        unitary[i, i], unitary[j, j] = new_phase_matrix[0,
                                                        0], new_phase_matrix[1,
                                                                             1]
        new_left_rotations.append((new_givens_matrix.conj(), (i, j)))

    phases = numpy.diag(unitary)
    rotations = []
    ordered_rotations = []
    for (gmat, (i, j)) in list(reversed(new_left_rotations)) + list(
            map(lambda x: (x[0].conj().T, x[1]), reversed(right_rotations))):
        ordered_rotations.append((gmat, (i, j)))

        # if this throws the impossible has happened
        # coverage: ignore
        if not numpy.isclose(gmat[0, 0].imag, 0.0):
            raise GivensMatrixError(
                "Givens matrix does not obey our convention that all elements "
                "in the first column are real")
        if not numpy.isclose(gmat[1, 0].imag, 0.0):
            raise GivensMatrixError(
                "Givens matrix does not obey our convention that all elements "
                "in the first column are real")
        # coverage: ignore

        theta = numpy.arcsin(numpy.real(gmat[1, 0]))
        phi = numpy.angle(gmat[1, 1])
        rotations.append((i, j, theta, phi))

    for op in reversed(rotations):
        i, j, theta, phi = cast(Tuple[int, int, float, float], op)
        if not numpy.isclose(phi, 0.0):
            yield cirq.Z(qubits[j])**(phi / numpy.pi)

        yield Ryxxy(-theta).on(qubits[i], qubits[j])

    for idx, phase in enumerate(phases):
        yield cirq.Z(qubits[idx])**(numpy.angle(phase) / numpy.pi)