def make_atomic_lattice(nx_atoms, ny_atoms, nz_atoms, spacing, basis, atom_type='H', charge=0, filename=''): """Function to create atomic lattice with n_atoms. Args: nx_atoms: Integer, the length of lattice (in number of atoms). ny_atoms: Integer, the width of lattice (in number of atoms). nz_atoms: Integer, the depth of lattice (in number of atoms). spacing: The spacing between atoms in the lattice in Angstroms. basis: The basis in which to perform the calculation. atom_type: String, the atomic symbol of the element in the ring. this defaults to 'H' for Hydrogen. charge: An integer giving the total molecular charge. Defaults to 0. filename: An optional string to give a filename for the molecule. Returns: molecule: A an instance of the MolecularData class. Raises: MolecularLatticeError: If lattice specification is invalid. """ # Make geometry. geometry = [] for x_dimension in range(nx_atoms): for y_dimension in range(ny_atoms): for z_dimension in range(nz_atoms): x_coord = spacing * x_dimension y_coord = spacing * y_dimension z_coord = spacing * z_dimension geometry += [(atom_type, (x_coord, y_coord, z_coord))] # Set multiplicity. n_atoms = nx_atoms * ny_atoms * nz_atoms n_electrons = n_atoms * periodic_hash_table[atom_type] n_electrons -= charge if (n_electrons % 2): multiplicity = 2 else: multiplicity = 1 # Name molecule. dimensions = bool(nx_atoms > 1) + bool(ny_atoms > 1) + bool(nz_atoms > 1) if dimensions == 1: description = 'linear_{}'.format(spacing) elif dimensions == 2: description = 'planar_{}'.format(spacing) elif dimensions == 3: description = 'cubic_{}'.format(spacing) else: raise MolecularLatticeError('Invalid lattice dimensions.') # Create molecule and return. molecule = MolecularData(geometry, basis, multiplicity, charge, description, filename) return molecule
def setUp(self): # Set up molecule. geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H1-Li1_sto-3g_singlet_1.45') self.molecule = MolecularData( geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() self.hubbard_hamiltonian = fermi_hubbard( 2, 2, 1.0, 4.0, chemical_potential=.2, magnetic_field=0.0, spinless=False)
def make_atom(atom_type, basis, filename=''): """Prepare a molecular data instance for a single element. Args: atom_type: Float giving atomic symbol. basis: The basis in which to perform the calculation. Returns: atom: An instance of the MolecularData class. """ geometry = [(atom_type, (0., 0., 0.))] atomic_number = periodic_hash_table[atom_type] spin = periodic_polarization[atomic_number] / 2. multiplicity = int(2 * spin + 1) atom = MolecularData(geometry, basis, multiplicity, filename=filename) return atom
def make_atomic_ring(n_atoms, spacing, basis, atom_type='H', charge=0, filename=''): """Function to create atomic rings with n_atoms. Note that basic geometry suggests that for spacing L between atoms the radius of the ring should be L / (2 * cos (pi / 2 - theta / 2)) Args: n_atoms: Integer, the number of atoms in the ring. spacing: The spacing between atoms in the ring in Angstroms. basis: The basis in which to perform the calculation. atom_type: String, the atomic symbol of the element in the ring. this defaults to 'H' for Hydrogen. charge: An integer giving the total molecular charge. Defaults to 0. filename: An optional string to give a filename for the molecule. Returns: molecule: A an instance of the MolecularData class. """ # Make geometry. geometry = [] theta = 2. * numpy.pi / float(n_atoms) radius = spacing / (2. * numpy.cos(numpy.pi / 2. - theta / 2.)) for atom in range(n_atoms): x_coord = radius * numpy.cos(atom * theta) y_coord = radius * numpy.sin(atom * theta) geometry += [(atom_type, (x_coord, y_coord, 0.))] # Set multiplicity. n_electrons = n_atoms * periodic_hash_table[atom_type] n_electrons -= charge if (n_electrons % 2): multiplicity = 2 else: multiplicity = 1 # Create molecule and return. description = 'ring_{}'.format(spacing) molecule = MolecularData(geometry, basis, multiplicity, charge, description, filename) return molecule
class GetNumberPreservingSparseOperatorIntegrationTestLiH(unittest.TestCase): def setUp(self): # Set up molecule. geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H1-Li1_sto-3g_singlet_1.45') self.molecule = MolecularData(geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() self.hubbard_hamiltonian = fermi_hubbard(2, 2, 1.0, 4.0, chemical_potential=.2, magnetic_field=0.0, spinless=False) def test_number_on_reference(self): sum_n_op = FermionOperator() sum_sparse_n_op = get_number_preserving_sparse_operator( sum_n_op, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False) space_size = sum_sparse_n_op.shape[0] reference = numpy.zeros((space_size)) reference[0] = 1.0 for i in range(self.molecule.n_qubits): n_op = FermionOperator(((i, 1), (i, 0))) sum_n_op += n_op sparse_n_op = get_number_preserving_sparse_operator( n_op, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False) sum_sparse_n_op += sparse_n_op expectation = reference.dot(sparse_n_op.dot(reference)) if i < self.molecule.n_electrons: assert expectation == 1.0 else: assert expectation == 0.0 convert_after_adding = get_number_preserving_sparse_operator( sum_n_op, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False) assert scipy.sparse.linalg.norm(convert_after_adding - sum_sparse_n_op) < 1E-9 assert reference.dot(sum_sparse_n_op.dot(reference)) - \ self.molecule.n_electrons < 1E-9 def test_space_size_correct(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True) space_size = sparse_ham.shape[0] # Naive Hilbert space size is 2**12, or 4096. assert space_size == 225 def test_hf_energy(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True) space_size = sparse_ham.shape[0] reference = numpy.zeros((space_size)) reference[0] = 1.0 sparse_hf_energy = reference.dot(sparse_ham.dot(reference)) assert numpy.linalg.norm(sparse_hf_energy - self.molecule.hf_energy) < 1E-9 def test_one_body_hf_energy(self): one_body_part = self.molecular_hamiltonian one_body_part.two_body_tensor = numpy.zeros_like( one_body_part.two_body_tensor) one_body_fop = get_fermion_operator(one_body_part) one_body_regular_sparse_op = get_sparse_operator(one_body_fop) make_hf_fop = FermionOperator(((3, 1), (2, 1), (1, 1), (0, 1))) make_hf_sparse_op = get_sparse_operator(make_hf_fop, n_qubits=12) hf_state = numpy.zeros((2**12)) hf_state[0] = 1.0 hf_state = make_hf_sparse_op.dot(hf_state) regular_sparse_hf_energy = \ (hf_state.dot(one_body_regular_sparse_op.dot(hf_state))).real one_body_sparse_op = get_number_preserving_sparse_operator( one_body_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True) space_size = one_body_sparse_op.shape[0] reference = numpy.zeros((space_size)) reference[0] = 1.0 sparse_hf_energy = reference.dot(one_body_sparse_op.dot(reference)) assert numpy.linalg.norm(sparse_hf_energy - regular_sparse_hf_energy) < 1E-9 def test_ground_state_energy(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True) eig_val, _ = scipy.sparse.linalg.eigsh(sparse_ham, k=1, which='SA') assert numpy.abs(eig_val[0] - self.molecule.fci_energy) < 1E-9 def test_doubles_are_subset(self): reference_determinants = [[ True, True, True, True, False, False, False, False, False, False, False, False ], [ True, True, False, False, False, False, True, True, False, False, False, False ]] for reference_determinant in reference_determinants: reference_determinant = numpy.asarray(reference_determinant) doubles_state_array = numpy.asarray( list( _iterate_basis_(reference_determinant, excitation_level=2, spin_preserving=True))) doubles_int_state_array = doubles_state_array.dot( 1 << numpy.arange(doubles_state_array.shape[1])[::-1]) all_state_array = numpy.asarray( list( _iterate_basis_(reference_determinant, excitation_level=4, spin_preserving=True))) all_int_state_array = all_state_array.dot( 1 << numpy.arange(all_state_array.shape[1])[::-1]) for item in doubles_int_state_array: assert item in all_int_state_array for reference_determinant in reference_determinants: reference_determinant = numpy.asarray(reference_determinant) doubles_state_array = numpy.asarray( list( _iterate_basis_(reference_determinant, excitation_level=2, spin_preserving=True))) doubles_int_state_array = doubles_state_array.dot( 1 << numpy.arange(doubles_state_array.shape[1])[::-1]) all_state_array = numpy.asarray( list( _iterate_basis_(reference_determinant, excitation_level=4, spin_preserving=False))) all_int_state_array = all_state_array.dot( 1 << numpy.arange(all_state_array.shape[1])[::-1]) for item in doubles_int_state_array: assert item in all_int_state_array for reference_determinant in reference_determinants: reference_determinant = numpy.asarray(reference_determinant) doubles_state_array = numpy.asarray( list( _iterate_basis_(reference_determinant, excitation_level=2, spin_preserving=False))) doubles_int_state_array = doubles_state_array.dot( 1 << numpy.arange(doubles_state_array.shape[1])[::-1]) all_state_array = numpy.asarray( list( _iterate_basis_(reference_determinant, excitation_level=4, spin_preserving=False))) all_int_state_array = all_state_array.dot( 1 << numpy.arange(all_state_array.shape[1])[::-1]) for item in doubles_int_state_array: assert item in all_int_state_array def test_full_ham_hermitian(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True) assert scipy.sparse.linalg.norm(sparse_ham - sparse_ham.getH()) < 1E-9 def test_full_ham_hermitian_non_spin_preserving(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False) assert scipy.sparse.linalg.norm(sparse_ham - sparse_ham.getH()) < 1E-9 def test_singles_simple_one_body_term_hermitian(self): fop = FermionOperator(((3, 1), (1, 0))) fop_conj = FermionOperator(((1, 1), (3, 0))) sparse_op = get_number_preserving_sparse_operator( fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) sparse_op_conj = get_number_preserving_sparse_operator( fop_conj, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) assert scipy.sparse.linalg.norm(sparse_op - sparse_op_conj.getH()) < 1E-9 def test_singles_simple_two_body_term_hermitian(self): fop = FermionOperator(((3, 1), (8, 1), (1, 0), (4, 0))) fop_conj = FermionOperator(((4, 1), (1, 1), (8, 0), (3, 0))) sparse_op = get_number_preserving_sparse_operator( fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) sparse_op_conj = get_number_preserving_sparse_operator( fop_conj, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) assert scipy.sparse.linalg.norm(sparse_op - sparse_op_conj.getH()) < 1E-9 def test_singles_repeating_two_body_term_hermitian(self): fop = FermionOperator(((3, 1), (1, 1), (5, 0), (1, 0))) fop_conj = FermionOperator(((5, 1), (1, 1), (3, 0), (1, 0))) sparse_op = get_number_preserving_sparse_operator( fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) sparse_op_conj = get_number_preserving_sparse_operator( fop_conj, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) assert scipy.sparse.linalg.norm(sparse_op - sparse_op_conj.getH()) < 1E-9 def test_singles_ham_hermitian(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=1) assert scipy.sparse.linalg.norm(sparse_ham - sparse_ham.getH()) < 1E-9 def test_singles_ham_hermitian_non_spin_preserving(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False, excitation_level=1) assert scipy.sparse.linalg.norm(sparse_ham - sparse_ham.getH()) < 1E-9 def test_cisd_energy(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True, excitation_level=2) eig_val, _ = scipy.sparse.linalg.eigsh(sparse_ham, k=1, which='SA') assert numpy.abs(eig_val[0] - self.molecule.cisd_energy) < 1E-9 def test_cisd_energy_non_spin_preserving(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False, excitation_level=2) eig_val, _ = scipy.sparse.linalg.eigsh(sparse_ham, k=1, which='SA') assert numpy.abs(eig_val[0] - self.molecule.cisd_energy) < 1E-9 def test_cisd_matches_fci_energy_two_electron_hubbard(self): hamiltonian_fop = self.hubbard_hamiltonian sparse_ham_cisd = get_number_preserving_sparse_operator( hamiltonian_fop, 8, 2, spin_preserving=True, excitation_level=2) sparse_ham_fci = get_sparse_operator(hamiltonian_fop, n_qubits=8) eig_val_cisd, _ = scipy.sparse.linalg.eigsh(sparse_ham_cisd, k=1, which='SA') eig_val_fci, _ = scipy.sparse.linalg.eigsh(sparse_ham_fci, k=1, which='SA') assert numpy.abs(eig_val_cisd[0] - eig_val_fci[0]) < 1E-9 def test_weird_determinant_matches_fci_energy_two_electron_hubbard(self): hamiltonian_fop = self.hubbard_hamiltonian sparse_ham_cisd = get_number_preserving_sparse_operator( hamiltonian_fop, 8, 2, spin_preserving=True, excitation_level=2, reference_determinant=numpy.asarray( [False, False, True, True, False, False, False, False])) sparse_ham_fci = get_sparse_operator(hamiltonian_fop, n_qubits=8) eig_val_cisd, _ = scipy.sparse.linalg.eigsh(sparse_ham_cisd, k=1, which='SA') eig_val_fci, _ = scipy.sparse.linalg.eigsh(sparse_ham_fci, k=1, which='SA') assert numpy.abs(eig_val_cisd[0] - eig_val_fci[0]) < 1E-9 def test_number_restricted_spectra_match_molecule(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham_number_preserving = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=False) sparse_ham = get_sparse_operator(hamiltonian_fop, self.molecule.n_qubits) sparse_ham_restricted_number_preserving = jw_number_restrict_operator( sparse_ham, n_electrons=self.molecule.n_electrons, n_qubits=self.molecule.n_qubits) spectrum_from_new_sparse_method = sparse_eigenspectrum( sparse_ham_number_preserving) spectrum_from_old_sparse_method = sparse_eigenspectrum( sparse_ham_restricted_number_preserving) spectral_deviation = numpy.amax( numpy.absolute(spectrum_from_new_sparse_method - spectrum_from_old_sparse_method)) self.assertAlmostEqual(spectral_deviation, 0.) def test_number_restricted_spectra_match_hubbard(self): hamiltonian_fop = self.hubbard_hamiltonian sparse_ham_number_preserving = get_number_preserving_sparse_operator( hamiltonian_fop, 8, 4, spin_preserving=False) sparse_ham = get_sparse_operator(hamiltonian_fop, 8) sparse_ham_restricted_number_preserving = jw_number_restrict_operator( sparse_ham, n_electrons=4, n_qubits=8) spectrum_from_new_sparse_method = sparse_eigenspectrum( sparse_ham_number_preserving) spectrum_from_old_sparse_method = sparse_eigenspectrum( sparse_ham_restricted_number_preserving) spectral_deviation = numpy.amax( numpy.absolute(spectrum_from_new_sparse_method - spectrum_from_old_sparse_method)) self.assertAlmostEqual(spectral_deviation, 0.) def test_number_sz_restricted_spectra_match_molecule(self): hamiltonian_fop = get_fermion_operator(self.molecular_hamiltonian) sparse_ham_number_sz_preserving = get_number_preserving_sparse_operator( hamiltonian_fop, self.molecule.n_qubits, self.molecule.n_electrons, spin_preserving=True) sparse_ham = get_sparse_operator(hamiltonian_fop, self.molecule.n_qubits) sparse_ham_restricted_number_sz_preserving = jw_sz_restrict_operator( sparse_ham, 0, n_electrons=self.molecule.n_electrons, n_qubits=self.molecule.n_qubits) spectrum_from_new_sparse_method = sparse_eigenspectrum( sparse_ham_number_sz_preserving) spectrum_from_old_sparse_method = sparse_eigenspectrum( sparse_ham_restricted_number_sz_preserving) spectral_deviation = numpy.amax( numpy.absolute(spectrum_from_new_sparse_method - spectrum_from_old_sparse_method)) self.assertAlmostEqual(spectral_deviation, 0.) def test_number_sz_restricted_spectra_match_hubbard(self): hamiltonian_fop = self.hubbard_hamiltonian sparse_ham_number_sz_preserving = get_number_preserving_sparse_operator( hamiltonian_fop, 8, 4, spin_preserving=True) sparse_ham = get_sparse_operator(hamiltonian_fop, 8) sparse_ham_restricted_number_sz_preserving = jw_sz_restrict_operator( sparse_ham, 0, n_electrons=4, n_qubits=8) spectrum_from_new_sparse_method = sparse_eigenspectrum( sparse_ham_number_sz_preserving) spectrum_from_old_sparse_method = sparse_eigenspectrum( sparse_ham_restricted_number_sz_preserving) spectral_deviation = numpy.amax( numpy.absolute(spectrum_from_new_sparse_method - spectrum_from_old_sparse_method)) self.assertAlmostEqual(spectral_deviation, 0.)