def test_identity_recognized_as_potential_term(self): potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays( FermionOperator.identity())) self.assertListEqual(list(potential_terms), [FermionOperator.identity()]) self.assertListEqual(list(kinetic_terms), [])
def _fourier_transform_helper(hamiltonian, grid, spinless, phase_factor, vec_func_1, vec_func_2): hamiltonian_t = FermionOperator.zero() normalize_factor = numpy.sqrt(1.0 / float(grid.num_points)) for term in hamiltonian.terms: transformed_term = FermionOperator.identity() for ladder_op_mode, ladder_op_type in term: indices_1 = grid.grid_indices(ladder_op_mode, spinless) vec1 = vec_func_1(indices_1) new_basis = FermionOperator.zero() for indices_2 in grid.all_points_indices(): vec2 = vec_func_2(indices_2) spin = None if spinless else ladder_op_mode % 2 orbital = grid.orbital_id(indices_2, spin) exp_index = phase_factor * 1.0j * numpy.dot(vec1, vec2) if ladder_op_type == 1: exp_index *= -1.0 element = FermionOperator(((orbital, ladder_op_type), ), numpy.exp(exp_index)) new_basis += element new_basis *= normalize_factor transformed_term *= new_basis # Coefficient. transformed_term *= hamiltonian.terms[term] hamiltonian_t += transformed_term return hamiltonian_t
def test_commutes_identity(self): com = commutator(FermionOperator.identity(), FermionOperator('2^ 3', 2.3)) self.assertEqual(com, FermionOperator.zero()) com = commutator(BosonOperator.identity(), BosonOperator('2^ 3', 2.3)) self.assertTrue(com == BosonOperator.zero()) com = commutator(QuadOperator.identity(), QuadOperator('q2 p3', 2.3)) self.assertTrue(com == QuadOperator.zero())
def test_canonical_anticommutation_relations(self): op_1 = FermionOperator('3') op_1_dag = FermionOperator('3^') op_2 = FermionOperator('4') op_2_dag = FermionOperator('4^') zero = FermionOperator.zero() one = FermionOperator.identity() self.assertEqual(one, normal_ordered(anticommutator(op_1, op_1_dag))) self.assertEqual(zero, normal_ordered(anticommutator(op_1, op_2))) self.assertEqual(zero, normal_ordered(anticommutator(op_1, op_2_dag))) self.assertEqual(zero, normal_ordered(anticommutator(op_1_dag, op_2))) self.assertEqual(zero, normal_ordered(anticommutator(op_1_dag, op_2_dag))) self.assertEqual(one, normal_ordered(anticommutator(op_2, op_2_dag)))
def test_diagonal_coulomb_hamiltonian_class(self): hamiltonian = DiagonalCoulombHamiltonian(numpy.array([[1, 1], [1, 1]], dtype=float), numpy.array([[0, 1], [1, 0]], dtype=float), constant=2.3) potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian) ) potential = sum(potential_terms, FermionOperator.zero()) kinetic = sum(kinetic_terms, FermionOperator.zero()) expected_potential = (2.3 * FermionOperator.identity() + FermionOperator('0^ 0') + FermionOperator('1^ 1') - FermionOperator('1^ 0^ 1 0', 2.0)) expected_kinetic = FermionOperator('0^ 1') + FermionOperator('1^ 0') self.assertEqual(potential, expected_potential) self.assertEqual(kinetic, expected_kinetic)
def test_split_operator_error_operator_VT_order_against_definition(self): hamiltonian = (normal_ordered(fermi_hubbard(3, 3, 1., 4.0)) - 2.3 * FermionOperator.identity()) potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian) ) potential = sum(potential_terms, FermionOperator.zero()) kinetic = sum(kinetic_terms, FermionOperator.zero()) error_operator = ( split_operator_trotter_error_operator_diagonal_two_body( hamiltonian, order='V+T')) # V-then-T ordered double commutators: [V, [T, V]] + [T, [T, V]] / 2 inner_commutator = normal_ordered(commutator(kinetic, potential)) error_operator_definition = normal_ordered( commutator(potential, inner_commutator)) error_operator_definition += normal_ordered( commutator(kinetic, inner_commutator)) / 2.0 error_operator_definition /= 12.0 self.assertEqual(error_operator, error_operator_definition)
def jellium_model(grid: Grid, spinless: bool = False, plane_wave: bool = True, include_constant: bool = False, e_cutoff: float = None, non_periodic: bool = False, period_cutoff: Optional[float] = None) -> FermionOperator: """Return jellium Hamiltonian as FermionOperator class. Args: grid (openfermion.utils.Grid): The discretization to use. spinless (bool): Whether to use the spinless model or not. plane_wave (bool): Whether to return in momentum space (True) or position space (False). include_constant (bool): Whether to include the Madelung constant. Note constant is unsupported for non-uniform, non-cubic cells with ions. e_cutoff (float): Energy cutoff. non_periodic (bool): If the system is non-periodic, default to False. period_cutoff (float): Period cutoff, default to grid.volume_scale() ** (1. / grid.dimensions). Returns: FermionOperator: The Hamiltonian of the model. """ if plane_wave: hamiltonian = plane_wave_kinetic(grid, spinless, e_cutoff) hamiltonian += plane_wave_potential(grid, spinless, e_cutoff, non_periodic, period_cutoff) else: hamiltonian = dual_basis_jellium_model(grid, spinless, True, True, include_constant, non_periodic, period_cutoff) # Include the Madelung constant if requested. if include_constant: # TODO: Check for other unit cell shapes hamiltonian += (FermionOperator.identity() * (2.8372 / grid.volume_scale()**(1. / grid.dimensions))) return hamiltonian
def hartree_fock_state_jellium(grid, n_electrons, spinless=True, plane_wave=False): """Give the Hartree-Fock state of jellium. Args: grid (Grid): The discretization to use. n_electrons (int): Number of electrons in the system. spinless (bool): Whether to use the spinless model or not. plane_wave (bool): Whether to return the Hartree-Fock state in the plane wave (True) or dual basis (False). Notes: The jellium model is built up by filling the lowest-energy single-particle states in the plane-wave Hamiltonian until n_electrons states are filled. """ from openfermion.hamiltonians import plane_wave_kinetic # Get the jellium Hamiltonian in the plane wave basis. # For determining the Hartree-Fock state in the PW basis, only the # kinetic energy terms matter. hamiltonian = plane_wave_kinetic(grid, spinless=spinless) hamiltonian = normal_ordered(hamiltonian) hamiltonian.compress() # The number of occupied single-particle states is the number of electrons. # Those states with the lowest single-particle energies are occupied first. occupied_states = lowest_single_particle_energy_states( hamiltonian, n_electrons) occupied_states = numpy.array(occupied_states) if plane_wave: # In the plane wave basis the HF state is a single determinant. hartree_fock_state_index = numpy.sum(2**occupied_states) hartree_fock_state = numpy.zeros(2**count_qubits(hamiltonian), dtype=complex) hartree_fock_state[hartree_fock_state_index] = 1.0 else: # Inverse Fourier transform the creation operators for the state to get # to the dual basis state, then use that to get the dual basis state. hartree_fock_state_creation_operator = FermionOperator.identity() for state in occupied_states[::-1]: hartree_fock_state_creation_operator *= (FermionOperator( ((int(state), 1), ))) dual_basis_hf_creation_operator = inverse_fourier_transform( hartree_fock_state_creation_operator, grid, spinless) dual_basis_hf_creation = normal_ordered( dual_basis_hf_creation_operator) # Initialize the HF state. hartree_fock_state = numpy.zeros(2**count_qubits(hamiltonian), dtype=complex) # Populate the elements of the HF state in the dual basis. for term in dual_basis_hf_creation.terms: index = 0 for operator in term: index += 2**operator[0] hartree_fock_state[index] = dual_basis_hf_creation.terms[term] return hartree_fock_state
def dual_basis_jellium_model( grid: Grid, spinless: bool = False, kinetic: bool = True, potential: bool = True, include_constant: bool = False, non_periodic: bool = False, period_cutoff: Optional[float] = None) -> FermionOperator: """Return jellium Hamiltonian in the dual basis of arXiv:1706.00023 Args: grid (Grid): The discretization to use. spinless (bool): Whether to use the spinless model or not. kinetic (bool): Whether to include kinetic terms. potential (bool): Whether to include potential terms. include_constant (bool): Whether to include the Madelung constant. Note constant is unsupported for non-uniform, non-cubic cells with ions. non_periodic (bool): If the system is non-periodic, default to False. period_cutoff (float): Period cutoff, default to grid.volume_scale() ** (1. / grid.dimensions). Returns: operator (FermionOperator) """ # Initialize. n_points = grid.num_points position_prefactor = 2.0 * numpy.pi / grid.volume_scale() operator = FermionOperator() spins = [None] if spinless else [0, 1] if potential and non_periodic and period_cutoff is None: period_cutoff = grid.volume_scale()**(1.0 / grid.dimensions) # Pre-Computations. position_vectors = {} momentum_vectors = {} momenta_squared_dict = {} orbital_ids = {} for indices in grid.all_points_indices(): position_vectors[indices] = grid.position_vector(indices) momenta = grid.momentum_vector(indices) momentum_vectors[indices] = momenta momenta_squared_dict[indices] = momenta.dot(momenta) orbital_ids[indices] = {} for spin in spins: orbital_ids[indices][spin] = grid.orbital_id(indices, spin) # Loop once through all lattice sites. grid_origin = (0, ) * grid.dimensions coordinates_origin = position_vectors[grid_origin] for grid_indices_b in grid.all_points_indices(): coordinates_b = position_vectors[grid_indices_b] differences = coordinates_b - coordinates_origin # Compute coefficients. kinetic_coefficient = 0. potential_coefficient = 0. for momenta_indices in grid.all_points_indices(): momenta = momentum_vectors[momenta_indices] momenta_squared = momenta_squared_dict[momenta_indices] if momenta_squared == 0: continue cos_difference = numpy.cos(momenta.dot(differences)) if kinetic: kinetic_coefficient += (cos_difference * momenta_squared / (2. * float(n_points))) if potential: potential_coefficient += (position_prefactor * cos_difference / momenta_squared) for grid_indices_shift in grid.all_points_indices(): # Loop over spins and identify interacting orbitals. orbital_a = {} orbital_b = {} shifted_index_1 = tuple([ (grid_origin[i] + grid_indices_shift[i]) % grid.length[i] for i in range(grid.dimensions) ]) shifted_index_2 = tuple([ (grid_indices_b[i] + grid_indices_shift[i]) % grid.length[i] for i in range(grid.dimensions) ]) for spin in spins: orbital_a[spin] = orbital_ids[shifted_index_1][spin] orbital_b[spin] = orbital_ids[shifted_index_2][spin] if kinetic: for spin in spins: operators = ((orbital_a[spin], 1), (orbital_b[spin], 0)) operator += FermionOperator(operators, kinetic_coefficient) if potential: for sa in spins: for sb in spins: if orbital_a[sa] == orbital_b[sb]: continue operators = ((orbital_a[sa], 1), (orbital_a[sa], 0), (orbital_b[sb], 1), (orbital_b[sb], 0)) operator += FermionOperator(operators, potential_coefficient) # Include the Madelung constant if requested. if include_constant: # TODO: Check for other unit cell shapes operator += (FermionOperator.identity() * (2.8372 / grid.volume_scale()**(1. / grid.dimensions))) # Return. return operator