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(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_jordan_wigner_transm_op(self): n = number_operator(self.n_qubits) n_jw = jordan_wigner(n) self.assertEqual(self.n_qubits + 1, len(n_jw.terms)) self.assertEqual(self.n_qubits / 2., n_jw.terms[()]) for qubit in range(self.n_qubits): operators = ((qubit, 'Z'), ) self.assertEqual(n_jw.terms[operators], -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(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_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(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_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_jw_restrict_operator(self): """Test the scheme for restricting JW encoded operators to number""" # Make a Hamiltonian that cares mostly about number of electrons n_qubits = 6 target_electrons = 3 penalty_const = 100. number_sparse = jordan_wigner_sparse(number_operator(n_qubits)) bias_sparse = jordan_wigner_sparse( sum([ FermionOperator(((i, 1), (i, 0)), 1.0) for i in range(n_qubits) ], FermionOperator())) hamiltonian_sparse = penalty_const * ( number_sparse - target_electrons * scipy.sparse.identity(2**n_qubits) ).dot(number_sparse - target_electrons * scipy.sparse.identity(2**n_qubits)) + bias_sparse restricted_hamiltonian = jw_number_restrict_operator( hamiltonian_sparse, target_electrons, n_qubits) true_eigvals, _ = eigh(hamiltonian_sparse.A) test_eigvals, _ = eigh(restricted_hamiltonian.A) self.assertAlmostEqual(norm(true_eigvals[:20] - test_eigvals[:20]), 0.0)
def test_transm_number(self): n = number_operator(self.n_qubits, 3) n_jw = jordan_wigner(n) self.assertEqual(n_jw.terms[((3, 'Z'), )], -0.5) self.assertEqual(n_jw.terms[()], 0.5) self.assertEqual(len(n_jw.terms), 2)
def reverse_jordan_wigner(qubit_operator, n_qubits=None): """Transforms a QubitOperator into a FermionOperator using the Jordan-Wigner transform. Operators are mapped as follows: Z_j -> I - 2 a^\dagger_j a_j X_j -> (a^\dagger_j + a_j) Z_{j-1} Z_{j-2} .. Z_0 Y_j -> i (a^\dagger_j - a_j) Z_{j-1} Z_{j-2} .. Z_0 Args: qubit_operator: the QubitOperator to be transformed. n_qubits: the number of qubits term acts on. If not set, defaults to the maximum qubit number acted on by term. Returns: transformed_term: An instance of the FermionOperator class. Raises: TypeError: Input must be a QubitOperator. TypeError: Invalid number of qubits specified. TypeError: Pauli operators must be X, Y or Z. """ if not isinstance(qubit_operator, QubitOperator): raise TypeError('Input must be a QubitOperator.') if n_qubits is None: n_qubits = count_qubits(qubit_operator) if n_qubits < count_qubits(qubit_operator): raise TypeError('Invalid number of qubits specified') # Loop through terms. transformed_operator = FermionOperator() for term in qubit_operator.terms: transformed_term = FermionOperator(()) if term: working_term = QubitOperator(term) pauli_operator = term[-1] while pauli_operator is not None: # Handle Pauli Z. if pauli_operator[1] == 'Z': transformed_pauli = FermionOperator( ()) + number_operator(n_qubits, pauli_operator[0], -2.) # Handle Pauli X and Y. else: raising_term = FermionOperator(((pauli_operator[0], 1), )) lowering_term = FermionOperator(((pauli_operator[0], 0), )) if pauli_operator[1] == 'Y': raising_term *= 1.j lowering_term *= -1.j elif pauli_operator[1] != 'X': raise TypeError('Pauli operators must be X, Y, or Z') transformed_pauli = raising_term + lowering_term # Account for the phase terms. for j in reversed(range(pauli_operator[0])): z_term = QubitOperator(((j, 'Z'), )) working_term = z_term * working_term term_key = list(working_term.terms)[0] transformed_pauli *= working_term.terms[term_key] working_term.terms[list(working_term.terms)[0]] = 1. # Get next non-identity operator acting below 'working_qubit'. assert len(working_term.terms) == 1 working_qubit = pauli_operator[0] - 1 for working_operator in reversed(list(working_term.terms)[0]): if working_operator[0] <= working_qubit: pauli_operator = working_operator break else: pauli_operator = None # Multiply term by transformed operator. transformed_term *= transformed_pauli # Account for overall coefficient transformed_term *= qubit_operator.terms[term] transformed_operator += transformed_term return transformed_operator
def fermi_hubbard(x_dimension, y_dimension, tunneling, coulomb, chemical_potential=None, magnetic_field=None, periodic=True, spinless=False): """Return symbolic representation of a Fermi-Hubbard Hamiltonian. Args: x_dimension: An integer giving the number of sites in width. y_dimension: An integer giving the number of sites in height. tunneling: A float giving the tunneling amplitude. coulomb: A float giving the attractive local interaction strength. chemical_potential: An optional float giving the potential of each site. Default value is None. magnetic_field: An optional float giving a magnetic field at each site. Default value is None. periodic: If True, add periodic boundary conditions. spinless: An optional Boolean. If False, each site has spin up orbitals and spin down orbitals. If True, return a spinless Fermi-Hubbard model. verbose: An optional Boolean. If True, print all second quantized terms. Returns: hubbard_model: An instance of the FermionOperator class. """ # Initialize fermion operator class. n_sites = x_dimension * y_dimension if spinless: n_spin_orbitals = n_sites else: n_spin_orbitals = 2 * n_sites hubbard_model = FermionOperator((), 0.0) # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential and magnetic field terms. if chemical_potential and spinless: x_index = site % x_dimension y_index = (site - 1) // x_dimension sign = (-1.) ** (x_index + y_index) coefficient = sign * chemical_potential hubbard_model += number_operator( n_spin_orbitals, site, coefficient) if chemical_potential and not spinless: coefficient = -1. * chemical_potential hubbard_model += number_operator( n_spin_orbitals, up(site), coefficient) hubbard_model += number_operator( n_spin_orbitals, down(site), coefficient) if magnetic_field and not spinless: coefficient = magnetic_field hubbard_model += number_operator( n_spin_orbitals, up(site), -coefficient) hubbard_model += number_operator( n_spin_orbitals, down(site), coefficient) # Add local pair interaction terms. if not spinless: operators = ((up(site), 1), (up(site), 0), (down(site), 1), (down(site), 0)) hubbard_model += FermionOperator(operators, coulomb) # Index coupled orbitals. right_neighbor = site + 1 bottom_neighbor = site + x_dimension # Account periodic boundaries. if periodic: if (x_dimension > 2) and ((site + 1) % x_dimension == 0): right_neighbor -= (x_dimension - 1) if (y_dimension > 2) and (site + x_dimension + 1 > n_sites): bottom_neighbor -= (x_dimension - 1) * y_dimension # Add transition to neighbor on right. if (site + 1) % x_dimension or (periodic and x_dimension > 2): if spinless: # Add Coulomb term. operators = ((site, 1), (site, 0), (right_neighbor, 1), (right_neighbor, 0)) hubbard_model += FermionOperator(operators, coulomb) # Add hopping term. operators = ((site, 1), (right_neighbor, 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) else: # Add hopping term. operators = ((up(site), 1), (up(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down(site), 1), (down(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) # Add transition to neighbor below. if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2): if spinless: # Add Coulomb term. operators = ((site, 1), (site, 0), (bottom_neighbor, 1), (bottom_neighbor, 0)) hubbard_model += FermionOperator(operators, coulomb) # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) else: # Add hopping term. operators = ((up(site), 1), (up(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down(site), 1), (down(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) # Return. return hubbard_model