コード例 #1
0
    def test_real_numbers(self):
        for m, n in self.test_dimensions:
            # Obtain a random real matrix of orthonormal rows
            Q = random_unitary_matrix(n, real=True)
            Q = Q[:m, :]

            # Get Givens decomposition of Q
            givens_rotations, V, diagonal = givens_decomposition(Q)

            # Compute U
            U = numpy.eye(n, dtype=complex)
            for parallel_set in givens_rotations:
                combined_givens = numpy.eye(n, dtype=complex)
                for i, j, theta, phi in reversed(parallel_set):
                    c = numpy.cos(theta)
                    s = numpy.sin(theta)
                    phase = numpy.exp(1.j * phi)
                    G = numpy.array([[c, -phase * s],
                                    [s, phase * c]], dtype=complex)
                    givens_rotate(combined_givens, G, i, j)
                U = combined_givens.dot(U)

            # Compute V * Q * U^\dagger
            W = V.dot(Q.dot(U.T.conj()))

            # Construct the diagonal matrix
            D = numpy.zeros((m, n), dtype=complex)
            D[numpy.diag_indices(m)] = diagonal

            # Assert that W and D are the same
            for i in range(m):
                for j in range(n):
                    self.assertAlmostEqual(D[i, j], W[i, j])
コード例 #2
0
def test_col_eliminate():
    """
    Test elimination by rotating in the column space.  Left multiplication of
    inverse givens
    """
    dim = 3
    u_generator = numpy.random.random((dim, dim)) + 1j * numpy.random.random(
        (dim, dim))
    u_generator = u_generator - numpy.conj(u_generator).T
    # make sure the generator is actually antihermitian
    assert numpy.allclose(-1 * u_generator, numpy.conj(u_generator).T)
    unitary = scipy.linalg.expm(u_generator)

    # eliminate U[1, 0] by rotation in rows [0, 1] and
    # mixing U[1, 0] and U[0, 0]
    unitary_original = unitary.copy()
    gmat = givens_matrix_elements(unitary[0, 0], unitary[1, 0], which='right')
    vec = numpy.array([[unitary[0, 0]], [unitary[1, 0]]])
    fullgmat = create_givens(gmat, 0, 1, 3)
    zeroed_unitary = fullgmat.dot(unitary)

    givens_rotate(unitary, gmat, 0, 1)
    assert numpy.isclose(unitary[1, 0], 0.0)
    assert numpy.allclose(unitary.real, zeroed_unitary.real)
    assert numpy.allclose(unitary.imag, zeroed_unitary.imag)

    # eliminate U[2, 0] by rotating columns [0, 1] and
    # mixing U[2, 0] and U[2, 1].
    unitary = unitary_original.copy()
    gmat = givens_matrix_elements(unitary[2, 0], unitary[2, 1], which='left')
    vec = numpy.array([[unitary[2, 0]], [unitary[2, 1]]])

    assert numpy.isclose((gmat.dot(vec))[0, 0], 0.0)
    assert numpy.isclose((vec.T.dot(gmat.T))[0, 0], 0.0)
    fullgmat = create_givens(gmat, 0, 1, 3)
    zeroed_unitary = unitary.dot(fullgmat.T)

    # because col takes g[0, 0] * col_i + g[0, 1].conj() * col_j -> col_i
    # this is equivalent ot left multiplication by gmat.T
    givens_rotate(unitary, gmat.conj(), 0, 1, which='col')
    assert numpy.isclose(zeroed_unitary[2, 0], 0.0)
    assert numpy.allclose(unitary, zeroed_unitary)
コード例 #3
0
def test_row_eliminate():
    """
    Test elemination of element in U[i, j] by rotating in i-1 and i.
    """
    dim = 3
    u_generator = numpy.random.random((dim, dim)) + 1j * numpy.random.random(
        (dim, dim))
    u_generator = u_generator - numpy.conj(u_generator).T

    # make sure the generator is actually antihermitian
    assert numpy.allclose(-1 * u_generator, numpy.conj(u_generator).T)

    unitary = scipy.linalg.expm(u_generator)

    # eliminate U[2, 0] by rotating in 1, 2
    gmat = givens_matrix_elements(unitary[1, 0], unitary[2, 0], which='right')
    givens_rotate(unitary, gmat, 1, 2, which='row')
    assert numpy.isclose(unitary[2, 0], 0.0)

    # eliminate U[1, 0] by rotating in 0, 1
    gmat = givens_matrix_elements(unitary[0, 0], unitary[1, 0], which='right')
    givens_rotate(unitary, gmat, 0, 1, which='row')
    assert numpy.isclose(unitary[1, 0], 0.0)

    # eliminate U[2, 1] by rotating in 1, 2
    gmat = givens_matrix_elements(unitary[1, 1], unitary[2, 1], which='right')
    givens_rotate(unitary, gmat, 1, 2, which='row')
    assert numpy.isclose(unitary[2, 1], 0.0)
コード例 #4
0
 def test_bad_input(self):
     """Test bad input."""
     with self.assertRaises(ValueError):
         v = numpy.random.randn(2)
         G = givens_matrix_elements(v[0], v[1])
         givens_rotate(v, G, 0, 1, which='a')
コード例 #5
0
    def test_main_procedure(self):
        for n in self.test_dimensions:
            # Obtain a random quadratic Hamiltonian
            quadratic_hamiltonian = random_quadratic_hamiltonian(n)

            # Get the diagonalizing transformation
            transformation_matrix = (
                quadratic_hamiltonian.diagonalizing_bogoliubov_transform())
            left_block = transformation_matrix[:, :n]
            right_block = transformation_matrix[:, n:]
            lower_unitary = numpy.empty((n, 2 * n), dtype=complex)
            lower_unitary[:, :n] = numpy.conjugate(right_block)
            lower_unitary[:, n:] = numpy.conjugate(left_block)

            # Get fermionic Gaussian decomposition of lower_unitary
            decomposition, left_decomposition, diagonal, left_diagonal = (
                fermionic_gaussian_decomposition(lower_unitary))

            # Compute left_unitary
            left_unitary = numpy.eye(n, dtype=complex)
            for parallel_set in left_decomposition:
                combined_op = numpy.eye(n, dtype=complex)
                for op in reversed(parallel_set):
                    i, j, theta, phi = op
                    c = numpy.cos(theta)
                    s = numpy.sin(theta)
                    phase = numpy.exp(1.j * phi)
                    givens_rotation = numpy.array(
                        [[c, -phase * s],
                         [s, phase * c]], dtype=complex)
                    givens_rotate(combined_op, givens_rotation, i, j)
                left_unitary = combined_op.dot(left_unitary)
            for i in range(n):
                left_unitary[i] *= left_diagonal[i]
            left_unitary = left_unitary.T
            for i in range(n):
                left_unitary[i] *= diagonal[i]

            # Check that left_unitary zeroes out the correct entries of
            # lower_unitary
            product = left_unitary.dot(lower_unitary)
            for i in range(n - 1):
                for j in range(n - 1 - i):
                    self.assertAlmostEqual(product[i, j], 0.)

            # Compute right_unitary
            right_unitary = numpy.eye(2 * n, dtype=complex)
            for parallel_set in decomposition:
                combined_op = numpy.eye(2 * n, dtype=complex)
                for op in reversed(parallel_set):
                    if op == 'pht':
                        swap_rows(combined_op, n - 1, 2 * n - 1)
                    else:
                        i, j, theta, phi = op
                        c = numpy.cos(theta)
                        s = numpy.sin(theta)
                        phase = numpy.exp(1.j * phi)
                        givens_rotation = numpy.array(
                            [[c, -phase * s],
                             [s, phase * c]], dtype=complex)
                        double_givens_rotate(combined_op, givens_rotation,
                                             i, j)
                right_unitary = combined_op.dot(right_unitary)

            # Compute left_unitary * lower_unitary * right_unitary^\dagger
            product = left_unitary.dot(lower_unitary.dot(
                right_unitary.T.conj()))

            # Construct the diagonal matrix
            diag = numpy.zeros((n, 2 * n), dtype=complex)
            diag[range(n), range(n, 2 * n)] = diagonal

            # Assert that W and D are the same
            for i in numpy.ndindex((n, 2 * n)):
                self.assertAlmostEqual(diag[i], product[i])
コード例 #6
0
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)