def sx_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the sx operator. $$ \begin{align} S^{x} = \frac{1}{2}\sum_{i = 1}^{n}(S^{+} + S^{-}) \end{align} $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits // 2). Returns: operator (FermionOperator): corresponding to the sx operator over n_spatial_orbitals. Note: The indexing convention used is that even indices correspond to spin-up (alpha) modes and odd indices correspond to spin-down (beta) modes. """ if not isinstance(n_spatial_orbitals, int): raise TypeError("n_orbitals must be specified as an integer") operator = FermionOperator() for ni in range(n_spatial_orbitals): operator += FermionOperator(((up_index(ni), 1), (down_index(ni), 0)), .5) operator += FermionOperator(((down_index(ni), 1), (up_index(ni), 0)), .5) return operator
def sz_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the sz operator. $$ \begin{align} S^{z} = \frac{1}{2}\sum_{i = 1}^{n}(n_{i, \alpha} - n_{i, \beta}) \end{align} $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits // 2). Returns: operator (FermionOperator): corresponding to the sz operator over n_spatial_orbitals. Note: The indexing convention used is that even indices correspond to spin-up (alpha) modes and odd indices correspond to spin-down (beta) modes. """ if not isinstance(n_spatial_orbitals, int): raise TypeError("n_orbitals must be specified as an integer") operator = FermionOperator() n_spinless_orbitals = 2 * n_spatial_orbitals for ni in range(n_spatial_orbitals): operator += ( number_operator(n_spinless_orbitals, up_index(ni), 0.5) + number_operator(n_spinless_orbitals, down_index(ni), -0.5)) return operator
def s_minus_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the s+ operator. .. math:: \begin{align} S^{-} = \sum_{i=1}^{n} a_{i, \beta}^{\dagger}a_{i, \alpha} \end{align} Args: n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2). Returns: operator (FermionOperator): corresponding to the s- operator over n_spatial_orbitals. Note: The indexing convention used is that even indices correspond to spin-up (alpha) modes and odd indices correspond to spin-down (beta) modes. """ if not isinstance(n_spatial_orbitals, int): raise TypeError("n_orbitals must be specified as an integer") operator = FermionOperator() for ni in range(n_spatial_orbitals): operator += FermionOperator(((down_index(ni), 1), (up_index(ni), 0))) return operator
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 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 uccsd_singlet_get_packed_amplitudes(single_amplitudes, double_amplitudes, n_qubits, n_electrons): r"""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