def test_random_operators_are_reproducible(self):
        op1 = random_diagonal_coulomb_hamiltonian(5, seed=5947)
        op2 = random_diagonal_coulomb_hamiltonian(5, seed=5947)
        numpy.testing.assert_allclose(op1.one_body, op2.one_body)
        numpy.testing.assert_allclose(op1.two_body, op2.two_body)

        op1 = random_interaction_operator(5, seed=8911)
        op2 = random_interaction_operator(5, seed=8911)
        numpy.testing.assert_allclose(op1.one_body_tensor, op2.one_body_tensor)
        numpy.testing.assert_allclose(op1.two_body_tensor, op2.two_body_tensor)

        op1 = random_quadratic_hamiltonian(5, seed=17711)
        op2 = random_quadratic_hamiltonian(5, seed=17711)
        numpy.testing.assert_allclose(op1.combined_hermitian_part,
                                      op2.combined_hermitian_part)
        numpy.testing.assert_allclose(op1.antisymmetric_part,
                                      op2.antisymmetric_part)

        op1 = random_antisymmetric_matrix(5, seed=24074)
        op2 = random_antisymmetric_matrix(5, seed=24074)
        numpy.testing.assert_allclose(op1, op2)

        op1 = random_hermitian_matrix(5, seed=56753)
        op2 = random_hermitian_matrix(5, seed=56753)
        numpy.testing.assert_allclose(op1, op2)

        op1 = random_unitary_matrix(5, seed=56486)
        op2 = random_unitary_matrix(5, seed=56486)
        numpy.testing.assert_allclose(op1, op2)
 def test_random_quadratic(self):
     n_qubits = 5
     quad_ham = random_quadratic_hamiltonian(n_qubits, True)
     ferm_op = get_fermion_operator(quad_ham)
     self.assertTrue(
         jordan_wigner(ferm_op) == jordan_wigner(
             get_diagonal_coulomb_hamiltonian(ferm_op)))
Beispiel #3
0
def test_prepare_gaussian_state(n_qubits,
                                conserves_particle_number,
                                occupied_orbitals,
                                initial_state,
                                atol=1e-5):

    qubits = LineQubit.range(n_qubits)
    if isinstance(initial_state, list):
        initial_state = sum(1 << (n_qubits - 1 - i) for i in initial_state)

    # Initialize a random quadratic Hamiltonian
    quad_ham = random_quadratic_hamiltonian(
            n_qubits, conserves_particle_number, real=False)
    quad_ham_sparse = get_sparse_operator(quad_ham)

    # Compute the energy of the desired state
    if occupied_orbitals is None:
        energy = quad_ham.ground_energy()
    else:
        orbital_energies, _, constant = (
                quad_ham.diagonalizing_bogoliubov_transform())
        energy = sum(orbital_energies[i] for i in occupied_orbitals) + constant

    # Get the state using a circuit simulation
    circuit = cirq.Circuit.from_ops(
            prepare_gaussian_state(
                qubits, quad_ham, occupied_orbitals,
                initial_state=initial_state))
    state = circuit.apply_unitary_effect_to_state(initial_state)

    # Check that the result is an eigenstate with the correct eigenvalue
    numpy.testing.assert_allclose(
            quad_ham_sparse.dot(state), energy * state, atol=atol)
    def test_ground_state_particle_nonconserving(self):
        """Test getting the ground state preparation circuit for a Hamiltonian
        that does not conserve particle number."""
        for n_qubits in self.n_qubits_range:
            # Initialize a particle-number-conserving Hamiltonian
            quadratic_hamiltonian = random_quadratic_hamiltonian(
                n_qubits, False, True)

            # Compute the true ground state
            sparse_operator = get_sparse_operator(quadratic_hamiltonian)
            ground_energy, _ = get_ground_state(sparse_operator)

            # Obtain the circuit
            circuit_description, start_orbitals = (
                gaussian_state_preparation_circuit(quadratic_hamiltonian))

            # Initialize the starting state
            state = jw_configuration_state(start_orbitals, n_qubits)

            # Apply the circuit
            particle_hole_transformation = (
                jw_sparse_particle_hole_transformation_last_mode(n_qubits))
            for parallel_ops in circuit_description:
                for op in parallel_ops:
                    if op == 'pht':
                        state = particle_hole_transformation.dot(state)
                    else:
                        i, j, theta, phi = op
                        state = jw_sparse_givens_rotation(
                            i, j, theta, phi, n_qubits).dot(state)

            # Check that the state obtained using the circuit is a ground state
            difference = sparse_operator * state - ground_energy * state
            discrepancy = numpy.amax(numpy.abs(difference))
            self.assertAlmostEqual(discrepancy, 0)
Beispiel #5
0
def test_prepare_gaussian_state_with_spin_symmetry(n_spatial_orbitals,
                                                   conserves_particle_number,
                                                   occupied_orbitals,
                                                   initial_state,
                                                   atol=1e-5):

    n_qubits = 2 * n_spatial_orbitals
    qubits = LineQubit.range(n_qubits)

    # Initialize a random quadratic Hamiltonian
    quad_ham = random_quadratic_hamiltonian(
            n_spatial_orbitals,
            conserves_particle_number,
            real=True,
            expand_spin=True,
            seed=639)

    # Reorder the Hamiltonian and get sparse matrix
    quad_ham = openfermion.get_quadratic_hamiltonian(
            openfermion.reorder(
                openfermion.get_fermion_operator(quad_ham),
                openfermion.up_then_down)
    )
    quad_ham_sparse = get_sparse_operator(quad_ham)

    # Compute the energy of the desired state
    energy = 0.0
    for spin_sector in range(2):
        orbital_energies, _, _ = (
            quad_ham.diagonalizing_bogoliubov_transform(
                spin_sector=spin_sector)
        )
        energy += sum(orbital_energies[i]
                      for i in occupied_orbitals[spin_sector])
    energy += quad_ham.constant

    # Get the state using a circuit simulation
    circuit = cirq.Circuit.from_ops(
            prepare_gaussian_state(
                qubits, quad_ham, occupied_orbitals,
                initial_state=initial_state))

    if isinstance(initial_state, list):
        initial_state = sum(1 << (n_qubits - 1 - i) for i in initial_state)
    state = circuit.apply_unitary_effect_to_state(initial_state)

    # Check that the result is an eigenstate with the correct eigenvalue
    numpy.testing.assert_allclose(
            quad_ham_sparse.dot(state), energy * state, atol=atol)
Beispiel #6
0
    def test_non_particle_conserving(self):
        for n_qubits in self.n_qubits_range:
            # Initialize a particle-number-conserving Hamiltonian
            quadratic_hamiltonian = random_quadratic_hamiltonian(
                n_qubits, False)
            sparse_operator = get_sparse_operator(quadratic_hamiltonian)

            # Diagonalize the Hamiltonian using the circuit
            circuit_description = reversed(
                quadratic_hamiltonian.diagonalizing_circuit())

            particle_hole_transformation = (
                jw_sparse_particle_hole_transformation_last_mode(n_qubits))
            for parallel_ops in circuit_description:
                for op in parallel_ops:
                    if op == 'pht':
                        gate = particle_hole_transformation
                    else:
                        i, j, theta, phi = op
                        gate = jw_sparse_givens_rotation(
                            i, j, theta, phi, n_qubits)
                    sparse_operator = (
                        gate.getH().dot(sparse_operator).dot(gate))

            # Check that the result is diagonal
            diag = scipy.sparse.diags(sparse_operator.diagonal())
            difference = sparse_operator - diag
            discrepancy = 0.
            if difference.nnz:
                discrepancy = max(abs(difference.data))

            self.assertTrue(discrepancy < EQ_TOLERANCE)

            # Check that the eigenvalues are in the expected order
            orbital_energies, constant = (
                quadratic_hamiltonian.orbital_energies())
            for index in range(2**n_qubits):
                bitstring = bin(index)[2:].zfill(n_qubits)
                subset = [j for j in range(n_qubits) if bitstring[j] == '1']
                energy = sum([orbital_energies[j] for j in subset]) + constant
                self.assertAlmostEqual(sparse_operator[index, index], energy)
    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])