def test_bk_jw_majoranas(self): # Check if the Majorana operators have the same spectrum # irrespectively of the transform. n_qubits = 7 a = FermionOperator(((1, 0),)) a_dag = FermionOperator(((1, 1),)) c = a + a_dag d = 1j * (a_dag - a) c_spins = [jordan_wigner(c), bravyi_kitaev_tree(c)] d_spins = [jordan_wigner(d), bravyi_kitaev_tree(d)] c_sparse = [get_sparse_operator(c_spins[0]), get_sparse_operator(c_spins[1])] d_sparse = [get_sparse_operator(d_spins[0]), get_sparse_operator(d_spins[1])] c_spectrum = [eigenspectrum(c_spins[0]), eigenspectrum(c_spins[1])] d_spectrum = [eigenspectrum(d_spins[0]), eigenspectrum(d_spins[1])] self.assertAlmostEqual(0., numpy.amax(numpy.absolute(d_spectrum[0] - d_spectrum[1])))
def test_bravyi_kitaev_tree_transform(self): # Check that the QubitOperators are two-term. lowering = bravyi_kitaev_tree(FermionOperator(((3, 0),))) raising = bravyi_kitaev_tree(FermionOperator(((3, 1),))) self.assertEqual(len(raising.terms), 2) self.assertEqual(len(lowering.terms), 2) # Test the locality invariant for N=2^d qubits # (c_j majorana is always log2N+1 local on qubits) n_qubits = 16 invariant = numpy.log2(n_qubits) + 1 for index in range(n_qubits): operator = bravyi_kitaev_tree(FermionOperator(((index, 0),)), n_qubits) qubit_terms = operator.terms.items() # Get the majorana terms. for item in qubit_terms: coeff = item[1] # Identify the c majorana terms by real # coefficients and check their length. if not isinstance(coeff, complex): self.assertEqual(len(item[0]), invariant) # Hardcoded coefficient test on 16 qubits lowering = bravyi_kitaev_tree(FermionOperator(((9, 0),)), n_qubits) raising = bravyi_kitaev_tree(FermionOperator(((9, 1),)), n_qubits) correct_operators_c = ((7, 'Z'), (8, 'Z'), (9, 'X'), (11, 'X'), (15, 'X')) correct_operators_d = ((7, 'Z'), (9, 'Y'), (11, 'X'), (15, 'X')) self.assertEqual(lowering.terms[correct_operators_c], 0.5) self.assertEqual(lowering.terms[correct_operators_d], 0.5j) self.assertEqual(raising.terms[correct_operators_d], -0.5j) self.assertEqual(raising.terms[correct_operators_c], 0.5)
def test_bk_jw_number_operator(self): # Check if number operator has the same spectrum in both # BK and JW representations n = number_operator(1, 0) jw_n = jordan_wigner(n) bk_n = bravyi_kitaev_tree(n) # Diagonalize and make sure the spectra are the same. jw_spectrum = eigenspectrum(jw_n) bk_spectrum = eigenspectrum(bk_n) self.assertAlmostEqual(0., numpy.amax( numpy.absolute(jw_spectrum - bk_spectrum)))
def test_bk_jw_hopping_operator(self): # Check if the spectrum fits for a single hoppping operator ho = FermionOperator(((1, 1), (4, 0))) + FermionOperator( ((4, 1), (1, 0))) jw_ho = jordan_wigner(ho) bk_ho = bravyi_kitaev_tree(ho) # Diagonalize and make sure the spectra are the same. jw_spectrum = eigenspectrum(jw_ho) bk_spectrum = eigenspectrum(bk_ho) self.assertAlmostEqual(0., numpy.amax( numpy.absolute(jw_spectrum - bk_spectrum)))
def test_bk_jw_number_operator_scaled(self): # Check if number operator has the same spectrum in both # JW and BK representations n_qubits = 1 n = number_operator(n_qubits, 0, coefficient=2) # eigenspectrum (0,2) jw_n = jordan_wigner(n) bk_n = bravyi_kitaev_tree(n) # Diagonalize and make sure the spectra are the same. jw_spectrum = eigenspectrum(jw_n) bk_spectrum = eigenspectrum(bk_n) self.assertAlmostEqual(0., numpy.amax( numpy.absolute(jw_spectrum - bk_spectrum)))
def test_bk_jw_integration(self): # This is a legacy test, which was a minimal failing example when # optimization for hermitian operators was used. # Minimal failing example: fo = FermionOperator(((3, 1),)) jw = jordan_wigner(fo) bk = bravyi_kitaev_tree(fo) jw_spectrum = eigenspectrum(jw) bk_spectrum = eigenspectrum(bk) self.assertAlmostEqual(0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum)))
def load_and_transform(filename, orbitals, transform): # Load data print('--- loading molecule ---') molecule = MolecularData(filename=filename) print('filename: {}'.format(molecule.filename)) #print('n_atoms: {}'.format(molecule.n_atoms)) #print('n_electrons: {}'.format(molecule.n_electrons)) #print('n_orbitals: {}'.format(molecule.n_orbitals)) #print('Canonical Orbitals: {}'.format(molecule.canonical_orbitals)) #print('n_qubits: {}'.format(molecule.n_qubits)) # get the Hamiltonian for a specific choice of active space # set the Hamiltonian parameters occupied_orbitals, active_orbitals = orbitals molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=range(occupied_orbitals), active_indices=range(active_orbitals)) # map the operator to fermions and then qubits fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian) # get interaction operator interaction_hamiltonian = get_interaction_operator(fermion_hamiltonian) if transform is 'JW': qubit_h = jordan_wigner(fermion_hamiltonian) qubit_h.compress() elif transform is 'BK': qubit_h = bravyi_kitaev(fermion_hamiltonian) qubit_h.compress() elif transform is 'BKSF': qubit_h = bravyi_kitaev_fast(interaction_hamiltonian) qubit_h.compress() elif transform is 'BKT': qubit_h = bravyi_kitaev_tree(fermion_hamiltonian) qubit_h.compress() elif transform is 'PC': qubit_h = binary_code_transform(fermion_hamiltonian, parity_code(2 * active_orbitals)) qubit_h.compress() else: print('ERROR: Unrecognized qubit transformation: {}'.format(transform)) sys.exit(2) return qubit_h
def test_bk_jw_number_operators(self): # Check if a number operator has the same spectrum in both # JW and BK representations n_qubits = 2 n1 = number_operator(n_qubits, 0) n2 = number_operator(n_qubits, 1) n = n1 + n2 jw_n = jordan_wigner(n) bk_n = bravyi_kitaev_tree(n) # Diagonalize and make sure the spectra are the same. jw_spectrum = eigenspectrum(jw_n) bk_spectrum = eigenspectrum(bk_n) self.assertAlmostEqual(0., numpy.amax( numpy.absolute(jw_spectrum - bk_spectrum)))
def test_bk_jw_integration_original(self): # This is a legacy test, which was an example proposed by Ryan, # failing when optimization for hermitian operators was used. fermion_operator = FermionOperator(((3, 1), (2, 1), (1, 0), (0, 0)), -4.3) fermion_operator += FermionOperator(((3, 1), (1, 0)), 8.17) fermion_operator += 3.2 * FermionOperator() # Map to qubits and compare matrix versions. jw_qubit_operator = jordan_wigner(fermion_operator) bk_qubit_operator = bravyi_kitaev_tree(fermion_operator) # Diagonalize and make sure the spectra are the same. jw_spectrum = eigenspectrum(jw_qubit_operator) bk_spectrum = eigenspectrum(bk_qubit_operator) self.assertAlmostEqual(0., numpy.amax(numpy.absolute(jw_spectrum - bk_spectrum)), places=5)
def symmetry_conserving_bravyi_kitaev_HOTFIX(fermion_hamiltonian, active_orbitals, active_fermions): """ Returns the qubit Hamiltonian for the fermionic Hamiltonian supplied, with two qubits removed using conservation of electron spin and number, as described in arXiv:1701.08213. Args: fermion_hamiltonian: A fermionic hamiltonian obtained using OpenFermion. An instance of the FermionOperator class. active_orbitals: Int type object. The number of active orbitals being considered for the system. active_fermions: Int type object. The number of active fermions being considered for the system (note, this is less than the number of electrons in a molecule if some orbitals have been assumed filled). Returns: qubit_hamiltonian: The qubit Hamiltonian corresponding to the supplied fermionic Hamiltonian, with two qubits removed using spin symmetries. WARNING: Reorders orbitals from the default even-odd ordering to all spin-up orbitals, then all spin-down orbitals. Raises: ValueError if fermion_hamiltonian isn't of the type FermionOperator, or active_orbitals isn't an integer, or active_fermions isn't an integer. Notes: This function reorders the spin orbitals as all spin-up, then all spin-down. It uses the OpenFermion bravyi_kitaev_tree mapping, rather than the bravyi-kitaev mapping. Caution advised when using with a Fermi-Hubbard Hamiltonian; this technique correctly reduces the Hamiltonian only for the lowest energy even and odd fermion number states, not states with an arbitrary number of fermions. """ # Catch errors if inputs are of wrong type. if type(fermion_hamiltonian) is not FermionOperator: raise ValueError('Supplied operator should be an instance ' 'of FermionOperator class') if type(active_orbitals) is not int: raise ValueError('Number of active orbitals should be an integer.') if type(active_fermions) is not int: raise ValueError('Number of active fermions should be an integer.') # Arrange spins up then down, then BK map to qubit Hamiltonian. ''' MODIFIED -- to make sure, that single operators of a Hamiltonian also give valid results ''' fermion_hamiltonian_reorder = reorder(fermion_hamiltonian, up_then_down, num_modes=active_orbitals) # added num_modes info qubit_hamiltonian = bravyi_kitaev_tree(fermion_hamiltonian_reorder, n_qubits=active_orbitals) # added n_qubits info ''' END MODIFIED ''' qubit_hamiltonian.compress() # Allocates the parity factors for the orbitals as in arXiv:1704.05018. remainder = active_fermions % 4 if remainder == 0: parity_final_orb = 1 parity_middle_orb = 1 elif remainder == 1: parity_final_orb = -1 parity_middle_orb = -1 elif remainder == 2: parity_final_orb = 1 parity_middle_orb = -1 else: parity_final_orb = -1 parity_middle_orb = 1 # Removes the final qubit, then the middle qubit. qubit_hamiltonian = edit_hamiltonian_for_spin(qubit_hamiltonian, active_orbitals, parity_final_orb) qubit_hamiltonian = edit_hamiltonian_for_spin(qubit_hamiltonian, active_orbitals/2, parity_middle_orb) qubit_hamiltonian = prune_unused_indices(qubit_hamiltonian) return qubit_hamiltonian
def test_bk_n_qubits_too_small(self): with self.assertRaises(ValueError): bravyi_kitaev_tree(FermionOperator('2^ 3^ 5 0'), n_qubits=4)
def test_bk_identity(self): self.assertTrue(bravyi_kitaev_tree(FermionOperator(())) == QubitOperator(()))