Пример #1
0
    def test_is_hopping_operator_terms_with_info_external_pot_at_end(self):
        grid_length = 4
        dimension = 1
        wigner_seitz_radius = 10.0
        inverse_filling_fraction = 2

        # Generate the Hamiltonian.
        grid = hypercube_grid_with_given_wigner_seitz_radius_and_filling(
            dimension, grid_length, wigner_seitz_radius,
            1. / inverse_filling_fraction)
        hamiltonian = normal_ordered(
            jellium_model(grid, spinless=True, plane_wave=False))
        hamiltonian.compress()

        # Unpack result into terms, indices they act on, and whether they're
        # hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian, external_potential_at_end=True)
        terms, _, is_hopping = result

        for i in range(len(terms)):
            single_term = list(terms[i].terms)[0]
            is_hopping_term = not (single_term[1][1]
                                   or single_term[0][0] == single_term[1][0])
            self.assertEqual(is_hopping_term, is_hopping[i])

        # Last four terms are the rotations
        self.assertFalse(sum(is_hopping[-4:]))
Пример #2
0
    def test_correct_indices_terms_with_info_external_pot_at_end(self):
        grid_length = 4
        dimension = 1
        wigner_seitz_radius = 10.0
        inverse_filling_fraction = 2
        grid_length**dimension

        # Generate the Hamiltonian.
        grid = hypercube_grid_with_given_wigner_seitz_radius_and_filling(
            dimension, grid_length, wigner_seitz_radius,
            1. / inverse_filling_fraction)
        hamiltonian = normal_ordered(
            jellium_model(grid, spinless=True, plane_wave=False))
        hamiltonian.compress()

        # Unpack result into terms, indices they act on, and whether they're
        # hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian, external_potential_at_end=True)
        terms, indices, _ = result

        for i in range(len(terms)):
            term = list(terms[i].terms)
            term_indices = set()
            for single_term in term:
                term_indices = term_indices.union(
                    [single_term[j][0] for j in range(len(single_term))])
            self.assertEqual(term_indices, indices[i])

        # Last four terms are the rotations
        self.assertListEqual(indices[-4:], [set([i]) for i in range(4)])
Пример #3
0
    def test_sum_of_ordered_terms_equals_full_hamiltonian_rots_at_end(self):
        grid_length = 4
        dimension = 2
        wigner_seitz_radius = 10.0
        inverse_filling_fraction = 2
        n_qubits = grid_length**dimension
        n_particles = n_qubits // inverse_filling_fraction

        # Generate the Hamiltonian.
        grid = hypercube_grid_with_given_wigner_seitz_radius_and_filling(
            dimension, grid_length, wigner_seitz_radius,
            1. / inverse_filling_fraction)
        hamiltonian = normal_ordered(
            jellium_model(grid, spinless=True, plane_wave=False))
        hamiltonian.compress()

        terms = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian, external_potential_at_end=True)[0]
        terms_total = sum(terms, FermionOperator.zero())

        length_scale = wigner_seitz_length_scale(wigner_seitz_radius,
                                                 n_particles, dimension)

        grid = Grid(dimension, grid_length, length_scale)
        hamiltonian = jellium_model(grid, spinless=True, plane_wave=False)
        hamiltonian = normal_ordered(hamiltonian)
        self.assertTrue(terms_total == hamiltonian)
Пример #4
0
    def test_error_bound_using_info_1d(self):
        # Generate the Hamiltonian.
        grid = hypercube_grid_with_given_wigner_seitz_radius_and_filling(
            dimension=1, grid_length=4, wigner_seitz_radius=10.)
        hamiltonian = normal_ordered(
            jellium_model(grid, spinless=True, plane_wave=False))
        hamiltonian.compress()

        # Unpack result into terms, indices they act on, and whether they're
        # hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian)
        terms, indices, is_hopping = result
        self.assertAlmostEqual(
            low_depth_second_order_trotter_error_bound(terms, indices,
                                                       is_hopping),
            7.4239378440953283)
Пример #5
0
    def test_hubbard_trotter_error_matches_low_depth_trotter_error(self):
        hamiltonian = normal_ordered(fermi_hubbard(3, 3, 1., 2.3))

        error_operator = (
            fermionic_swap_trotter_error_operator_diagonal_two_body(
                hamiltonian))
        error_operator.compress()

        # Unpack result into terms, indices they act on, and whether
        # they're hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian)
        terms, indices, is_hopping = result

        old_error_operator = low_depth_second_order_trotter_error_operator(
            terms, indices, is_hopping, jellium_only=True)

        old_error_operator -= error_operator
        self.assertEqual(old_error_operator, FermionOperator.zero())
Пример #6
0
    def test_total_length(self):
        grid_length = 8
        dimension = 1
        wigner_seitz_radius = 10.0
        inverse_filling_fraction = 2
        n_qubits = grid_length**dimension

        # Generate the Hamiltonian.
        grid = hypercube_grid_with_given_wigner_seitz_radius_and_filling(
            dimension, grid_length, wigner_seitz_radius,
            1. / inverse_filling_fraction)
        hamiltonian = normal_ordered(
            jellium_model(grid, spinless=True, plane_wave=False))
        hamiltonian.compress()

        # Unpack result into terms, indices they act on, and whether they're
        # hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian)
        terms, _, _ = result

        self.assertEqual(len(terms), n_qubits * (n_qubits - 1))
Пример #7
0
    def test_1D_jellium_trotter_error_matches_low_depth_trotter_error(self):
        hamiltonian = normal_ordered(
            jellium_model(
                hypercube_grid_with_given_wigner_seitz_radius_and_filling(
                    1, 5, wigner_seitz_radius=10., spinless=True),
                spinless=True,
                plane_wave=False))

        error_operator = (
            fermionic_swap_trotter_error_operator_diagonal_two_body(
                hamiltonian))
        error_operator.compress()

        # Unpack result into terms, indices they act on, and whether
        # they're hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian)
        terms, indices, is_hopping = result

        old_error_operator = low_depth_second_order_trotter_error_operator(
            terms, indices, is_hopping, jellium_only=True)

        old_error_operator -= error_operator
        self.assertEqual(old_error_operator, FermionOperator.zero())
def fermionic_swap_trotter_error_operator_diagonal_two_body(
        hamiltonian, external_potential_at_end=False):
    """Compute the fermionic swap network Trotter error of a diagonal
    two-body Hamiltonian.

    Args:
        hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to
                                       compute the Trotter error for.

    Returns:
        error_operator: The second-order Trotter error operator.

    Notes:
        Follows Eq 9 of Poulin et al., arXiv:1406.4920, applied to the
        Trotter step detailed in Kivlichan et al., arxiv:1711.04789.
    """
    single_terms = numpy.array(
        simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian,
            external_potential_at_end=external_potential_at_end)[0])

    # Cache the halved terms for use in the second commutator.
    halved_single_terms = single_terms / 2.0

    term_mode_mask = bit_mask_of_modes_acted_on_by_fermionic_terms(
        single_terms, count_qubits(hamiltonian))

    error_operator = FermionOperator.zero()

    for beta, term_beta in enumerate(single_terms):
        modes_acted_on_by_term_beta = set()
        for beta_action in term_beta.terms:
            modes_acted_on_by_term_beta.update(
                set(operator[0] for operator in beta_action))

        beta_mode_mask = numpy.logical_or.reduce(
            [term_mode_mask[mode] for mode in modes_acted_on_by_term_beta])

        # alpha_prime indices that could have a nonzero commutator, i.e.
        # there's overlap between the modes the corresponding terms act on.
        valid_alpha_primes = numpy.where(beta_mode_mask)[0]

        # Only alpha_prime < beta enters the error operator; filter for this.
        valid_alpha_primes = valid_alpha_primes[valid_alpha_primes < beta]

        for alpha_prime in valid_alpha_primes:
            term_alpha_prime = single_terms[alpha_prime]

            inner_commutator_term = (
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    term_beta, term_alpha_prime))

            modes_acted_on_by_inner_commutator = set()
            for inner_commutator_action in inner_commutator_term.terms:
                modes_acted_on_by_inner_commutator.update(
                    set(operator[0] for operator in inner_commutator_action))

            # If the inner commutator has no action, the commutator is zero.
            if not modes_acted_on_by_inner_commutator:
                continue

            inner_commutator_mask = numpy.logical_or.reduce([
                term_mode_mask[mode]
                for mode in modes_acted_on_by_inner_commutator
            ])

            # alpha indices that could have a nonzero commutator.
            valid_alphas = numpy.where(inner_commutator_mask)[0]
            # Filter so alpha <= beta in the double commutator.
            valid_alphas = valid_alphas[valid_alphas <= beta]

            for alpha in valid_alphas:
                # If alpha = beta, only use half the term.
                if alpha != beta:
                    outer_term_alpha = single_terms[alpha]
                else:
                    outer_term_alpha = halved_single_terms[alpha]

                # Add the partial double commutator to the error operator.
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    outer_term_alpha,
                    inner_commutator_term,
                    prior_terms=error_operator)

    # Divide by 12 to match the error operator definition.
    error_operator /= 12.0
    return error_operator