def test_position_vector(self): # Test in 1D. grid = Grid(dimensions=1, length=4, scale=4.) test_output = [position_vector(i, grid) for i in range(grid.length)] correct_output = [-2, -1, 0, 1] self.assertEqual(correct_output, test_output) grid = Grid(dimensions=1, length=11, scale=2. * numpy.pi) for i in range(grid.length): self.assertAlmostEqual(-position_vector(i, grid), position_vector(grid.length - i - 1, grid)) # Test in 2D. grid = Grid(dimensions=2, length=3, scale=3.) test_input = [] test_output = [] for i in range(3): for j in range(3): test_input += [(i, j)] test_output += [position_vector((i, j), grid)] correct_output = numpy.array([[-1., -1.], [-1., 0.], [-1., 1.], [0., -1.], [0., 0.], [0., 1.], [1., -1.], [1., 0.], [1., 1.]]) self.assertAlmostEqual(0., numpy.amax(test_output - correct_output))
def test_properties(self): g = Grid(dimensions=2, length=3, scale=5.0) self.assertEqual(g.num_points(), 9) self.assertEqual(g.volume_scale(), 25) self.assertEqual(list(g.all_points_indices()), [ (0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2), ])
def test_plane_wave_hamiltonian_integration(self): length_set = [3, 4] spinless_set = [True, False] geometry = [('H', (0, )), ('H', (0.8, ))] length_scale = 1.1 for l in length_set: for spinless in spinless_set: grid = Grid(dimensions=1, scale=length_scale, length=l) h_plane_wave = plane_wave_hamiltonian(grid, geometry, spinless, True, include_constant=True) h_dual_basis = plane_wave_hamiltonian(grid, geometry, spinless, False) jw_h_plane_wave = jordan_wigner(h_plane_wave) jw_h_dual_basis = jordan_wigner(h_dual_basis) h_plane_wave_spectrum = eigenspectrum(jw_h_plane_wave) h_dual_basis_spectrum = eigenspectrum(jw_h_dual_basis) max_diff = numpy.amax(h_plane_wave_spectrum - h_dual_basis_spectrum) min_diff = numpy.amin(h_plane_wave_spectrum - h_dual_basis_spectrum) self.assertAlmostEqual(max_diff, 2.8372 / length_scale) self.assertAlmostEqual(min_diff, 2.8372 / length_scale)
def test_model_integration_with_constant(self): # Compute Hamiltonian in both momentum and position space. length_scale = 0.7 grid = Grid(dimensions=2, length=3, scale=length_scale) spinless = True # Include the Madelung constant in the momentum but not the position # Hamiltonian. momentum_hamiltonian = jellium_model(grid, spinless, True, include_constant=True) position_hamiltonian = jellium_model(grid, spinless, False) # Diagonalize and confirm the same energy. jw_momentum = jordan_wigner(momentum_hamiltonian) jw_position = jordan_wigner(position_hamiltonian) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm momentum spectrum is shifted 2.8372 / length_scale higher. max_difference = numpy.amax(momentum_spectrum - position_spectrum) min_difference = numpy.amax(momentum_spectrum - position_spectrum) self.assertAlmostEqual(max_difference, 2.8372 / length_scale) self.assertAlmostEqual(min_difference, 2.8372 / length_scale)
def dual_basis_jellium_hamiltonian(grid_length, dimension=3, wigner_seitz_radius=10., n_particles=None, spinless=True): """Return the jellium Hamiltonian with the given parameters. Args: grid_length (int): The number of spatial orbitals per dimension. dimension (int): The dimension of the system. wigner_seitz_radius (float): The radius per particle in Bohr. n_particles (int): The number of particles in the system. Defaults to half filling if not specified. """ n_qubits = grid_length**dimension if not spinless: n_qubits *= 2 if n_particles is None: # Default to half filling fraction. n_particles = n_qubits // 2 if not (0 <= n_particles <= n_qubits): raise ValueError('n_particles must be between 0 and the number of' ' spin-orbitals.') # Compute appropriate length scale. length_scale = wigner_seitz_length_scale(wigner_seitz_radius, n_particles, dimension) grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless=spinless, plane_wave=False) hamiltonian = normal_ordered(hamiltonian) hamiltonian.compress() return hamiltonian
def test_jordan_wigner_dual_basis_jellium(self): # Parameters. grid = Grid(dimensions=2, length=3, scale=1.) spinless = True # Compute fermionic Hamiltonian. Include then subtract constant. fermion_hamiltonian = dual_basis_jellium_model(grid, spinless, include_constant=True) qubit_hamiltonian = jordan_wigner(fermion_hamiltonian) qubit_hamiltonian -= QubitOperator((), 2.8372) # Compute Jordan-Wigner Hamiltonian. test_hamiltonian = jordan_wigner_dual_basis_jellium(grid, spinless) # Make sure Hamiltonians are the same. self.assertTrue(test_hamiltonian.isclose(qubit_hamiltonian)) # Check number of terms. n_qubits = count_qubits(qubit_hamiltonian) if spinless: paper_n_terms = 1 - .5 * n_qubits + 1.5 * (n_qubits**2) num_nonzeros = sum(1 for coeff in qubit_hamiltonian.terms.values() if coeff != 0.0) self.assertTrue(num_nonzeros <= paper_n_terms)
def test_hf_state_energy_close_to_ground_energy_at_high_density(self): grid_length = 8 dimension = 1 spinless = True n_particles = grid_length**dimension // 2 # High density -> small length_scale. length_scale = 0.25 grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless) hamiltonian_sparse = get_sparse_operator(hamiltonian) hf_state = hartree_fock_state_jellium(grid, n_particles, spinless, plane_wave=True) restricted_hamiltonian = jw_number_restrict_operator( hamiltonian_sparse, n_particles) E_g = get_ground_state(restricted_hamiltonian)[0] E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state) self.assertAlmostEqual(E_g, E_HF_plane_wave, places=5)
def test_hf_state_plane_wave_basis_lowest_single_determinant_state(self): grid_length = 7 dimension = 1 spinless = True n_particles = 4 length_scale = 2.0 grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless) hamiltonian_sparse = get_sparse_operator(hamiltonian) hf_state = hartree_fock_state_jellium(grid, n_particles, spinless, plane_wave=True) HF_energy = expectation(hamiltonian_sparse, hf_state) for occupied_orbitals in permutations([1] * n_particles + [0] * (grid_length - n_particles)): state_index = numpy.sum(2**numpy.array(occupied_orbitals)) HF_competitor = csr_matrix(([1.0], ([state_index], [0])), shape=(2**grid_length, 1)) self.assertLessEqual( HF_energy, expectation(hamiltonian_sparse, HF_competitor))
def test_sum_of_ordered_terms_equals_full_hamiltonian(self): grid_length = 4 dimension = 2 wigner_seitz_radius = 10.0 inverse_filling_fraction = 2 n_qubits = grid_length**dimension # Compute appropriate length scale. n_particles = n_qubits // inverse_filling_fraction # Generate the Hamiltonian. hamiltonian = dual_basis_jellium_hamiltonian(grid_length, dimension, wigner_seitz_radius, n_particles) terms = simulation_ordered_grouped_dual_basis_terms_with_info( hamiltonian)[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.isclose(hamiltonian))
def test_inverse_fourier_transform_2d(self): grid = Grid(dimensions=2, scale=1.5, length=3) spinless = True geometry = [('H', (0, 0)), ('H', (0.5, 0.8))] h_plane_wave = plane_wave_hamiltonian(grid, geometry, spinless, True) h_dual_basis = plane_wave_hamiltonian(grid, geometry, spinless, False) h_dual_basis_t = inverse_fourier_transform(h_dual_basis, grid, spinless) self.assertTrue( normal_ordered(h_dual_basis_t).isclose( normal_ordered(h_plane_wave)))
def test_momentum_vector(self): grid = Grid(dimensions=1, length=3, scale=2. * numpy.pi) test_output = [momentum_vector(i, grid) for i in range(grid.length)] correct_output = [-1., 0, 1.] self.assertEqual(correct_output, test_output) grid = Grid(dimensions=1, length=11, scale=2. * numpy.pi) for i in range(grid.length): self.assertAlmostEqual(-momentum_vector(i, grid), momentum_vector(grid.length - i - 1, grid)) # Test in 2D. grid = Grid(dimensions=2, length=3, scale=2. * numpy.pi) test_input = [] test_output = [] for i in range(3): for j in range(3): test_input += [(i, j)] test_output += [momentum_vector((i, j), grid)] correct_output = numpy.array([[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1]]) self.assertAlmostEqual(0., numpy.amax(test_output - correct_output))
def test_preconditions(self): nan = float('nan') # No exception _ = Grid(dimensions=0, length=0, scale=1.0) _ = Grid(dimensions=1, length=1, scale=1.0) _ = Grid(dimensions=2, length=3, scale=0.01) _ = Grid(dimensions=234, length=345, scale=456.0) with self.assertRaises(ValueError): _ = Grid(dimensions=1, length=1, scale=1) with self.assertRaises(ValueError): _ = Grid(dimensions=1, length=1, scale=0.0) with self.assertRaises(ValueError): _ = Grid(dimensions=1, length=1, scale=-1.0) with self.assertRaises(ValueError): _ = Grid(dimensions=1, length=1, scale=nan) with self.assertRaises(ValueError): _ = Grid(dimensions=1, length=-1, scale=1.0) with self.assertRaises(ValueError): _ = Grid(dimensions=-1, length=1, scale=1.0)
def test_fourier_transform(self): grid = Grid(dimensions=1, scale=1.5, length=3) spinless_set = [True, False] geometry = [('H', (0, )), ('H', (0.5, ))] for spinless in spinless_set: h_plane_wave = plane_wave_hamiltonian(grid, geometry, spinless, True) h_dual_basis = plane_wave_hamiltonian(grid, geometry, spinless, False) h_plane_wave_t = fourier_transform(h_plane_wave, grid, spinless) self.assertTrue( normal_ordered(h_plane_wave_t).isclose( normal_ordered(h_dual_basis)))
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))
def test_orbital_id(self): # Test in 1D with spin. grid = Grid(dimensions=1, length=5, scale=1.0) input_coords = [0, 1, 2, 3, 4] tensor_factors_up = [1, 3, 5, 7, 9] tensor_factors_down = [0, 2, 4, 6, 8] test_output_up = [orbital_id(grid, i, 1) for i in input_coords] test_output_down = [orbital_id(grid, i, 0) for i in input_coords] self.assertEqual(test_output_up, tensor_factors_up) self.assertEqual(test_output_down, tensor_factors_down) with self.assertRaises(OrbitalSpecificationError): orbital_id(grid, 6, 1) # Test in 2D without spin. grid = Grid(dimensions=2, length=3, scale=1.0) input_coords = [(0, 0), (0, 1), (1, 2)] tensor_factors = [0, 3, 7] test_output = [orbital_id(grid, i) for i in input_coords] self.assertEqual(test_output, tensor_factors)
def test_jordan_wigner_dual_basis_hamiltonian(self): grid = Grid(dimensions=2, length=3, scale=1.) spinless = True geometry = [('H', (0, 0)), ('H', (0.5, 0.8))] fermion_hamiltonian = plane_wave_hamiltonian(grid, geometry, spinless, False, include_constant=True) qubit_hamiltonian = jordan_wigner(fermion_hamiltonian) test_hamiltonian = jordan_wigner_dual_basis_hamiltonian( grid, geometry, spinless, include_constant=True) self.assertTrue(test_hamiltonian.isclose(qubit_hamiltonian))
def test_jw_restrict_jellium_ground_state_integration(self): n_qubits = 4 grid = Grid(dimensions=1, length=n_qubits, scale=1.0) jellium_hamiltonian = jordan_wigner_sparse( jellium_model(grid, spinless=False)) # 2 * n_qubits because of spin number_sparse = jordan_wigner_sparse(number_operator(2 * n_qubits)) restricted_number = jw_number_restrict_operator(number_sparse, 2) restricted_jellium_hamiltonian = jw_number_restrict_operator( jellium_hamiltonian, 2) energy, ground_state = get_ground_state(restricted_jellium_hamiltonian) number_expectation = expectation(restricted_number, ground_state) self.assertAlmostEqual(number_expectation, 2)
def test_kinetic_integration(self): # Compute kinetic energy operator in both momentum and position space. grid = Grid(dimensions=2, length=2, scale=3.) spinless = False momentum_kinetic = plane_wave_kinetic(grid, spinless) position_kinetic = dual_basis_kinetic(grid, spinless) # Diagonalize and confirm the same energy. jw_momentum = jordan_wigner(momentum_kinetic) jw_position = jordan_wigner(position_kinetic) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm spectra are the same. difference = numpy.amax( numpy.absolute(momentum_spectrum - position_spectrum)) self.assertAlmostEqual(difference, 0.)
def test_potential_integration(self): # Compute potential energy operator in momentum and position space. grid = Grid(dimensions=2, length=3, scale=2.) spinless = 1 momentum_potential = plane_wave_potential(grid, spinless) position_potential = dual_basis_potential(grid, spinless) # Diagonalize and confirm the same energy. jw_momentum = jordan_wigner(momentum_potential) jw_position = jordan_wigner(position_potential) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm spectra are the same. difference = numpy.amax( numpy.absolute(momentum_spectrum - position_spectrum)) self.assertAlmostEqual(difference, 0.)
def test_model_integration(self): # Compute Hamiltonian in both momentum and position space. grid = Grid(dimensions=2, length=3, scale=1.0) spinless = True momentum_hamiltonian = jellium_model(grid, spinless, True) position_hamiltonian = jellium_model(grid, spinless, False) # Diagonalize and confirm the same energy. jw_momentum = jordan_wigner(momentum_hamiltonian) jw_position = jordan_wigner(position_hamiltonian) momentum_spectrum = eigenspectrum(jw_momentum) position_spectrum = eigenspectrum(jw_position) # Confirm spectra are the same. difference = numpy.amax( numpy.absolute(momentum_spectrum - position_spectrum)) self.assertAlmostEqual(difference, 0.)
def test_hf_state_energy_same_in_plane_wave_and_dual_basis(self): grid_length = 4 dimension = 1 wigner_seitz_radius = 10.0 spinless = False n_orbitals = grid_length**dimension if not spinless: n_orbitals *= 2 n_particles = n_orbitals // 2 length_scale = wigner_seitz_length_scale(wigner_seitz_radius, n_particles, dimension) grid = Grid(dimension, grid_length, length_scale) hamiltonian = jellium_model(grid, spinless) hamiltonian_dual_basis = jellium_model(grid, spinless, plane_wave=False) # Get the Hamiltonians as sparse operators. hamiltonian_sparse = get_sparse_operator(hamiltonian) hamiltonian_dual_sparse = get_sparse_operator(hamiltonian_dual_basis) hf_state = hartree_fock_state_jellium(grid, n_particles, spinless, plane_wave=True) hf_state_dual = hartree_fock_state_jellium(grid, n_particles, spinless, plane_wave=False) E_HF_plane_wave = expectation(hamiltonian_sparse, hf_state) E_HF_dual = expectation(hamiltonian_dual_sparse, hf_state_dual) self.assertAlmostEqual(E_HF_dual, E_HF_plane_wave)
def test_plane_wave_hamiltonian_bad_element(self): grid = Grid(dimensions=3, scale=1.0, length=4) with self.assertRaises(ValueError): plane_wave_hamiltonian(grid, geometry=[('Unobtainium', (0, 0, 0))])
def test_jordan_wigner_dual_basis_hamiltonian_default_to_jellium(self): grid = Grid(dimensions=1, scale=1.0, length=4) self.assertTrue( jordan_wigner_dual_basis_hamiltonian(grid).isclose( jordan_wigner(jellium_model(grid, plane_wave=False))))
def test_jordan_wigner_dual_basis_hamiltonian_bad_element(self): grid = Grid(dimensions=3, scale=1.0, length=4) with self.assertRaises(ValueError): jordan_wigner_dual_basis_hamiltonian(grid, geometry=[('Unobtainium', (0, 0, 0))])
def test_plane_wave_hamiltonian_default_to_jellium_with_no_geometry(self): grid = Grid(dimensions=1, scale=1.0, length=4) self.assertTrue( plane_wave_hamiltonian(grid).isclose(jellium_model(grid)))
def test_plane_wave_hamiltonian_bad_geometry(self): grid = Grid(dimensions=1, scale=1.0, length=4) with self.assertRaises(ValueError): plane_wave_hamiltonian(grid, geometry=[('H', (0, 0, 0))])
def test_coefficients(self): # Test that the coefficients post-JW transform are as claimed in paper. grid = Grid(dimensions=2, length=3, scale=2.) spinless = 1 n_orbitals = grid.num_points() n_qubits = (2**(1 - spinless)) * n_orbitals volume = grid.volume_scale() # Kinetic operator. kinetic = dual_basis_kinetic(grid, spinless) qubit_kinetic = jordan_wigner(kinetic) # Potential operator. potential = dual_basis_potential(grid, spinless) qubit_potential = jordan_wigner(potential) # Check identity. identity = tuple() kinetic_coefficient = qubit_kinetic.terms[identity] potential_coefficient = qubit_potential.terms[identity] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = momentum_vector(indices, grid) paper_kinetic_coefficient += float(n_qubits) * momenta.dot( momenta) / float(4. * n_orbitals) if momenta.any(): potential_contribution = -numpy.pi * float(n_qubits) / float( 2. * momenta.dot(momenta) * volume) paper_potential_coefficient += potential_contribution self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp. for p in range(n_qubits): zp = ((p, 'Z'), ) kinetic_coefficient = qubit_kinetic.terms[zp] potential_coefficient = qubit_potential.terms[zp] paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. for indices in grid.all_points_indices(): momenta = momentum_vector(indices, grid) paper_kinetic_coefficient -= momenta.dot(momenta) / float( 4. * n_orbitals) if momenta.any(): potential_contribution = numpy.pi / float( momenta.dot(momenta) * volume) paper_potential_coefficient += potential_contribution self.assertAlmostEqual(kinetic_coefficient, paper_kinetic_coefficient) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient) # Check Zp Zq. if spinless: spins = [None] else: spins = [0, 1] for indices_a in grid.all_points_indices(): for indices_b in grid.all_points_indices(): paper_kinetic_coefficient = 0. paper_potential_coefficient = 0. position_a = position_vector(indices_a, grid) position_b = position_vector(indices_b, grid) differences = position_b - position_a for spin_a in spins: for spin_b in spins: p = orbital_id(grid, indices_a, spin_a) q = orbital_id(grid, indices_b, spin_b) if p == q: continue zpzq = ((min(p, q), 'Z'), (max(p, q), 'Z')) if zpzq in qubit_potential.terms: potential_coefficient = qubit_potential.terms[zpzq] for indices_c in grid.all_points_indices(): momenta = momentum_vector(indices_c, grid) if momenta.any(): potential_contribution = numpy.pi * numpy.cos( differences.dot(momenta)) / float( momenta.dot(momenta) * volume) paper_potential_coefficient += ( potential_contribution) self.assertAlmostEqual(potential_coefficient, paper_potential_coefficient)