Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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