コード例 #1
0
    def test_jordan_wigner_dual_basis_jellium_constant_shift(self):
        length_scale = 0.6
        grid = Grid(dimensions=2, length=3, scale=length_scale)
        spinless = True

        hamiltonian_without_constant = jordan_wigner_dual_basis_jellium(
            grid, spinless, include_constant=False)
        hamiltonian_with_constant = jordan_wigner_dual_basis_jellium(
            grid, spinless, include_constant=True)

        difference = hamiltonian_with_constant - hamiltonian_without_constant
        expected = FermionOperator.identity() * (2.8372 / length_scale)

        self.assertTrue(expected.isclose(difference))
コード例 #2
0
def jellium_model(grid, spinless=False, plane_wave=True,
                  include_constant=False):
    """Return jellium Hamiltonian as FermionOperator class.

    Args:
        grid (fermilib.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.

    Returns:
        FermionOperator: The Hamiltonian of the model.
    """
    if plane_wave:
        hamiltonian = plane_wave_kinetic(grid, spinless)
        hamiltonian += plane_wave_potential(grid, spinless)
    else:
        hamiltonian = dual_basis_jellium_model(grid, spinless)
    # Include the Madelung constant if requested.
    if include_constant:
        hamiltonian += FermionOperator.identity() * (2.8372 / grid.scale)
    return hamiltonian
コード例 #3
0
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_indices(ladder_op_mode, grid, spinless)
            vec1 = vec_func_1(indices_1, grid)
            new_basis = FermionOperator.zero()
            for indices_2 in grid.all_points_indices():
                vec2 = vec_func_2(indices_2, grid)
                spin = None if spinless else ladder_op_mode % 2
                orbital = orbital_id(grid, 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
コード例 #4
0
def dual_basis_jellium_model(grid, spinless=False,
                             kinetic=True, potential=True,
                             include_constant=False):
    """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.

    Returns:
        operator (FermionOperator)
    """
    # Initialize.
    n_points = grid.num_points()
    position_prefactor = 2. * numpy.pi / grid.volume_scale()
    operator = FermionOperator()
    spins = [None] if spinless else [0, 1]

    # Pre-Computations.
    position_vectors = {}
    momentum_vectors = {}
    momenta_squared_dict = {}
    orbital_ids = {}
    for indices in grid.all_points_indices():
        position_vectors[indices] = position_vector(indices, grid)
        momenta = momentum_vector(indices, grid)
        momentum_vectors[indices] = momenta
        momenta_squared_dict[indices] = momenta.dot(momenta)
        orbital_ids[indices] = {}
        for spin in spins:
            orbital_ids[indices][spin] = orbital_id(grid, indices, spin)

    # Loop once through all lattice sites.
    for grid_indices_a in grid.all_points_indices():
        coordinates_a = position_vectors[grid_indices_a]
        for grid_indices_b in grid.all_points_indices():
            coordinates_b = position_vectors[grid_indices_b]
            differences = coordinates_b - coordinates_a

            # 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)

            # Loop over spins and identify interacting orbitals.
            orbital_a = {}
            orbital_b = {}
            for spin in spins:
                orbital_a[spin] = orbital_ids[grid_indices_a][spin]
                orbital_b[spin] = orbital_ids[grid_indices_b][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:
        operator += FermionOperator.identity() * (2.8372 / grid.scale)

    # Return.
    return operator
コード例 #5
0
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.
    """
    # Get the jellium Hamiltonian in the plane wave basis.
    hamiltonian = jellium_model(grid, spinless, plane_wave=True)
    hamiltonian = normal_ordered(hamiltonian)
    hamiltonian.compress()

    # Enumerate the single-particle states.
    n_single_particle_states = (grid.length**grid.dimensions)
    if not spinless:
        n_single_particle_states *= 2

    # Compute the energies for each of the single-particle states.
    single_particle_energies = numpy.zeros(n_single_particle_states,
                                           dtype=float)
    for i in range(n_single_particle_states):
        single_particle_energies[i] = hamiltonian.terms.get(((i, 1), (i, 0)),
                                                            0.0)

    # 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 = single_particle_energies.argsort()[:n_electrons]

    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 = csr_matrix(
            ([1.0], ([hartree_fock_state_index], [0])),
            shape=(2**n_single_particle_states, 1))

    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 as a sparse matrix.
        hartree_fock_state = csr_matrix(([], ([], [])),
                                        shape=(2**n_single_particle_states, 1),
                                        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, 0] = dual_basis_hf_creation.terms[term]

    return hartree_fock_state