def sx_operator(n_spatial_orbitals): r"""Return the sx operator. .. math:: \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): r"""Return the sz operator. .. math:: \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 _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 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
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 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