def _spinful_fermi_hubbard_model(x_dimension, y_dimension, tunneling, coulomb, chemical_potential, magnetic_field, periodic, particle_hole_symmetry): # Initialize operator. n_sites = x_dimension * y_dimension n_spin_orbitals = 2 * n_sites hubbard_model = FermionOperator() # Loop through sites and add terms. for site in range(n_sites): # Get indices of right and bottom neighbors right_neighbor = _right_neighbor(site, x_dimension, y_dimension, periodic) bottom_neighbor = _bottom_neighbor(site, x_dimension, y_dimension, periodic) # Avoid double-counting edges when one of the dimensions is 2 # and the system is periodic if x_dimension == 2 and periodic and site % 2 == 1: right_neighbor = None if y_dimension == 2 and periodic and site >= x_dimension: bottom_neighbor = None # Add hopping terms with neighbors to the right and bottom. if right_neighbor is not None: hubbard_model += _hopping_term(up_index(site), up_index(right_neighbor), -tunneling) hubbard_model += _hopping_term(down_index(site), down_index(right_neighbor), -tunneling) if bottom_neighbor is not None: hubbard_model += _hopping_term(up_index(site), up_index(bottom_neighbor), -tunneling) hubbard_model += _hopping_term(down_index(site), down_index(bottom_neighbor), -tunneling) # Add local pair Coulomb interaction terms. hubbard_model += _coulomb_interaction_term(n_spin_orbitals, up_index(site), down_index(site), coulomb, particle_hole_symmetry) # Add chemical potential and magnetic field terms. hubbard_model += number_operator(n_spin_orbitals, up_index(site), -chemical_potential - magnetic_field) hubbard_model += number_operator(n_spin_orbitals, down_index(site), -chemical_potential + magnetic_field) return hubbard_model
def test_number_operator_nosite(self): op = number_operator(4, parity=-1) expected = (FermionOperator(((0, 1), (0, 0))) + FermionOperator( ((1, 1), (1, 0))) + FermionOperator( ((2, 1), (2, 0))) + FermionOperator(((3, 1), (3, 0)))) self.assertEqual(op, expected) op = number_operator(4, parity=1) expected = (BosonOperator(((0, 1), (0, 0))) + BosonOperator( ((1, 1), (1, 0))) + BosonOperator(((2, 1), (2, 0))) + BosonOperator( ((3, 1), (3, 0)))) self.assertTrue(op == expected)
def _coulomb_interaction_term(n_sites, i, j, coefficient, particle_hole_symmetry, bosonic=False): op_class = BosonOperator if bosonic else FermionOperator number_operator_i = number_operator(n_sites, i, parity=2 * bosonic - 1) number_operator_j = number_operator(n_sites, j, parity=2 * bosonic - 1) if particle_hole_symmetry: number_operator_i -= op_class((), 0.5) number_operator_j -= op_class((), 0.5) return coefficient * number_operator_i * number_operator_j
def _spinless_fermi_hubbard_model(x_dimension, y_dimension, tunneling, coulomb, chemical_potential, magnetic_field, periodic, particle_hole_symmetry): # Initialize operator. n_sites = x_dimension * y_dimension hubbard_model = FermionOperator() # Loop through sites and add terms. for site in range(n_sites): # Get indices of right and bottom neighbors right_neighbor = _right_neighbor(site, x_dimension, y_dimension, periodic) bottom_neighbor = _bottom_neighbor(site, x_dimension, y_dimension, periodic) # Avoid double-counting edges when one of the dimensions is 2 # and the system is periodic if x_dimension == 2 and periodic and site % 2 == 1: right_neighbor = None if y_dimension == 2 and periodic and site >= x_dimension: bottom_neighbor = None # Add terms that couple with neighbors to the right and bottom. if right_neighbor is not None: # Add hopping term hubbard_model += _hopping_term(site, right_neighbor, -tunneling) # Add local Coulomb interaction term hubbard_model += _coulomb_interaction_term(n_sites, site, right_neighbor, coulomb, particle_hole_symmetry) if bottom_neighbor is not None: # Add hopping term hubbard_model += _hopping_term(site, bottom_neighbor, -tunneling) # Add local Coulomb interaction term hubbard_model += _coulomb_interaction_term(n_sites, site, bottom_neighbor, coulomb, particle_hole_symmetry) # Add chemical potential. The magnetic field doesn't contribute. hubbard_model += number_operator(n_sites, site, -chemical_potential) return hubbard_model
def mean_field_dwave(x_dimension, y_dimension, tunneling, sc_gap, chemical_potential=0., periodic=True): r"""Return symbolic representation of a BCS mean-field d-wave Hamiltonian. The Hamiltonians of this model live on a grid of dimensions `x_dimension` x `y_dimension`. The grid can have periodic boundary conditions or not. Each site on the grid can have an "up" fermion and a "down" fermion. Therefore, there are a total of `2N` spin-orbitals, where `N = x_dimension * y_dimension` is the number of sites. The Hamiltonian for this model has the form .. math:: \begin{align} H = &- t \sum_{\langle i,j \rangle} \sum_\sigma (a^\dagger_{i, \sigma} a_{j, \sigma} + a^\dagger_{j, \sigma} a_{i, \sigma}) - \mu \sum_i \sum_{\sigma} a^\dagger_{i, \sigma} a_{i, \sigma} \\ &- \sum_{\langle i,j \rangle} \Delta_{ij} (a^\dagger_{i, \uparrow} a^\dagger_{j, \downarrow} - a^\dagger_{i, \downarrow} a^\dagger_{j, \uparrow} + a_{j, \downarrow} a_{i, \uparrow} - a_{j, \uparrow} a_{i, \downarrow}) \end{align} where - The indices :math:`\langle i, j \rangle` run over pairs :math:`i` and :math:`j` of sites that are connected to each other in the grid - :math:`\sigma \in \{\uparrow, \downarrow\}` is the spin - :math:`t` is the tunneling amplitude - :math:`\Delta_{ij}` is equal to :math:`+\Delta/2` for horizontal edges and :math:`-\Delta/2` for vertical edges, where :math:`\Delta` is the superconducting gap. - :math:`\mu` is the chemical potential Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. tunneling (float): The tunneling amplitude :math:`t`. sc_gap (float): The superconducting gap :math:`\Delta` chemical_potential (float, optional): The chemical potential :math:`\mu` at each site. Default value is 0. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. Returns: mean_field_dwave_model: An instance of the FermionOperator class. """ # Initialize fermion operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = 2 * n_sites mean_field_dwave_model = FermionOperator() # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential mean_field_dwave_model += number_operator(n_spin_orbitals, up_index(site), -chemical_potential) mean_field_dwave_model += number_operator(n_spin_orbitals, down_index(site), -chemical_potential) # Index coupled orbitals. right_neighbor = site + 1 bottom_neighbor = site + x_dimension # Account for periodic boundaries. if periodic: if (x_dimension > 2) and ((site + 1) % x_dimension == 0): right_neighbor -= x_dimension if (y_dimension > 2) and (site + x_dimension + 1 > n_sites): bottom_neighbor -= x_dimension * y_dimension # Add transition to neighbor on right if (site + 1) % x_dimension or (periodic and x_dimension > 2): # Add spin-up hopping term. operators = ((up_index(site), 1), (up_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add spin-down hopping term operators = ((down_index(site), 1), (down_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add pairing term operators = ((up_index(site), 1), (down_index(right_neighbor), 1)) pairing_term = FermionOperator(operators, sc_gap / 2.) operators = ((down_index(site), 1), (up_index(right_neighbor), 1)) pairing_term += FermionOperator(operators, -sc_gap / 2.) mean_field_dwave_model -= pairing_term mean_field_dwave_model -= hermitian_conjugated(pairing_term) # Add transition to neighbor below. if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2): # Add spin-up hopping term. operators = ((up_index(site), 1), (up_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add spin-down hopping term operators = ((down_index(site), 1), (down_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) mean_field_dwave_model += hopping_term mean_field_dwave_model += hermitian_conjugated(hopping_term) # Add pairing term operators = ((up_index(site), 1), (down_index(bottom_neighbor), 1)) pairing_term = FermionOperator(operators, -sc_gap / 2.) operators = ((down_index(site), 1), (up_index(bottom_neighbor), 1)) pairing_term += FermionOperator(operators, sc_gap / 2.) mean_field_dwave_model -= pairing_term mean_field_dwave_model -= hermitian_conjugated(pairing_term) # Return. return mean_field_dwave_model
def test_bad_parity(self): with self.assertRaises(ValueError): number_operator(4, parity=2)
def test_fermion_number_operator_site(self): op = number_operator(3, 2, 1j, -1) self.assertEqual(op, FermionOperator(((2, 1), (2, 0))) * 1j) op = number_operator(3, 2, 1j, 1) self.assertTrue(op == BosonOperator(((2, 1), (2, 0))) * 1j)
def reverse_jordan_wigner(qubit_operator, n_qubits=None): r"""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 = op_utils.count_qubits(qubit_operator) if n_qubits < op_utils.count_qubits(qubit_operator): raise ValueError('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 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 bose_hubbard(x_dimension, y_dimension, tunneling, interaction, chemical_potential=0., dipole=0., periodic=True): r"""Return symbolic representation of a Bose-Hubbard Hamiltonian. In this model, bosons move around on a lattice, and the energy of the model depends on where the bosons are. The lattice is described by a 2D grid, with dimensions `x_dimension` x `y_dimension`. It is also possible to specify if the grid has periodic boundary conditions or not. The Hamiltonian for the Bose-Hubbard model has the form .. math:: H = - t \sum_{\langle i, j \rangle} (b_i^\dagger b_j + b_j^\dagger b_i) + V \sum_{\langle i, j \rangle} b_i^\dagger b_i b_j^\dagger b_j + \frac{U}{2} \sum_i b_i^\dagger b_i (b_i^\dagger b_i - 1) - \mu \sum_i b_i^\dagger b_i. where - The indices :math:`\langle i, j \rangle` run over pairs :math:`i` and :math:`j` of nodes that are connected to each other in the grid - :math:`t` is the tunneling amplitude - :math:`U` is the on-site interaction potential - :math:`\mu` is the chemical potential - :math:`V` is the dipole or nearest-neighbour interaction potential Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. tunneling (float): The tunneling amplitude :math:`t`. interaction (float): The attractive local interaction strength :math:`U`. chemical_potential (float, optional): The chemical potential :math:`\mu` at each site. Default value is 0. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. dipole (float): The attractive dipole interaction strength :math:`V`. Returns: bose_hubbard_model: An instance of the BosonOperator class. """ # Initialize operator. n_sites = x_dimension * y_dimension hubbard_model = BosonOperator() # Loop through sites and add terms. for site in range(n_sites): # Get indices of right and bottom neighbors right_neighbor = _right_neighbor(site, x_dimension, y_dimension, periodic) bottom_neighbor = _bottom_neighbor(site, x_dimension, y_dimension, periodic) # Avoid double-counting edges when one of the dimensions is 2 # and the system is periodic if x_dimension == 2 and periodic and site % 2 == 1: right_neighbor = None if y_dimension == 2 and periodic and site >= x_dimension: bottom_neighbor = None # Add terms that couple with neighbors to the right and bottom. if right_neighbor is not None: # Add hopping term hubbard_model += _hopping_term(site, right_neighbor, -tunneling, bosonic=True) # Add local Coulomb interaction term hubbard_model += _coulomb_interaction_term( n_sites, site, right_neighbor, dipole, particle_hole_symmetry=False, bosonic=True) if bottom_neighbor is not None: # Add hopping term hubbard_model += _hopping_term(site, bottom_neighbor, -tunneling, bosonic=True) # Add local Coulomb interaction term hubbard_model += _coulomb_interaction_term( n_sites, site, bottom_neighbor, dipole, particle_hole_symmetry=False, bosonic=True) # Add on-site interaction. hubbard_model += ( number_operator(n_sites, site, 0.5 * interaction, parity=1) * (number_operator(n_sites, site, parity=1) - BosonOperator(()))) # Add chemical potential. hubbard_model += number_operator(n_sites, site, -chemical_potential, parity=1) return hubbard_model