def test_trivially_double_commutes_excess_create(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('5^ 2'), FermionOperator('5^ 5'), FermionOperator('5^ 1')))
def test_trivially_double_commutes_excess_annihilate(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('5^ 2'), FermionOperator('3^ 2'), FermionOperator('2^ 2')))
def test_trivially_double_commutes_double_annihilate_in_a_and_c(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('5^ 2'), FermionOperator('3^ 1'), FermionOperator('4^ 1^ 4 1')))
def test_no_trivial_double_commute_double_annihilate_with_create(self): self.assertFalse( trivially_double_commutes_dual_basis(FermionOperator('5^ 2'), FermionOperator('2^ 1'), FermionOperator('4^ 2')))
def test_trivially_double_commutes_hopping_and_number_on_same_modes(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('4^ 3'), FermionOperator('4^ 1'), FermionOperator('4^ 1^ 4 1')))
def test_trivially_double_commutes_no_intersection_a_with_bc(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('5^ 2'), FermionOperator('3^ 1'), FermionOperator('4^ 1^ 4 1')))
def test_no_trivial_double_commute_left_hopping_operator(self): self.assertFalse( trivially_double_commutes_dual_basis(FermionOperator('4^ 3'), FermionOperator('3^ 2'), FermionOperator('3^ 3')))
def test_trivially_double_commutes_both_hopping_annihilate_same_mode(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('1^ 1'), FermionOperator('4^ 1'), FermionOperator('3^ 1')))
def test_trivially_double_commutes_one_double_number_operators(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('4^ 3'), FermionOperator('3^ 2^ 3 2'), FermionOperator('3^ 3')))
def test_trivially_double_commutes_nonintersecting_single_number_ops(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('3^ 2'), FermionOperator('2^ 2'), FermionOperator('3^ 3')))
def test_no_trivial_double_commute_with_intersection(self): self.assertFalse( trivially_double_commutes_dual_basis(FermionOperator('4^ 2'), FermionOperator('2^ 1'), FermionOperator('5^ 2^ 5 2')))
def test_trivially_double_commutes_no_intersection(self): self.assertTrue( trivially_double_commutes_dual_basis(FermionOperator('3^ 4'), FermionOperator('3^ 2^ 3 2'), FermionOperator('4^ 1')))
def low_depth_second_order_trotter_error_operator(terms, indices=None, is_hopping_operator=None, jellium_only=False, verbose=False): """Determine the difference between the exact generator of unitary evolution and the approximate generator given by the second-order Trotter-Suzuki expansion. Args: terms: a list of FermionOperators in the Hamiltonian in the order in which they will be simulated. indices: a set of indices the terms act on in the same order as terms. is_hopping_operator: a list of whether each term is a hopping operator. jellium_only: Whether the terms are from the jellium Hamiltonian only, rather than the full dual basis Hamiltonian (i.e. whether c_i = c for all number operators i^ i, or whether they depend on i as is possible in the general case). verbose: Whether to print percentage progress. Returns: The difference between the true and effective generators of time evolution for a single Trotter step. Notes: follows Equation 9 of Poulin et al.'s work in "The Trotter Step Size Required for Accurate Quantum Simulation of Quantum Chemistry", applied to the "stagger"-based Trotter step for detailed in Kivlichan et al., "Quantum Simulation of Electronic Structure with Linear Depth and Connectivity", arxiv:1711.04789. """ more_info = bool(indices) n_terms = len(terms) if verbose: import time start = time.time() error_operator = FermionOperator.zero() for beta in range(n_terms): if verbose and beta % (n_terms // 30) == 0: print('%4.3f percent done in' % ((float(beta) / n_terms)**3 * 100), time.time() - start) for alpha in range(beta + 1): for alpha_prime in range(beta): # If we have pre-computed info on indices, use it to determine # trivial double commutation. if more_info: if (not trivially_double_commutes_dual_basis_using_term_info( # pylint: disable=C indices[alpha], indices[beta], indices[alpha_prime], is_hopping_operator[alpha], is_hopping_operator[beta], is_hopping_operator[alpha_prime], jellium_only)): # Determine the result of the double commutator. double_com = double_commutator( terms[alpha], terms[beta], terms[alpha_prime], indices[beta], indices[alpha_prime], is_hopping_operator[beta], is_hopping_operator[alpha_prime]) if alpha == beta: double_com /= 2.0 error_operator += double_com # If we don't have more info, check for trivial double # commutation using the terms directly. elif not trivially_double_commutes_dual_basis( terms[alpha], terms[beta], terms[alpha_prime]): double_com = double_commutator(terms[alpha], terms[beta], terms[alpha_prime]) if alpha == beta: double_com /= 2.0 error_operator += double_com error_operator /= 12.0 return error_operator