def test_consistency(self): """Test consistency with JW for FermionOperators.""" # Random interaction operator n_qubits = 5 iop = random_interaction_operator(n_qubits, real=False) op1 = jordan_wigner(iop) op2 = jordan_wigner(get_fermion_operator(iop)) self.assertEqual(op1, op2) # Interaction operator from molecule geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(DATA_DIRECTORY, 'H1-Li1_sto-3g_singlet_1.45') molecule = MolecularData(geometry, basis, multiplicity, filename=filename) molecule.load() iop = molecule.get_molecular_hamiltonian() op1 = jordan_wigner(iop) op2 = jordan_wigner(get_fermion_operator(iop)) self.assertEqual(op1, op2)
def test_mul(self): new_tensor = self.polynomial_tensor_a * self.polynomial_tensor_b self.assertEqual(new_tensor, self.polynomial_tensor_axb) new_tensor_1 = self.polynomial_tensor_a * 2. new_tensor_2 = 2. * self.polynomial_tensor_a self.assertEqual( new_tensor_1, PolynomialTensor({ (): self.constant * 2., (1, 0): self.one_body_a * 2., (1, 1, 0, 0): self.two_body_a * 2. })) self.assertEqual( new_tensor_2, PolynomialTensor({ (): self.constant * 2., (1, 0): self.one_body_a * 2., (1, 1, 0, 0): self.two_body_a * 2. })) self.assertEqual(get_fermion_operator(new_tensor_1), get_fermion_operator(self.polynomial_tensor_a) * 2.) self.assertEqual(get_fermion_operator(new_tensor_2), get_fermion_operator(self.polynomial_tensor_a) * 2.)
def test_multiply(self): n_qubits = 5 op1 = random_diagonal_coulomb_hamiltonian(n_qubits) op2 = op1 * 1.5 op3 = 1.5 * op1 self.assertEqual( get_fermion_operator(op1) * 1.5, get_fermion_operator(op2), get_fermion_operator(op3))
def test_one_body_square_decomposition(self): # Initialize H2 InteractionOperator. n_qubits = 4 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') molecule = MolecularData(filename=filename) molecule_interaction = molecule.get_molecular_hamiltonian() get_fermion_operator(molecule_interaction) two_body_coefficients = molecule_interaction.two_body_tensor # Decompose. eigenvalues, one_body_squares, _, _ = ( low_rank_two_body_decomposition(two_body_coefficients)) rank = eigenvalues.size for l in range(rank): one_body_operator = FermionOperator() for p, q in itertools.product(range(n_qubits), repeat=2): term = ((p, 1), (q, 0)) coefficient = one_body_squares[l, p, q] one_body_operator += FermionOperator(term, coefficient) one_body_squared = one_body_operator ** 2 # Get the squared one-body operator via one-body decomposition. if abs(eigenvalues[l]) < 1e-6: with self.assertRaises(ValueError): prepare_one_body_squared_evolution(one_body_squares[l]) continue else: density_density_matrix, basis_transformation_matrix = ( prepare_one_body_squared_evolution(one_body_squares[l])) two_body_operator = FermionOperator() for p, q in itertools.product(range(n_qubits), repeat=2): term = ((p, 1), (p, 0), (q, 1), (q, 0)) coefficient = density_density_matrix[p, q] two_body_operator += FermionOperator(term, coefficient) # Confirm that the rotations diagonalize the one-body squares. hopefully_diagonal = basis_transformation_matrix.dot( numpy.dot(one_body_squares[l], numpy.transpose(numpy.conjugate( basis_transformation_matrix)))) diagonal = numpy.diag(hopefully_diagonal) difference = hopefully_diagonal - numpy.diag(diagonal) self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference))) density_density_alternative = numpy.outer(diagonal, diagonal) difference = density_density_alternative - density_density_matrix self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference))) # Test spectra. one_body_squared_spectrum = eigenspectrum(one_body_squared) two_body_spectrum = eigenspectrum(two_body_operator) difference = two_body_spectrum - one_body_squared_spectrum self.assertAlmostEqual(0., numpy.amax(numpy.absolute(difference)))
def test_hermiticity(self): n_qubits = 5 # Real case iop = random_interaction_operator(n_qubits, True) ferm_op = get_fermion_operator(iop) self.assertTrue(is_hermitian(ferm_op)) # Complex case iop = random_interaction_operator(n_qubits, False) ferm_op = get_fermion_operator(iop) self.assertTrue(is_hermitian(ferm_op))
def test_integration_random_diagonal_coulomb_hamiltonian(self): hamiltonian1 = normal_ordered( get_fermion_operator( random_diagonal_coulomb_hamiltonian(n_qubits=7))) hamiltonian2 = normal_ordered( get_fermion_operator( random_diagonal_coulomb_hamiltonian(n_qubits=7))) reference = normal_ordered(commutator(hamiltonian1, hamiltonian2)) result = commutator_ordered_diagonal_coulomb_with_two_body_operator( hamiltonian1, hamiltonian2) self.assertTrue(result.isclose(reference))
def decompose_hamiltonian(mol_name, hf_data, mapping="jordan_wigner", docc_mo_indices=None, active_mo_indices=None): r"""Decomposes the electronic Hamiltonian into a linear combination of Pauli operators using OpenFermion tools. **Example usage:** >>> decompose_hamiltonian('h2', './pyscf/sto-3g/', mapping='bravyi_kitaev') (-0.04207897696293986+0j) [] + (0.04475014401986122+0j) [X0 Z1 X2] + (0.04475014401986122+0j) [X0 Z1 X2 Z3] +(0.04475014401986122+0j) [Y0 Z1 Y2] + (0.04475014401986122+0j) [Y0 Z1 Y2 Z3] +(0.17771287459806262+0j) [Z0] + (0.17771287459806265+0j) [Z0 Z1] +(0.1676831945625423+0j) [Z0 Z1 Z2] + (0.1676831945625423+0j) [Z0 Z1 Z2 Z3] +(0.12293305054268105+0j) [Z0 Z2] + (0.12293305054268105+0j) [Z0 Z2 Z3] +(0.1705973832722409+0j) [Z1] + (-0.2427428049645989+0j) [Z1 Z2 Z3] +(0.1762764080276107+0j) [Z1 Z3] + (-0.2427428049645989+0j) [Z2] Args: mol_name (str): name of the molecule hf_data (str): path to the directory containing the file with the Hartree-Fock electronic structure mapping (str): optional argument to specify the fermion-to-qubit mapping Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'`` docc_mo_indices (list): indices of doubly-occupied molecular orbitals, i.e., the orbitals that are not correlated in the many-body wave function active_mo_indices (list): indices of active molecular orbitals, i.e., the orbitals used to build the correlated many-body wave function Returns: transformed_operator: instance of the QubitOperator class representing the electronic Hamiltonian """ # loading HF data from a hdf5 file molecule = MolecularData( filename=os.path.join(hf_data.strip(), mol_name.strip())) # getting the terms entering the second-quantized Hamiltonian terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=docc_mo_indices, active_indices=active_mo_indices) # generating the fermionic Hamiltonian fermionic_hamiltonian = get_fermion_operator(terms_molecular_hamiltonian) mapping = mapping.strip().lower() if mapping not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.". format(mapping)) # fermionic-to-qubit transformation of the Hamiltonian if mapping == "bravyi_kitaev": return bravyi_kitaev(fermionic_hamiltonian) return jordan_wigner(fermionic_hamiltonian)
def lih_hamiltonian(): """ Generate test Hamiltonian from LiH. Args: None Return: hamiltonian: FermionicOperator spectrum: List of energies. """ geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))] active_space_start = 1 active_space_stop = 3 molecule = MolecularData(geometry, 'sto-3g', 1, description="1.45") molecule.load() molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=range(active_space_start), active_indices=range(active_space_start, active_space_stop)) hamiltonian = get_fermion_operator(molecular_hamiltonian) spectrum = eigenspectrum(hamiltonian) return hamiltonian, spectrum
def test_random_quadratic(self): n_qubits = 5 quad_ham = random_quadratic_hamiltonian(n_qubits, True) ferm_op = get_fermion_operator(quad_ham) self.assertTrue( jordan_wigner(ferm_op) == jordan_wigner( get_diagonal_coulomb_hamiltonian(ferm_op)))
def get_qubit_hamiltonian(mol, geometry, basis, charge=0, multiplicity=1, qubit_transf='bk'): ''' Generating qubit hamiltonina of the given molecules with specified geometry, basis sets, charge and multiplicity Give its qubit form using specified transformation ''' g = get_molecular_data(mol, geometry) mol = MolecularData(g, basis, multiplicity, charge) mol = run_pyscf(mol) ham = mol.get_molecular_hamiltonian() hamf = get_fermion_operator(ham) if qubit_transf == 'bk': hamq = bravyi_kitaev(hamf) elif qubit_transf == 'jw': hamq = jordan_wigner(hamf) else: raise (ValueError(qubit_transf, 'Unknown transformation specified')) return hamq
def test_majorana_form(self): """Test getting the Majorana form.""" majorana_matrix, majorana_constant = self.quad_ham_npc.majorana_form() # Convert the Majorana form to a FermionOperator majorana_op = FermionOperator((), majorana_constant) normalization = 1. / numpy.sqrt(2.) for i in range(2 * self.n_qubits): if i < self.n_qubits: left_op = majorana_operator((i, 0), normalization) else: left_op = majorana_operator((i - self.n_qubits, 1), normalization) for j in range(2 * self.n_qubits): if j < self.n_qubits: right_op = majorana_operator( (j, 0), majorana_matrix[i, j] * normalization) else: right_op = majorana_operator( (j - self.n_qubits, 1), majorana_matrix[i, j] * normalization) majorana_op += .5j * left_op * right_op # Get FermionOperator for original Hamiltonian fermion_operator = normal_ordered( get_fermion_operator(self.quad_ham_npc)) self.assertTrue(normal_ordered(majorana_op) == fermion_operator)
def exp_hamiltoniantrot_H2(time, atomic_distance, trotter_order): """ Here we are using some packages related to quantum chemistry to obtain hamiltonian of H2 molecule. The we are translating this hamiltonian into sequence of pyquil gates. And finally, we are trotterazing the exponent of the hamiltonian (exp(-iHt)) of our system and returning the obtained pyquil program :param time: is t in the exp(iHt). Note the + sign. :param atomic_distance: the distance between to H atoms in H2 molecule :return: program of the Trotter-Suzuki decomposition of hamiltonian exp(iHt) for H2 molecule """ geometry = [['H', [0, 0, 0]], ['H', [0, 0, atomic_distance]]] # H--H distance = 0.74angstrom basis = 'sto-3g' multiplicity = 1 # (2S+1) charge = 0 h2_molecule = MolecularData(geometry, basis, multiplicity, charge) h2_molecule = run_pyscf(h2_molecule) h2_qubit_hamiltonian = jordan_wigner(get_fermion_operator(h2_molecule.get_molecular_hamiltonian())) pyquil_h2_qubit_hamiltonian = qubitop_to_pyquilpauli(h2_qubit_hamiltonian) return trotterization(pyquil_h2_qubit_hamiltonian, float(time), trotter_order)
def test_exception(self): n_qubits = 6 random_operator = get_fermion_operator( random_interaction_operator(n_qubits)) bad_term = ((2, 1), (3, 1)) random_operator += FermionOperator(bad_term) with self.assertRaises(OperatorSpecificationError): chemist_operator = chemist_ordered(random_operator)
def test_convert_forward_back(self): n_qubits = 6 random_operator = get_fermion_operator( random_interaction_operator(n_qubits)) chemist_operator = chemist_ordered(random_operator) normalized_chemist = normal_ordered(chemist_operator) difference = normalized_chemist - normal_ordered(random_operator) self.assertAlmostEqual(0., difference.induced_norm())
def decompose(hf_file, mapping="jordan_wigner", core=None, active=None): r"""Decomposes the molecular Hamiltonian into a linear combination of Pauli operators using OpenFermion tools. This function uses OpenFermion functions to build the second-quantized electronic Hamiltonian of the molecule and map it to the Pauli basis using the Jordan-Wigner or Bravyi-Kitaev transformation. Args: hf_file (str): absolute path to the hdf5-formatted file with the Hartree-Fock electronic structure mapping (str): Specifies the transformation to map the fermionic Hamiltonian to the Pauli basis. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``. core (list): indices of core orbitals, i.e., the orbitals that are not correlated in the many-body wave function active (list): indices of active orbitals, i.e., the orbitals used to build the correlated many-body wave function Returns: QubitOperator: an instance of OpenFermion's ``QubitOperator`` **Example** >>> decompose('./pyscf/sto-3g/h2', mapping='bravyi_kitaev') (-0.04207897696293986+0j) [] + (0.04475014401986122+0j) [X0 Z1 X2] + (0.04475014401986122+0j) [X0 Z1 X2 Z3] +(0.04475014401986122+0j) [Y0 Z1 Y2] + (0.04475014401986122+0j) [Y0 Z1 Y2 Z3] +(0.17771287459806262+0j) [Z0] + (0.17771287459806265+0j) [Z0 Z1] +(0.1676831945625423+0j) [Z0 Z1 Z2] + (0.1676831945625423+0j) [Z0 Z1 Z2 Z3] +(0.12293305054268105+0j) [Z0 Z2] + (0.12293305054268105+0j) [Z0 Z2 Z3] +(0.1705973832722409+0j) [Z1] + (-0.2427428049645989+0j) [Z1 Z2 Z3] +(0.1762764080276107+0j) [Z1 Z3] + (-0.2427428049645989+0j) [Z2] """ # loading HF data from the hdf5 file molecule = MolecularData(filename=hf_file.strip()) # getting the terms entering the second-quantized Hamiltonian terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=core, active_indices=active ) # generating the fermionic Hamiltonian fermionic_hamiltonian = get_fermion_operator(terms_molecular_hamiltonian) mapping = mapping.strip().lower() if mapping not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.".format(mapping) ) # fermionic-to-qubit transformation of the Hamiltonian if mapping == "bravyi_kitaev": return bravyi_kitaev(fermionic_hamiltonian) return jordan_wigner(fermionic_hamiltonian)
def run_simulation(bond_lengths): # Load saved file for H3. basis = 'sto-3g' spin = 2 # Set Hamiltonian parameters. active_space_start = 1 active_space_stop = 3 # Set calculation parameters. run_scf = 1 run_mp2 = 1 run_cisd = 0 run_ccsd = 0 run_fci = 1 delete_input = True delete_output = True # Begin Running Simulation, Convert distance_counter to angstroms geometry = [('H', (0., 0., bond_lengths[1][-1] * 0.529177249)), ('H', (0., 0., bond_lengths[2][-1] * 0.529177249)), ('H', (0., 0., bond_lengths[3][-1] * 0.529177249))] # Generate and populate instance of MolecularData. molecule = MolecularData(geometry, basis, spin, description="h3") molecule = run_pyscf(molecule, run_scf=run_scf, run_mp2=run_mp2, run_cisd=run_cisd, run_ccsd=run_ccsd, run_fci=run_fci) # Use a Jordan-Wigner encoding, and compress to remove 0 imaginary components molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=range(active_space_start), active_indices=range(active_space_start, active_space_stop)) fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian) qubit_hamiltonian = jordan_wigner(fermion_hamiltonian) qubit_hamiltonian.compress() compiler_engine = uccsd_trotter_engine() initial_energy = energy_objective(opt_amplitudes) # Run VQE Optimization to find new CCSD parameters opt_result = minimize(energy_objective, opt_amplitudes, method="CG", options={'disp': True}) opt_energy, opt_amplitudes = opt_result.fun, opt_result.x return ({ "Name": molecule.name, "UCCSD Energy": opt_energy, "FCI Energy": molecule.fci_energy })
def test_molecular_operator_consistency(self): # Initialize H2 InteractionOperator. n_qubits = 4 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') molecule = MolecularData(filename=filename) molecule_interaction = molecule.get_molecular_hamiltonian() molecule_operator = get_fermion_operator(molecule_interaction) constant = molecule_interaction.constant one_body_coefficients = molecule_interaction.one_body_tensor two_body_coefficients = molecule_interaction.two_body_tensor # Perform decomposition. eigenvalues, one_body_squares, one_body_corrections, trunc_error = ( low_rank_two_body_decomposition(two_body_coefficients)) self.assertAlmostEqual(trunc_error, 0.) # Build back operator constant and one-body components. decomposed_operator = FermionOperator((), constant) for p, q in itertools.product(range(n_qubits), repeat=2): term = ((p, 1), (q, 0)) coefficient = (one_body_coefficients[p, q] + one_body_corrections[p, q]) decomposed_operator += FermionOperator(term, coefficient) # Build back two-body component. for l in range(one_body_squares.shape[0]): one_body_operator = FermionOperator() for p, q in itertools.product(range(n_qubits), repeat=2): term = ((p, 1), (q, 0)) coefficient = one_body_squares[l, p, q] if abs(eigenvalues[l]) > 1e-6: self.assertTrue(is_hermitian(one_body_squares[l])) one_body_operator += FermionOperator(term, coefficient) decomposed_operator += eigenvalues[l] * (one_body_operator ** 2) # Test for consistency. difference = normal_ordered(decomposed_operator - molecule_operator) self.assertAlmostEqual(0., difference.induced_norm()) # Decompose with slightly negative operator that must use eigen molecule = MolecularData(filename=filename) molecule.two_body_integrals[0, 0, 0, 0] -= 1 eigenvalues, one_body_squares, one_body_corrections, trunc_error = ( low_rank_two_body_decomposition(two_body_coefficients)) self.assertAlmostEqual(trunc_error, 0.) # Check for property errors with self.assertRaises(TypeError): eigenvalues, one_body_squares, _, trunc_error = ( low_rank_two_body_decomposition( two_body_coefficients + 0.01j, truncation_threshold=1., final_rank=1))
def get_BK_ham(distance, basis="sto-3g", multiplicity=1, charge=1): geometry = [["He", [0, 0, 0]], ["H", [0, 0, distance]]] description = str(distance) molecule = MolecularData(geometry, basis, multiplicity, charge, description) molecule = run_pyscf(molecule, run_fci=1) bk_hamiltonian = get_sparse_operator( bravyi_kitaev( get_fermion_operator(molecule.get_molecular_hamiltonian()))) return bk_hamiltonian
def get_qubit_hamiltonian(g, basis, charge=0, spin=1, qubit_transf='jw'): ## Create OpenFermion molecule #mol = gto.Mole() #mol.atom = g #mol.basis = basis #mol.spin = spin #mol.charge = charge #mol.symmetry = False ##mol.max_memory = 1024 #mol.build() multiplicity = spin + 1 # spin here is 2S ? mol = MolecularData(g, basis, multiplicity, charge) #mol.load() # Convert to PySCF molecule and run SCF print("Running run_pyscf...") print(f"Time: {time()}") print("=" * 20) mol = run_pyscf(mol) # Freeze some orbitals? occupied_indices = range(183) active_indices = range(183, 183+5) # Get Hamiltonian print("Running get_molecular_hamiltonian...") print(f"Time: {time()}") print("=" * 20) ham = mol.get_molecular_hamiltonian( occupied_indices=occupied_indices, active_indices=active_indices) import pdb; pdb.set_trace() print("Running get_fermion_operator...") print(f"Time: {time()}") print("=" * 20) hamf = get_fermion_operator(ham) print(f"Running {qubit_transf}...") print(f"Time: {time()}") print("=" * 20) if qubit_transf == 'bk': hamq = bravyi_kitaev(hamf) elif qubit_transf == 'jw': hamq = jordan_wigner(hamf) else: raise(ValueError(qubit_transf, 'Unknown transformation specified')) return remove_complex(hamq)
def full_ham_terms(spacing,dead_space = range(1),active_space = range(1,4)): molecule=molecule_data(spacing) molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=dead_space, active_indices=active_space) fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian) qubit_hamiltonian = bravyi_kitaev(fermion_hamiltonian) qubit_hamiltonian.compress() terms_dict=qubit_hamiltonian.terms return terms_dict
def lih_hamiltonian(): geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))] active_space_start = 1 active_space_stop = 3 molecule = MolecularData(geometry, 'sto-3g', 1, description="1.45") molecule.load() molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=range(active_space_start), active_indices=range(active_space_start, active_space_stop)) hamiltonian = get_fermion_operator(molecular_hamiltonian) ground_state_energy = eigenspectrum(hamiltonian)[0] return hamiltonian, ground_state_energy
def energy_levels(separation): g = [('H', (0., 0., 0.)), ('H', (0., 0., separation))] d = str(separation) m = MolecularData(g, basis, multiplicity, description=d, data_directory=data_directory) run_psi4(m, run_fci=True, delete_output=True) _, s = get_ground_state( get_sparse_operator( normal_ordered( get_fermion_operator( m.get_molecular_hamiltonian( active_indices=range(active_spatial_orbitals)))))) h = normal_ordered( get_fermion_operator( m.get_molecular_hamiltonian( active_indices=range(active_spatial_orbitals + virtual_spatial_orbitals)))) o = vqse_operators(s) return generalized_eigenvalues(h_matrix(h, o, s), s_matrix(o, s))
def LiH_sto3g(): """ Generates the Hamiltonian for LiH in the STO-3G basis, at a distance of 1.45 A. """ geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.45))] molecule = MolecularData(geometry, 'sto-3g', 1, description="1.45") molecule.load() molecular_hamiltonian = molecule.get_molecular_hamiltonian() hamiltonian = get_fermion_operator(molecular_hamiltonian) num_electrons = molecule.n_electrons num_orbitals = 2 * molecule.n_orbitals return hamiltonian, num_orbitals, num_electrons