def sz_integer(bitstring): """Computes the total number of occupied up sites minus the total number of occupied down sites.""" n_sites = len(bitstring) // 2 n_up = len([site for site in range(n_sites) if bitstring[up_index(site)] == '1']) n_down = len([site for site in range(n_sites) if bitstring[down_index(site)] == '1']) return n_up - n_down
def uccsd_singlet_get_packed_amplitudes(single_amplitudes, double_amplitudes, n_qubits, n_electrons): """Convert amplitudes for use with singlet UCCSD The output list contains only those amplitudes that are relevant to singlet UCCSD, in an order suitable for use with the function `uccsd_singlet_generator`. Args: single_amplitudes(ndarray): [NxN] array storing single excitation amplitudes corresponding to t[i,j] * (a_i^\dagger a_j - H.C.) double_amplitudes(ndarray): [NxNxNxN] array storing double excitation amplitudes corresponding to t[i,j,k,l] * (a_i^\dagger a_j a_k^\dagger a_l - H.C.) n_qubits(int): Number of spin-orbitals used to represent the system, which also corresponds to number of qubits in a non-compact map. n_electrons(int): Number of electrons in the physical system. Returns: packed_amplitudes(list): List storing the unique single and double excitation amplitudes for a singlet UCCSD operator. The ordering lists unique single excitations before double excitations. """ n_spatial_orbitals = n_qubits // 2 n_occupied = int(numpy.ceil(n_electrons / 2)) n_virtual = n_spatial_orbitals - n_occupied singles = [] doubles_1 = [] doubles_2 = [] # Get singles and doubles amplitudes associated with one # spatial occupied-virtual pair for p, q in itertools.product(range(n_virtual), range(n_occupied)): # Get indices of spatial orbitals virtual_spatial = n_occupied + p occupied_spatial = q # Get indices of spin orbitals virtual_up = up_index(virtual_spatial) virtual_down = down_index(virtual_spatial) occupied_up = up_index(occupied_spatial) occupied_down = down_index(occupied_spatial) # Get singles amplitude # Just get up amplitude, since down should be the same singles.append(single_amplitudes[virtual_up, occupied_up]) # Get doubles amplitude doubles_1.append(double_amplitudes[virtual_up, occupied_up, virtual_down, occupied_down]) # Get doubles amplitudes associated with two spatial occupied-virtual pairs for (p, q), (r, s) in itertools.combinations( itertools.product(range(n_virtual), range(n_occupied)), 2): # Get indices of spatial orbitals virtual_spatial_1 = n_occupied + p occupied_spatial_1 = q virtual_spatial_2 = n_occupied + r occupied_spatial_2 = s # Get indices of spin orbitals # Just get up amplitudes, since down and cross terms should be the same virtual_1_up = up_index(virtual_spatial_1) occupied_1_up = up_index(occupied_spatial_1) virtual_2_up = up_index(virtual_spatial_2) occupied_2_up = up_index(occupied_spatial_2) # Get amplitude doubles_2.append(double_amplitudes[virtual_1_up, occupied_1_up, virtual_2_up, occupied_2_up]) return singles + doubles_1 + doubles_2
def jw_sz_indices(sz_value, n_qubits, n_electrons=None): r"""Return the indices of basis vectors with fixed Sz under JW encoding. The returned indices label computational basis vectors which lie within the corresponding eigenspace of the Sz operator, .. math:: \begin{align} S^{z} = \frac{1}{2}\sum_{i = 1}^{n}(n_{i, \alpha} - n_{i, \beta}) \end{align} Args: sz_value(float): Desired Sz value. Should be an integer or half-integer. n_qubits(int): Number of qubits defining the total state n_electrons(int, optional): Number of particles to restrict the operator to, if such a restriction is desired Returns: indices(list): The list of indices """ if n_qubits % 2 != 0: raise ValueError('Number of qubits must be even') if not (2. * sz_value).is_integer(): raise ValueError('Sz value must be an integer or half-integer') n_sites = n_qubits // 2 sz_integer = int(2. * sz_value) indices = [] if n_electrons is not None: # Particle number is fixed, so the number of spin-up electrons # (as well as the number of spin-down electrons) is fixed if ((n_electrons + sz_integer) % 2 != 0 or n_electrons < abs(sz_integer)): raise ValueError('The specified particle number and sz value are ' 'incompatible.') num_up = (n_electrons + sz_integer) // 2 num_down = n_electrons - num_up up_occupations = itertools.combinations(range(n_sites), num_up) down_occupations = list( itertools.combinations(range(n_sites), num_down)) # Each arrangement of up spins can be paired with an arrangement # of down spins for up_occupation in up_occupations: up_occupation = [up_index(index) for index in up_occupation] for down_occupation in down_occupations: down_occupation = [down_index(index) for index in down_occupation] occupation = up_occupation + down_occupation indices.append(sum(2 ** (n_qubits - 1 - k) for k in occupation)) else: # Particle number is not fixed if sz_integer < 0: # There are more down spins than up spins more_map = down_index less_map = up_index else: # There are at least as many up spins as down spins more_map = up_index less_map = down_index for n in range(abs(sz_integer), n_sites + 1): # Choose n of the 'more' spin and n - abs(sz_integer) of the # 'less' spin more_occupations = itertools.combinations(range(n_sites), n) less_occupations = list(itertools.combinations( range(n_sites), n - abs(sz_integer))) # Each arrangement of the 'more' spins can be paired with an # arrangement of the 'less' spin for more_occupation in more_occupations: more_occupation = [more_map(index) for index in more_occupation] for less_occupation in less_occupations: less_occupation = [less_map(index) for index in less_occupation] occupation = more_occupation + less_occupation indices.append(sum(2 ** (n_qubits - 1 - k) for k in occupation)) return indices
def uccsd_singlet_generator(packed_amplitudes, n_qubits, n_electrons): """Create a singlet UCCSD generator for a system with n_electrons This function generates a FermionOperator for a UCCSD generator designed to act on a single reference state consisting of n_qubits spin orbitals and n_electrons electrons, that is a spin singlet operator, meaning it conserves spin. Args: packed_amplitudes(ndarray): Compact array storing the unique single and double excitation amplitudes for a singlet UCCSD operator. The ordering lists unique single excitations before double excitations. n_qubits(int): Number of spin-orbitals used to represent the system, which also corresponds to number of qubits in a non-compact map. n_electrons(int): Number of electrons in the physical system. Returns: generator(FermionOperator): Generator of the UCCSD operator that builds the UCCSD wavefunction. """ if n_qubits % 2 != 0: raise ValueError('The total number of spin-orbitals should be even.') n_spatial_orbitals = n_qubits // 2 n_occupied = int(numpy.ceil(n_electrons / 2)) n_virtual = n_spatial_orbitals - n_occupied # Unpack amplitudes n_single_amplitudes = n_occupied * n_virtual # Single amplitudes t1 = packed_amplitudes[:n_single_amplitudes] # Double amplitudes associated with one spatial occupied-virtual pair t2_1 = packed_amplitudes[n_single_amplitudes:2 * n_single_amplitudes] # Double amplitudes associated with two spatial occupied-virtual pairs t2_2 = packed_amplitudes[2 * n_single_amplitudes:] # Initialize operator generator = FermionOperator() # Generate all spin-conserving single and double excitations derived # from one spatial occupied-virtual pair for i, (p, q) in enumerate( itertools.product(range(n_virtual), range(n_occupied))): # Get indices of spatial orbitals virtual_spatial = n_occupied + p occupied_spatial = q # Get indices of spin orbitals virtual_up = up_index(virtual_spatial) virtual_down = down_index(virtual_spatial) occupied_up = up_index(occupied_spatial) occupied_down = down_index(occupied_spatial) # Generate single excitations coeff = t1[i] # Spin up excitation generator += FermionOperator(((virtual_up, 1), (occupied_up, 0)), coeff) generator += FermionOperator(((occupied_up, 1), (virtual_up, 0)), -coeff) # Spin down excitation generator += FermionOperator(((virtual_down, 1), (occupied_down, 0)), coeff) generator += FermionOperator(((occupied_down, 1), (virtual_down, 0)), -coeff) # Generate double excitation coeff = t2_1[i] generator += FermionOperator(((virtual_up, 1), (occupied_up, 0), (virtual_down, 1), (occupied_down, 0)), coeff) generator += FermionOperator(((occupied_down, 1), (virtual_down, 0), (occupied_up, 1), (virtual_up, 0)), -coeff) # Generate all spin-conserving double excitations derived # from two spatial occupied-virtual pairs for i, ((p, q), (r, s)) in enumerate( itertools.combinations( itertools.product(range(n_virtual), range(n_occupied)), 2)): # Get indices of spatial orbitals virtual_spatial_1 = n_occupied + p occupied_spatial_1 = q virtual_spatial_2 = n_occupied + r occupied_spatial_2 = s # Generate double excitations coeff = t2_2[i] spin_index_functions = [up_index, down_index] for s in range(2): # Get the functions which map a spatial orbital index to a # spin orbital index this_index = spin_index_functions[s] other_index = spin_index_functions[1 - s] # Get indices of spin orbitals virtual_1_this = this_index(virtual_spatial_1) occupied_1_this = this_index(occupied_spatial_1) virtual_2_other = other_index(virtual_spatial_2) occupied_2_other = other_index(occupied_spatial_2) virtual_2_this = this_index(virtual_spatial_2) occupied_2_this = this_index(occupied_spatial_2) # p -> q is this spin and s -> r is the other spin generator += FermionOperator( ((virtual_1_this, 1), (occupied_1_this, 0), (virtual_2_other, 1), (occupied_2_other, 0)), coeff) generator += FermionOperator( ((occupied_2_other, 1), (virtual_2_other, 0), (occupied_1_this, 1), (virtual_1_this, 0)), -coeff) # Both are this spin generator += FermionOperator( ((virtual_1_this, 1), (occupied_1_this, 0), (virtual_2_this, 1), (occupied_2_this, 0)), coeff) generator += FermionOperator( ((occupied_2_this, 1), (virtual_2_this, 0), (occupied_1_this, 1), (virtual_1_this, 0)), -coeff) return generator
def fermi_hubbard(x_dimension, y_dimension, tunneling, coulomb, chemical_potential=0., magnetic_field=0., periodic=True, spinless=False, particle_hole_symmetry=False): """Return symbolic representation of a Fermi-Hubbard Hamiltonian. The idea of this model is that some fermions move around on a grid and the energy of the model depends on where the fermions are. 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. In the standard Fermi-Hubbard model (which we call the "spinful" model), there is room for an "up" fermion and a "down" fermion at each site on the grid. In this model, there are a total of `2N` spin-orbitals, where `N = x_dimension * y_dimension` is the number of sites. In the spinless model, there is only one spin-orbital per site for a total of `N`. The Hamiltonian for the spinful 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}) + U \sum_{i} a^\dagger_{i, \\uparrow} a_{i, \\uparrow} a^\dagger_{j, \downarrow} a_{j, \downarrow} \\\\ &- \mu \sum_i \sum_{\sigma} a^\dagger_{i, \sigma} a_{i, \sigma} - h \sum_i (a^\dagger_{i, \\uparrow} a_{i, \\uparrow} - a^\dagger_{i, \downarrow} 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:`U` is the Coulomb potential - :math:`\mu` is the chemical potential - :math:`h` is the magnetic field One can also construct the Hamiltonian for the spinless model, which has the form .. math:: H = - t \sum_{k=1}^{N-1} (a_k^\dagger a_{k + 1} + a_{k+1}^\dagger a_k) + U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1} - \mu \sum_{k=1}^N a_k^\dagger a_k. Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. tunneling (float): The tunneling amplitude :math:`t`. coulomb (float): The attractive local interaction strength :math:`U`. chemical_potential (float, optional): The chemical potential :math:`\mu` at each site. Default value is 0. magnetic_field (float, optional): The magnetic field :math:`h` at each site. Default value is 0. Ignored for the spinless case. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. spinless (bool, optional): If True, return a spinless Fermi-Hubbard model. Default is False. particle_hole_symmetry (bool, optional): If False, the repulsion term corresponds to: .. math:: U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1} If True, the repulsion term is replaced by: .. math:: U \sum_{k=1}^{N-1} (a_k^\dagger a_k - \\frac12) (a_{k+1}^\dagger a_{k+1} - \\frac12) which is unchanged under a particle-hole transformation. Default is False Returns: hubbard_model: An instance of the FermionOperator class. """ tunneling = float(tunneling) coulomb = float(coulomb) chemical_potential = float(chemical_potential) magnetic_field = float(magnetic_field) # Initialize fermion operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = n_sites if not spinless: n_spin_orbitals *= 2 hubbard_model = FermionOperator.zero() # Select particle-hole symmetry if particle_hole_symmetry: coulomb_shift = FermionOperator((), 0.5) else: coulomb_shift = FermionOperator.zero() # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential to the spinless case. The magnetic field # doesn't contribute. if spinless and chemical_potential: hubbard_model += number_operator(n_spin_orbitals, site, -chemical_potential) # With spin, add the chemical potential and magnetic field terms. elif not spinless: 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) # Add local pair interaction terms. operator_1 = number_operator(n_spin_orbitals, up_index(site)) - coulomb_shift operator_2 = number_operator(n_spin_orbitals, down_index(site)) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # 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 (right_neighbor) % x_dimension or (periodic and x_dimension > 2): if spinless: # Add Coulomb term. operator_1 = number_operator(n_spin_orbitals, site, 1.0) - coulomb_shift operator_2 = number_operator(n_spin_orbitals, right_neighbor, 1.0) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (right_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(right_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(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. operator_1 = number_operator(n_spin_orbitals, site) - coulomb_shift operator_2 = number_operator(n_spin_orbitals, bottom_neighbor) - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(bottom_neighbor), 0)) hopping_term = FermionOperator(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) 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 _hubbard(parity, x_dimension, y_dimension, tunneling, local_interaction, coulomb, chemical_potential=0., magnetic_field=0., periodic=True, spinless=False, particle_hole_symmetry=False): r"""Returns a generic Hubbard-style Hamiltonian, to be used by the bose_hubbard and fermi_hubbard wrapper functions. See the corresponding bose_hubbard and fermi_hubbard for descriptions of the various arguments. Args: parity (int): parity=-1 returns the output as an instance of the FermionOperator class. Alternatively, parity=1 returns the output as an instance of the BosonOperator class Returns: hubbard_model: An instance of the FermionOperator or BosonOperator class. """ tunneling = float(tunneling) local_interaction = float(local_interaction) coulomb = float(coulomb) chemical_potential = float(chemical_potential) magnetic_field = float(magnetic_field) if parity == -1: Op = FermionOperator elif parity == 1: Op = BosonOperator # Initialize operator class. n_sites = x_dimension * y_dimension n_spin_orbitals = n_sites if not spinless: n_spin_orbitals *= 2 hubbard_model = Op.zero() # Select particle-hole symmetry if particle_hole_symmetry: coulomb_shift = Op((), 0.5) else: coulomb_shift = Op.zero() # Loop through sites and add terms. for site in range(n_sites): # Add chemical potential to the spinless case. The magnetic field # doesn't contribute. if spinless and chemical_potential: hubbard_model += number_operator(n_spin_orbitals, site, -chemical_potential, parity=parity) # no Pauli-Exclusion principle, spinless particles interact on-site if parity == 1: int_op = number_operator( n_sites, site, local_interaction, parity=1) \ * (number_operator(n_sites, site, parity=1) - Op.identity()) hubbard_model += int_op # With spin, add the chemical potential and magnetic field terms. elif not spinless: hubbard_model += number_operator(n_spin_orbitals, up_index(site), -chemical_potential - magnetic_field, parity=parity) hubbard_model += number_operator(n_spin_orbitals, down_index(site), -chemical_potential + magnetic_field, parity=parity) # Add local pair interaction terms. operator_1 = number_operator( n_spin_orbitals, up_index(site), parity=parity) \ - coulomb_shift operator_2 = number_operator( n_spin_orbitals, down_index(site), parity=parity) \ - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # 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 (right_neighbor) % x_dimension or (periodic and x_dimension > 2): if spinless: # Add Coulomb term. operator_1 = number_operator( n_spin_orbitals, site, 1.0, parity=parity) - coulomb_shift operator_2 = number_operator( n_spin_orbitals, right_neighbor, 1.0, parity=parity) \ - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (right_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(right_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(right_neighbor), 0)) hopping_term = Op(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. operator_1 = number_operator( n_spin_orbitals, site, parity=parity) - coulomb_shift operator_2 = number_operator( n_spin_orbitals, bottom_neighbor, parity=parity) \ - coulomb_shift hubbard_model += coulomb * operator_1 * operator_2 # Add hopping term. operators = ((site, 1), (bottom_neighbor, 0)) else: # Add hopping term. operators = ((up_index(site), 1), (up_index(bottom_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) operators = ((down_index(site), 1), (down_index(bottom_neighbor), 0)) hopping_term = Op(operators, -tunneling) hubbard_model += hopping_term hubbard_model += hermitian_conjugated(hopping_term) return hubbard_model
def test_up_down(self): self.assertTrue(numpy.isclose(down_index(2), 5)) self.assertTrue(numpy.isclose(down_index(5), 11))