Skip to content

Commit e250399

Browse files
idk3babbush
authored andcommitted
Code for jellium Hartree-Fock state in plane wave and dual basis (#120)
* Code for jellium Hartree-Fock state in plane wave and dual basis * Trying to figure out where Python3 is making a float
1 parent 544b3d9 commit e250399

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
"""This module constructs the uniform electron gas' Hartree-Fock state."""
14+
from __future__ import absolute_import
15+
16+
from fermilib.ops import FermionOperator, normal_ordered
17+
from fermilib.utils import jellium_model, inverse_fourier_transform
18+
19+
from scipy.sparse import csr_matrix
20+
21+
import numpy
22+
23+
24+
def hartree_fock_state_jellium(grid, n_electrons, spinless=True,
25+
plane_wave=False):
26+
"""Give the Hartree-Fock state of jellium.
27+
28+
Args:
29+
grid (Grid): The discretization to use.
30+
n_electrons (int): Number of electrons in the system.
31+
spinless (bool): Whether to use the spinless model or not.
32+
plane_wave (bool): Whether to return the Hartree-Fock state in
33+
the plane wave (True) or dual basis (False).
34+
35+
Notes:
36+
The jellium model is built up by filling the lowest-energy
37+
single-particle states in the plane-wave Hamiltonian until
38+
n_electrons states are filled.
39+
"""
40+
# Get the jellium Hamiltonian in the plane wave basis.
41+
hamiltonian = jellium_model(grid, spinless, plane_wave=True)
42+
hamiltonian = normal_ordered(hamiltonian)
43+
hamiltonian.compress()
44+
45+
# Enumerate the single-particle states.
46+
n_single_particle_states = (grid.length ** grid.dimensions)
47+
if not spinless:
48+
n_single_particle_states *= 2
49+
50+
# Compute the energies for each of the single-particle states.
51+
single_particle_energies = numpy.zeros(n_single_particle_states,
52+
dtype=float)
53+
for i in range(n_single_particle_states):
54+
single_particle_energies[i] = hamiltonian.terms.get(
55+
((i, 1), (i, 0)), 0.0)
56+
57+
# The number of occupied single-particle states is the number of electrons.
58+
# Those states with the lowest single-particle energies are occupied first.
59+
occupied_states = single_particle_energies.argsort()[:n_electrons]
60+
61+
if plane_wave:
62+
# In the plane wave basis the HF state is a single determinant.
63+
hartree_fock_state_index = numpy.sum(2 ** occupied_states)
64+
hartree_fock_state = csr_matrix(
65+
([1.0], ([hartree_fock_state_index], [0])),
66+
shape=(2 ** n_single_particle_states, 1))
67+
68+
else:
69+
# Inverse Fourier transform the creation operators for the state to get
70+
# to the dual basis state, then use that to get the dual basis state.
71+
hartree_fock_state_creation_operator = FermionOperator.identity()
72+
for state in occupied_states[::-1]:
73+
hartree_fock_state_creation_operator *= (
74+
FermionOperator(((int(state), 1),)))
75+
dual_basis_hf_creation_operator = inverse_fourier_transform(
76+
hartree_fock_state_creation_operator, grid, spinless)
77+
78+
dual_basis_hf_creation = normal_ordered(
79+
dual_basis_hf_creation_operator)
80+
81+
# Initialize the HF state as a sparse matrix.
82+
hartree_fock_state = csr_matrix(
83+
([], ([], [])), shape=(2 ** n_single_particle_states, 1),
84+
dtype=complex)
85+
86+
# Populate the elements of the HF state in the dual basis.
87+
for term in dual_basis_hf_creation.terms:
88+
index = 0
89+
for operator in term:
90+
index += 2 ** operator[0]
91+
hartree_fock_state[index, 0] = dual_basis_hf_creation.terms[term]
92+
93+
return hartree_fock_state
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"""Tests for _jellium_hf_state.py."""
2+
from __future__ import absolute_import
3+
4+
from itertools import permutations
5+
from scipy.sparse import csr_matrix
6+
7+
import numpy
8+
import unittest
9+
10+
from fermilib.transforms import get_sparse_operator
11+
from fermilib.utils import expectation, get_ground_state, Grid, jellium_model
12+
from fermilib.utils._plane_wave_hamiltonian import wigner_seitz_length_scale
13+
from fermilib.utils._sparse_tools import jw_number_restrict_operator
14+
15+
from fermilib.utils._jellium_hf_state import hartree_fock_state_jellium
16+
17+
18+
class JelliumHartreeFockStateTest(unittest.TestCase):
19+
def test_hf_state_energy_close_to_ground_energy_at_high_density(self):
20+
grid_length = 8
21+
dimension = 1
22+
spinless = True
23+
n_particles = grid_length ** dimension // 2
24+
25+
# High density -> small length_scale.
26+
length_scale = 0.25
27+
28+
grid = Grid(dimension, grid_length, length_scale)
29+
hamiltonian = jellium_model(grid, spinless)
30+
hamiltonian_sparse = get_sparse_operator(hamiltonian)
31+
32+
hf_state = hartree_fock_state_jellium(grid, n_particles,
33+
spinless, plane_wave=True)
34+
35+
restricted_hamiltonian = jw_number_restrict_operator(
36+
hamiltonian_sparse, n_particles)
37+
38+
E_g = get_ground_state(restricted_hamiltonian)[0]
39+
E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state)
40+
41+
self.assertAlmostEqual(E_g, E_HF_plane_wave, places=5)
42+
43+
def test_hf_state_energy_same_in_plane_wave_and_dual_basis(self):
44+
grid_length = 4
45+
dimension = 1
46+
wigner_seitz_radius = 10.0
47+
spinless = False
48+
49+
n_orbitals = grid_length ** dimension
50+
if not spinless:
51+
n_orbitals *= 2
52+
n_particles = n_orbitals // 2
53+
54+
length_scale = wigner_seitz_length_scale(
55+
wigner_seitz_radius, n_particles, dimension)
56+
57+
grid = Grid(dimension, grid_length, length_scale)
58+
hamiltonian = jellium_model(grid, spinless)
59+
hamiltonian_dual_basis = jellium_model(
60+
grid, spinless, plane_wave=False)
61+
62+
# Get the Hamiltonians as sparse operators.
63+
hamiltonian_sparse = get_sparse_operator(hamiltonian)
64+
hamiltonian_dual_sparse = get_sparse_operator(hamiltonian_dual_basis)
65+
66+
hf_state = hartree_fock_state_jellium(
67+
grid, n_particles, spinless, plane_wave=True)
68+
hf_state_dual = hartree_fock_state_jellium(
69+
grid, n_particles, spinless, plane_wave=False)
70+
71+
E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state)
72+
E_HF_dual = expectation(hamiltonian_dual_sparse, hf_state_dual)
73+
74+
self.assertAlmostEqual(E_HF_dual, E_HF_plane_wave)
75+
76+
def test_hf_state_plane_wave_basis_lowest_single_determinant_state(self):
77+
grid_length = 7
78+
dimension = 1
79+
spinless = True
80+
n_particles = 4
81+
length_scale = 2.0
82+
83+
grid = Grid(dimension, grid_length, length_scale)
84+
hamiltonian = jellium_model(grid, spinless)
85+
hamiltonian_sparse = get_sparse_operator(hamiltonian)
86+
87+
hf_state = hartree_fock_state_jellium(grid, n_particles,
88+
spinless, plane_wave=True)
89+
90+
HF_energy = expectation(hamiltonian_sparse, hf_state)
91+
92+
for occupied_orbitals in permutations(
93+
[1] * n_particles + [0] * (grid_length - n_particles)):
94+
state_index = numpy.sum(2 ** numpy.array(occupied_orbitals))
95+
HF_competitor = csr_matrix(([1.0], ([state_index], [0])),
96+
shape=(2 ** grid_length, 1))
97+
98+
self.assertLessEqual(
99+
HF_energy, expectation(hamiltonian_sparse, HF_competitor))

0 commit comments

Comments
 (0)