def setUp(self): self.n_qubits = 5 self.constant = 1.7 self.chemical_potential = 2. # Obtain random Hermitian and antisymmetric matrices self.hermitian_mat = random_hermitian_matrix(self.n_qubits) self.antisymmetric_mat = random_antisymmetric_matrix(self.n_qubits) self.combined_hermitian = ( self.hermitian_mat - self.chemical_potential * numpy.eye(self.n_qubits)) # Initialize a particle-number-conserving Hamiltonian self.quad_ham_pc = QuadraticHamiltonian(self.hermitian_mat, constant=self.constant) # Initialize a non-particle-number-conserving Hamiltonian self.quad_ham_npc = QuadraticHamiltonian(self.hermitian_mat, self.antisymmetric_mat, self.constant, self.chemical_potential) # Initialize the sparse operators and get their ground energies self.quad_ham_pc_sparse = get_sparse_operator(self.quad_ham_pc) self.quad_ham_npc_sparse = get_sparse_operator(self.quad_ham_npc) self.pc_ground_energy, self.pc_ground_state = get_ground_state( self.quad_ham_pc_sparse) self.npc_ground_energy, self.npc_ground_state = get_ground_state( self.quad_ham_npc_sparse)
def test_plane_wave_hamiltonian_integration(self): length_set = [2, 3, 4] spinless_set = [True, False] length_scale = 1.1 for geometry in [[('H', (0, )), ('H', (0.8, ))], [('H', (0.1, ))], [('H', (0.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=False) h_dual_basis = plane_wave_hamiltonian( grid, geometry, spinless, False, include_constant=False) # Test for Hermiticity plane_wave_operator = get_sparse_operator(h_plane_wave) dual_operator = get_sparse_operator(h_dual_basis) self.assertTrue(is_hermitian((plane_wave_operator))) self.assertTrue(is_hermitian(dual_operator)) 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 = np.amax(h_plane_wave_spectrum - h_dual_basis_spectrum) min_diff = np.amin(h_plane_wave_spectrum - h_dual_basis_spectrum) self.assertAlmostEqual(max_diff, 0) self.assertAlmostEqual(min_diff, 0)
def test_ground_state_particle_nonconserving(self): """Test getting the ground state preparation circuit for a Hamiltonian that does not conserve particle number.""" for n_qubits in self.n_qubits_range: # Initialize a particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False, True) # Compute the true ground state sparse_operator = get_sparse_operator(quadratic_hamiltonian) ground_energy, _ = get_ground_state(sparse_operator) # Obtain the circuit circuit_description, start_orbitals = ( gaussian_state_preparation_circuit(quadratic_hamiltonian)) # Initialize the starting state state = jw_configuration_state(start_orbitals, n_qubits) # Apply the circuit particle_hole_transformation = ( jw_sparse_particle_hole_transformation_last_mode(n_qubits)) for parallel_ops in circuit_description: for op in parallel_ops: if op == 'pht': state = particle_hole_transformation.dot(state) else: i, j, theta, phi = op state = jw_sparse_givens_rotation( i, j, theta, phi, n_qubits).dot(state) # Check that the state obtained using the circuit is a ground state difference = sparse_operator * state - ground_energy * state discrepancy = numpy.amax(numpy.abs(difference)) self.assertAlmostEqual(discrepancy, 0)
def test_excited_state_particle_nonconserving(self): """Test getting an excited state of a Hamiltonian that conserves particle number.""" for n_qubits in self.n_qubits_range: # Initialize a non-particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False) # Pick some orbitals to occupy num_occupied_orbitals = numpy.random.randint(1, n_qubits + 1) occupied_orbitals = numpy.random.choice(range(n_qubits), num_occupied_orbitals, False) # Compute the Gaussian state circuit_energy, gaussian_state = jw_get_gaussian_state( quadratic_hamiltonian, occupied_orbitals) # Compute the true energy orbital_energies, constant = ( quadratic_hamiltonian.orbital_energies()) energy = numpy.sum(orbital_energies[occupied_orbitals]) + constant # Check that the energies match self.assertAlmostEqual(energy, circuit_energy) # Check that the state obtained using the circuit is an eigenstate # with the correct eigenvalue sparse_operator = get_sparse_operator(quadratic_hamiltonian) difference = (sparse_operator * gaussian_state - energy * gaussian_state) discrepancy = numpy.amax(numpy.abs(difference)) self.assertAlmostEqual(discrepancy, 0)
def setUp(self): geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') self.molecule = MolecularData(geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() # Get FCI RDM. self.fci_rdm = self.molecule.get_molecular_rdm(use_fci=1) # Get explicit coefficients. self.nuclear_repulsion = self.molecular_hamiltonian.constant self.one_body = self.molecular_hamiltonian.one_body_tensor self.two_body = self.molecular_hamiltonian.two_body_tensor # Get fermion Hamiltonian. self.fermion_hamiltonian = normal_ordered( get_fermion_operator(self.molecular_hamiltonian)) # Get qubit Hamiltonian. self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Get the sparse matrix. self.hamiltonian_matrix = get_sparse_operator( self.molecular_hamiltonian)
def test_particle_conserving(self): for n_qubits in self.n_qubits_range: # Initialize a particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, True) sparse_operator = get_sparse_operator(quadratic_hamiltonian) # Diagonalize the Hamiltonian using the circuit circuit_description = reversed( quadratic_hamiltonian.diagonalizing_circuit()) for parallel_ops in circuit_description: for op in parallel_ops: i, j, theta, phi = op gate = jw_sparse_givens_rotation(i, j, theta, phi, n_qubits) sparse_operator = ( gate.getH().dot(sparse_operator).dot(gate)) # Check that the result is diagonal diag = scipy.sparse.diags(sparse_operator.diagonal()) difference = sparse_operator - diag discrepancy = 0. if difference.nnz: discrepancy = max(abs(difference.data)) numpy.testing.assert_allclose(discrepancy, 0.0, atol=1e-7) # Check that the eigenvalues are in the expected order orbital_energies, constant = ( quadratic_hamiltonian.orbital_energies()) for index in range(2**n_qubits): bitstring = bin(index)[2:].zfill(n_qubits) subset = [j for j in range(n_qubits) if bitstring[j] == '1'] energy = sum([orbital_energies[j] for j in subset]) + constant self.assertAlmostEqual(sparse_operator[index, index], energy)
def eigenspectrum(operator, n_qubits=None): """Compute the eigenspectrum of an operator. WARNING: This function has cubic runtime in dimension of Hilbert space operator, which might be exponential. NOTE: This function does not currently support QuadOperator and BosonOperator. Args: operator: QubitOperator, InteractionOperator, FermionOperator, PolynomialTensor, or InteractionRDM. n_qubits (int): number of qubits/modes in operator. if None, will be counted. Returns: spectrum: dense numpy array of floats giving eigenspectrum. """ if isinstance(operator, (QuadOperator, BosonOperator)): raise TypeError('Operator of invalid type.') from openfermion.linalg.sparse_tools import (get_sparse_operator, sparse_eigenspectrum) sparse_operator = get_sparse_operator(operator, n_qubits) spectrum = sparse_eigenspectrum(sparse_operator) return spectrum
def test_error_operator_xyz(self): terms = [QubitOperator('X1'), QubitOperator('Y1'), QubitOperator('Z1')] expected = numpy.array([[-2. / 3, 1. / 3 + 1.j / 6, 0., 0.], [1. / 3 - 1.j / 6, 2. / 3, 0., 0.], [0., 0., -2. / 3, 1. / 3 + 1.j / 6], [0., 0., 1. / 3 - 1.j / 6, 2. / 3]]) sparse_op = get_sparse_operator(error_operator(terms)) matrix = sparse_op.todense() self.assertTrue(numpy.allclose(matrix, expected), ("Got " + str(matrix)))
def test_bravyi_kitaev_fast_generate_fermions(self): n_qubits = count_qubits(self.molecular_hamiltonian) # test for generating two fermions edge_matrix = bksf.bravyi_kitaev_fast_edge_matrix( self.molecular_hamiltonian) edge_matrix_indices = numpy.array( numpy.nonzero( numpy.triu(edge_matrix) - numpy.diag(numpy.diag(edge_matrix)))) fermion_generation_operator = bksf.generate_fermions( edge_matrix_indices, 2, 3) fermion_generation_sp_matrix = get_sparse_operator( fermion_generation_operator) fermion_generation_matrix = fermion_generation_sp_matrix.toarray() bksf_vacuum_state_operator = bksf.vacuum_operator(edge_matrix_indices) bksf_vacuum_state_sp_matrix = get_sparse_operator( bksf_vacuum_state_operator) bksf_vacuum_state_matrix = bksf_vacuum_state_sp_matrix.toarray() vacuum_state = numpy.zeros((2**(n_qubits), 1)) vacuum_state[0] = 1. bksf_vacuum_state = numpy.dot(bksf_vacuum_state_matrix, vacuum_state) two_fermion_state = numpy.dot(fermion_generation_matrix, bksf_vacuum_state) # using total number operator to check the number of fermions generated tot_number_operator = bksf.number_operator(self.molecular_hamiltonian) number_operator_sp_matrix = get_sparse_operator(tot_number_operator) number_operator_matrix = number_operator_sp_matrix.toarray() tot_fermions = numpy.dot( two_fermion_state.conjugate().T, numpy.dot(number_operator_matrix, two_fermion_state)) # checking the occupation number of site 2 and 3 number_operator_2 = bksf.number_operator(self.molecular_hamiltonian, 2) number_operator_3 = bksf.number_operator(self.molecular_hamiltonian, 3) number_operator_23 = number_operator_2 + number_operator_3 number_operator_23_sp_matrix = get_sparse_operator(number_operator_23) number_operator_23_matrix = number_operator_23_sp_matrix.toarray() tot_23_fermions = numpy.dot( two_fermion_state.conjugate().T, numpy.dot(number_operator_23_matrix, two_fermion_state)) self.assertTrue(2.0 - float(tot_fermions.real) < 1e-13) self.assertTrue(2.0 - float(tot_23_fermions.real) < 1e-13)
def test_hubbard_reduce_symmetry_qubits(self): for i in range(4): n_sites = i + 2 n_ferm = n_sites hub_hamil, n_orb = set_1D_hubbard(n_sites) # Use test function to reduce the qubits. hub_qbt = (symmetry_conserving_bravyi_kitaev( hub_hamil, n_orb, n_ferm)) sparse_op = get_sparse_operator(hub_hamil) ground_energy, _ = jw_get_ground_state_at_particle_number( sparse_op, n_ferm) self.assertAlmostEqual(eigenspectrum(hub_qbt)[0], ground_energy)
def test_ground_state_particle_nonconserving(self): """Test getting the ground state of a Hamiltonian that does not conserve particle number.""" for n_qubits in self.n_qubits_range: # Initialize a non-particle-number-conserving Hamiltonian quadratic_hamiltonian = random_quadratic_hamiltonian( n_qubits, False) # Compute the true ground state sparse_operator = get_sparse_operator(quadratic_hamiltonian) ground_energy, _ = get_ground_state(sparse_operator) # Compute the ground state using the circuit circuit_energy, circuit_state = ( jw_get_gaussian_state(quadratic_hamiltonian)) # Check that the energies match self.assertAlmostEqual(ground_energy, circuit_energy) # Check that the state obtained using the circuit is a ground state difference = (sparse_operator * circuit_state - ground_energy * circuit_state) discrepancy = numpy.amax(numpy.abs(difference)) self.assertAlmostEqual(discrepancy, 0)
def test_ucc_h2_singlet(self): geometry = [('H', (0., 0., 0.)), ('H', (0., 0., 0.7414))] basis = 'sto-3g' multiplicity = 1 filename = os.path.join(THIS_DIRECTORY, 'data', 'H2_sto-3g_singlet_0.7414') self.molecule = MolecularData(geometry, basis, multiplicity, filename=filename) self.molecule.load() # Get molecular Hamiltonian. self.molecular_hamiltonian = self.molecule.get_molecular_hamiltonian() # Get FCI RDM. self.fci_rdm = self.molecule.get_molecular_rdm(use_fci=1) # Get explicit coefficients. self.nuclear_repulsion = self.molecular_hamiltonian.constant self.one_body = self.molecular_hamiltonian.one_body_tensor self.two_body = self.molecular_hamiltonian.two_body_tensor # Get fermion Hamiltonian. self.fermion_hamiltonian = normal_ordered( get_fermion_operator(self.molecular_hamiltonian)) # Get qubit Hamiltonian. self.qubit_hamiltonian = jordan_wigner(self.fermion_hamiltonian) # Get the sparse matrix. self.hamiltonian_matrix = get_sparse_operator( self.molecular_hamiltonian) # Test UCCSD for accuracy against FCI using loaded t amplitudes. ucc_operator = uccsd_generator(self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps) hf_state = jw_hartree_fock_state(self.molecule.n_electrons, count_qubits(self.qubit_hamiltonian)) uccsd_sparse = jordan_wigner_sparse(ucc_operator) uccsd_state = scipy.sparse.linalg.expm_multiply(uccsd_sparse, hf_state) expected_uccsd_energy = expectation(self.hamiltonian_matrix, uccsd_state) self.assertAlmostEqual(expected_uccsd_energy, self.molecule.fci_energy, places=4) print("UCCSD ENERGY: {}".format(expected_uccsd_energy)) # Test CCSD singlet for precise match against FCI using loaded t # amplitudes packed_amplitudes = uccsd_singlet_get_packed_amplitudes( self.molecule.ccsd_single_amps, self.molecule.ccsd_double_amps, self.molecule.n_qubits, self.molecule.n_electrons) ccsd_operator = uccsd_singlet_generator(packed_amplitudes, self.molecule.n_qubits, self.molecule.n_electrons, anti_hermitian=False) ccsd_sparse_r = jordan_wigner_sparse(ccsd_operator) ccsd_sparse_l = jordan_wigner_sparse( -hermitian_conjugated(ccsd_operator)) ccsd_state_r = scipy.sparse.linalg.expm_multiply( ccsd_sparse_r, hf_state) ccsd_state_l = scipy.sparse.linalg.expm_multiply( ccsd_sparse_l, hf_state) expected_ccsd_energy = ccsd_state_l.conjugate().dot( self.hamiltonian_matrix.dot(ccsd_state_r)) self.assertAlmostEqual(expected_ccsd_energy, self.molecule.fci_energy)