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:]))
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)])
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)
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)
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())
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))
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