Exemplo n.º 1
0
 def test_trivially_double_commutes_double_annihilate_in_a_and_c(self):
     self.assertTrue(
         trivially_double_commutes_dual_basis(FermionOperator('5^ 2'),
                                              FermionOperator('3^ 1'),
                                              FermionOperator('4^ 1^ 4 1')))
Exemplo n.º 2
0
def normal_ordered(operator, hbar=1.):
    r"""Compute and return the normal ordered form of a FermionOperator,
    BosonOperator, QuadOperator, or InteractionOperator.

    Due to the canonical commutation/anticommutation relations satisfied
    by these operators, there are multiple forms that the same operator
    can take. Here, we define the normal ordered form of each operator,
    providing a distinct representation for distinct operators.

    In our convention, normal ordering implies terms are ordered
    from highest tensor factor (on left) to lowest (on right). In
    addition:

    * FermionOperators: a^\dagger comes before a
    * BosonOperators: b^\dagger comes before b
    * QuadOperators: q operators come before p operators,

    Args:
        operator: an instance of the FermionOperator, BosonOperator,
            QuadOperator, or InteractionOperator classes.
        hbar (float): the value of hbar used in the definition of the
            commutator [q_i, p_j] = i hbar delta_ij. By default hbar=1.
            This argument only applies when normal ordering QuadOperators.
    """
    kwargs = {}

    if isinstance(operator, FermionOperator):
        ordered_operator = FermionOperator()
        order_fn = normal_ordered_ladder_term
        kwargs['parity'] = -1

    elif isinstance(operator, BosonOperator):
        ordered_operator = BosonOperator()
        order_fn = normal_ordered_ladder_term
        kwargs['parity'] = 1

    elif isinstance(operator, QuadOperator):
        ordered_operator = QuadOperator()
        order_fn = normal_ordered_quad_term
        kwargs['hbar'] = hbar

    elif isinstance(operator, InteractionOperator):
        constant = operator.constant
        n_modes = operator.n_qubits
        one_body_tensor = operator.one_body_tensor.copy()
        two_body_tensor = numpy.zeros_like(operator.two_body_tensor)
        quadratic_index_pairs = (
            (pq, pq) for pq in itertools.combinations(range(n_modes)[::-1], 2))
        cubic_index_pairs = (index_pair
            for p, q, r in itertools.combinations(range(n_modes)[::-1], 3)
            for index_pair in [
                ((p, q), (p, r)), ((p, r), (p, q)),
                ((p, q), (q, r)), ((q, r), (p, q)),
                ((p, r), (q, r)), ((q, r), (p, r))])
        quartic_index_pairs = (index_pair
            for p, q, r, s in itertools.combinations(range(n_modes)[::-1], 4)
            for index_pair in [
                ((p, q), (r, s)), ((r, s), (p, q)),
                ((p, r), (q, s)), ((q, s), (p, r)),
                ((p, s), (q, r)), ((q, r), (p, s))])
        index_pairs = itertools.chain(
            quadratic_index_pairs, cubic_index_pairs, quartic_index_pairs)
        for pq, rs in index_pairs:
            two_body_tensor[pq + rs] = sum(
                s * ss * operator.two_body_tensor[pq[::s] + rs[::ss]]
                for s, ss in itertools.product([-1, 1], repeat=2))
        return InteractionOperator(constant, one_body_tensor, two_body_tensor)

    else:
        raise TypeError('Can only normal order FermionOperator, '
                        'BosonOperator, QuadOperator, or InteractionOperator.')

    for term, coefficient in operator.terms.items():
        ordered_operator += order_fn(term, coefficient, **kwargs)

    return ordered_operator
Exemplo n.º 3
0
 def test_get_quadratic_hamiltonian_too_few_qubits(self):
     """Test asking for too few qubits."""
     with self.assertRaises(ValueError):
         get_quadratic_hamiltonian(FermionOperator('3^ 2^'), n_qubits=3)
 def test_jordan_wigner_twobody_interaction_op_reversal_symmetric(self):
     test_op = FermionOperator('1^ 2^ 2 1')
     test_op += hermitian_conjugated(test_op)
     self.assertTrue(
         jordan_wigner(test_op) == jordan_wigner(
             get_interaction_operator(test_op)))
Exemplo n.º 5
0
def freeze_orbitals(fermion_operator, occupied, unoccupied=None, prune=True):
    """Fix some orbitals to be occupied and others unoccupied.

    Removes all operators acting on the specified orbitals, and renumbers the
    remaining orbitals to eliminate unused indices. The sign of each term
    is modified according to the ladder uperator anti-commutation relations in
    order to preserve the expectation value of the operator.

    Args:
        occupied: A list containing the indices of the orbitals that are to be
            assumed to be occupied.
        unoccupied: A list containing the indices of the orbitals that are to
            be assumed to be unoccupied.
    """
    new_operator = fermion_operator
    frozen = [(index, 1) for index in occupied]
    if unoccupied is not None:
        frozen += [(index, 0) for index in unoccupied]

    # Loop over each orbital to be frozen. Within each term, move all
    # ops acting on that orbital to the right side of the term, keeping
    # track of sign flips that come from swapping operators.
    for item in frozen:
        tmp_operator = FermionOperator()
        for term in new_operator.terms:
            new_term = []
            new_coef = new_operator.terms[term]
            current_occupancy = item[1]
            n_ops = 0  # Number of operations on index that have been moved
            n_swaps = 0  # Number of swaps that have been done

            for op in enumerate(reversed(term)):
                if op[1][0] is item[0]:
                    n_ops += 1

                    # Determine number of swaps needed to bring the op in
                    # front of all ops acting on other indices
                    n_swaps += op[0] - n_ops

                    # Check if the op annihilates the current state
                    if current_occupancy == op[1][1]:
                        new_coef = 0

                    # Update current state
                    current_occupancy = (current_occupancy + 1) % 2
                else:
                    new_term.insert(0, op[1])
            if n_swaps % 2:
                new_coef *= -1
            if new_coef and current_occupancy == item[1]:
                tmp_operator += FermionOperator(tuple(new_term), new_coef)
        new_operator = tmp_operator

    # For occupied frozen orbitals, we must also bring together the creation
    # operator from the ket and the annihilation operator from the bra when
    # evaluating expectation values. This can result in an additional minus
    # sign.
    for term in new_operator.terms:
        for index in occupied:
            for op in term:
                if op[0] > index:
                    new_operator.terms[term] *= -1

    # Renumber indices to remove frozen orbitals
    new_operator = prune_unused_indices(new_operator)

    return new_operator
    def test_ccr_offsite_even_cc(self):
        c2 = FermionOperator(((2, 1), ))
        c4 = FermionOperator(((4, 1), ))
        self.assertTrue(normal_ordered(c2 * c4) == normal_ordered(-c4 * c2))

        self.assertTrue(jordan_wigner(c2 * c4) == jordan_wigner(-c4 * c2))
    def test_ccr_offsite_even_aa(self):
        a2 = FermionOperator(((2, 0), ))
        a4 = FermionOperator(((4, 0), ))
        self.assertTrue(normal_ordered(a2 * a4) == normal_ordered(-a4 * a2))

        self.assertTrue(jordan_wigner(a2 * a4) == jordan_wigner(-a4 * a2))
Exemplo n.º 8
0
 def test_commutator_hopping_with_double_number_one_intersection(self):
     com = commutator(FermionOperator('1^ 3'), FermionOperator('3^ 2^ 3 2'))
     com = normal_ordered(com)
     self.assertTrue(com == -FermionOperator('2^ 1^ 3 2'))
Exemplo n.º 9
0
 def test_commutator_hopping_with_double_number_two_intersections(self):
     com = commutator(FermionOperator('2^ 3'), FermionOperator('3^ 2^ 3 2'))
     com = normal_ordered(com)
     self.assertTrue(com == FermionOperator.zero())
Exemplo n.º 10
0
 def test_commutator_hopping_operators(self):
     com = commutator(3 * FermionOperator('1^ 2'), FermionOperator('2^ 3'))
     com = normal_ordered(com)
     self.assertTrue(com == FermionOperator('1^ 3', 3))
Exemplo n.º 11
0
 def test_commutator_hopping_with_single_number(self):
     com = commutator(FermionOperator('1^ 2', 1j), FermionOperator('1^ 1'))
     com = normal_ordered(com)
     self.assertTrue(com == -FermionOperator('1^ 2') * 1j)
Exemplo n.º 12
0
 def test_trivially_double_commutes_excess_annihilate(self):
     self.assertTrue(
         trivially_double_commutes_dual_basis(FermionOperator('5^ 2'),
                                              FermionOperator('3^ 2'),
                                              FermionOperator('2^ 2')))
Exemplo n.º 13
0
 def test_trivially_double_commutes_excess_create(self):
     self.assertTrue(
         trivially_double_commutes_dual_basis(FermionOperator('5^ 2'),
                                              FermionOperator('5^ 5'),
                                              FermionOperator('5^ 1')))
Exemplo n.º 14
0
 def test_no_trivial_double_commute_double_annihilate_with_create(self):
     self.assertFalse(
         trivially_double_commutes_dual_basis(FermionOperator('5^ 2'),
                                              FermionOperator('2^ 1'),
                                              FermionOperator('4^ 2')))
Exemplo n.º 15
0
 def test_get_interaction_operator_nonmolecular_term(self):
     with self.assertRaises(InteractionOperatorError):
         get_interaction_operator(FermionOperator('3^ 2 1'))
Exemplo n.º 16
0
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
Exemplo n.º 17
0
    def setUp(self):
        self.hermitian_op = FermionOperator((), 1.)
        self.hermitian_op += FermionOperator('1^ 1', 3.)
        self.hermitian_op += FermionOperator('1^ 2', 3. + 4.j)
        self.hermitian_op += FermionOperator('2^ 1', 3. - 4.j)
        self.hermitian_op += FermionOperator('3^ 4^', 2. + 5.j)
        self.hermitian_op += FermionOperator('4 3', 2. - 5.j)

        self.hermitian_op_pc = FermionOperator((), 1.)
        self.hermitian_op_pc += FermionOperator('1^ 1', 3.)
        self.hermitian_op_pc += FermionOperator('1^ 2', 3. + 4.j)
        self.hermitian_op_pc += FermionOperator('2^ 1', 3. - 4.j)
        self.hermitian_op_pc += FermionOperator('3^ 4', 2. + 5.j)
        self.hermitian_op_pc += FermionOperator('4^ 3', 2. - 5.j)

        self.hermitian_op_bad_term = FermionOperator('1^ 1 2', 3.)
        self.hermitian_op_bad_term += FermionOperator('2^ 1^ 1', 3.)

        self.not_hermitian_1 = FermionOperator('2^ 0^')
        self.not_hermitian_2 = FermionOperator('3^ 0^')
        self.not_hermitian_2 += FermionOperator('3 0', 3.)
        self.not_hermitian_3 = FermionOperator('2 0')
        self.not_hermitian_4 = FermionOperator('4 0')
        self.not_hermitian_4 += FermionOperator('4^ 0^', 3.)
        self.not_hermitian_5 = FermionOperator('2^ 3', 3.)
        self.not_hermitian_5 += FermionOperator('3^ 2', 2.)
Exemplo n.º 18
0
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
Exemplo n.º 19
0
    def test_ccr_offsite_odd_cc(self):
        c1 = FermionOperator(((1, 1), ))
        c4 = FermionOperator(((4, 1), ))
        self.assertTrue(normal_ordered(c1 * c4) == normal_ordered(-c4 * c1))

        self.assertTrue(jordan_wigner(c1 * c4) == jordan_wigner(-c4 * c1))
Exemplo n.º 20
0
def plane_wave_potential(grid, spinless=False):
    """Return the potential operator in the plane wave basis.

    Args:
        grid (Grid): The discretization to use.
        spinless (bool): Whether to use the spinless model or not.

    Returns:
        operator (FermionOperator)
    """
    # Initialize.
    prefactor = 2. * numpy.pi / grid.volume_scale()
    operator = FermionOperator((), 0.0)
    spins = [None] if spinless else [0, 1]

    # Pre-Computations.
    shifted_omega_indices_dict = {}
    shifted_indices_minus_dict = {}
    shifted_indices_plus_dict = {}
    orbital_ids = {}
    for indices_a in grid.all_points_indices():
        shifted_omega_indices = [j - grid.length // 2 for j in indices_a]
        shifted_omega_indices_dict[indices_a] = shifted_omega_indices
        shifted_indices_minus_dict[indices_a] = {}
        shifted_indices_plus_dict[indices_a] = {}
        for indices_b in grid.all_points_indices():
            shifted_indices_minus_dict[indices_a][indices_b] = tuple([
                (indices_b[i] - shifted_omega_indices[i]) % grid.length
                for i in range(grid.dimensions)])
            shifted_indices_plus_dict[indices_a][indices_b] = tuple([
                (indices_b[i] + shifted_omega_indices[i]) % grid.length
                for i in range(grid.dimensions)])
        orbital_ids[indices_a] = {}
        for spin in spins:
            orbital_ids[indices_a][spin] = orbital_id(grid, indices_a, spin)

    # Loop once through all plane waves.
    for omega_indices in grid.all_points_indices():
        shifted_omega_indices = shifted_omega_indices_dict[omega_indices]

        # Get the momenta vectors.
        omega_momenta = momentum_vector(omega_indices, grid)

        # Skip if omega momentum is zero.
        if not omega_momenta.any():
            continue

        # Compute coefficient.
        coefficient = prefactor / omega_momenta.dot(omega_momenta)

        for grid_indices_a in grid.all_points_indices():
            shifted_indices_d = (
                shifted_indices_minus_dict[omega_indices][grid_indices_a])
            for grid_indices_b in grid.all_points_indices():
                shifted_indices_c = (
                    shifted_indices_plus_dict[omega_indices][grid_indices_b])

                # Loop over spins.
                for spin_a in spins:
                    orbital_a = orbital_ids[grid_indices_a][spin_a]
                    orbital_d = orbital_ids[shifted_indices_d][spin_a]
                    for spin_b in spins:
                        orbital_b = orbital_ids[grid_indices_b][spin_b]
                        orbital_c = orbital_ids[shifted_indices_c][spin_b]

                        # Add interaction term.
                        if ((orbital_a != orbital_b) and
                                (orbital_c != orbital_d)):
                            operators = ((orbital_a, 1), (orbital_b, 1),
                                         (orbital_c, 0), (orbital_d, 0))
                            operator += FermionOperator(operators, coefficient)

    # Return.
    return operator
Exemplo n.º 21
0
    def test_ccr_offsite_odd_aa(self):
        a1 = FermionOperator(((1, 0), ))
        a4 = FermionOperator(((4, 0), ))
        self.assertTrue(normal_ordered(a1 * a4) == normal_ordered(-a4 * a1))

        self.assertTrue(jordan_wigner(a1 * a4) == jordan_wigner(-a4 * a1))
Exemplo n.º 22
0
def dual_basis_jellium_model(grid, spinless=False,
                             kinetic=True, potential=True,
                             include_constant=False):
    """Return jellium Hamiltonian in the dual basis of arXiv:1706.00023

    Args:
        grid (Grid): The discretization to use.
        spinless (bool): Whether to use the spinless model or not.
        kinetic (bool): Whether to include kinetic terms.
        potential (bool): Whether to include potential terms.
        include_constant (bool): Whether to include the Madelung constant.

    Returns:
        operator (FermionOperator)
    """
    # Initialize.
    n_points = grid.num_points()
    position_prefactor = 2. * numpy.pi / grid.volume_scale()
    operator = FermionOperator()
    spins = [None] if spinless else [0, 1]

    # Pre-Computations.
    position_vectors = {}
    momentum_vectors = {}
    momenta_squared_dict = {}
    orbital_ids = {}
    for indices in grid.all_points_indices():
        position_vectors[indices] = position_vector(indices, grid)
        momenta = momentum_vector(indices, grid)
        momentum_vectors[indices] = momenta
        momenta_squared_dict[indices] = momenta.dot(momenta)
        orbital_ids[indices] = {}
        for spin in spins:
            orbital_ids[indices][spin] = orbital_id(grid, indices, spin)

    # Loop once through all lattice sites.
    for grid_indices_a in grid.all_points_indices():
        coordinates_a = position_vectors[grid_indices_a]
        for grid_indices_b in grid.all_points_indices():
            coordinates_b = position_vectors[grid_indices_b]
            differences = coordinates_b - coordinates_a

            # Compute coefficients.
            kinetic_coefficient = 0.
            potential_coefficient = 0.
            for momenta_indices in grid.all_points_indices():
                momenta = momentum_vectors[momenta_indices]
                momenta_squared = momenta_squared_dict[momenta_indices]
                if momenta_squared == 0:
                    continue
                cos_difference = numpy.cos(momenta.dot(differences))
                if kinetic:
                    kinetic_coefficient += (
                        cos_difference * momenta_squared /
                        (2. * float(n_points)))
                if potential:
                    potential_coefficient += (
                        position_prefactor * cos_difference / momenta_squared)

            # Loop over spins and identify interacting orbitals.
            orbital_a = {}
            orbital_b = {}
            for spin in spins:
                orbital_a[spin] = orbital_ids[grid_indices_a][spin]
                orbital_b[spin] = orbital_ids[grid_indices_b][spin]
            if kinetic:
                for spin in spins:
                    operators = ((orbital_a[spin], 1), (orbital_b[spin], 0))
                    operator += FermionOperator(operators, kinetic_coefficient)
            if potential:
                for sa in spins:
                    for sb in spins:
                        if orbital_a[sa] == orbital_b[sb]:
                            continue
                        operators = ((orbital_a[sa], 1), (orbital_a[sa], 0),
                                     (orbital_b[sb], 1), (orbital_b[sb], 0))
                        operator += FermionOperator(operators,
                                                    potential_coefficient)

    # Include the Madelung constant if requested.
    if include_constant:
        operator += FermionOperator.identity() * (2.8372 / grid.scale)

    # Return.
    return operator
Exemplo n.º 23
0
def majorana_operator(term=None, coefficient=1.):
    """Initialize a Majorana operator.

    Args:
        term(tuple or string): The first element of the tuple indicates the
            mode on which the Majorana operator acts, starting from zero.
            The second element of the tuple is an integer, either 0 or 1,
            indicating which type of Majorana operator it is:

                Type 0: :math:`a^\dagger_p + a_p`

                Type 1: :math:`i (a^\dagger_p - a_p)`

            where the :math:`a^\dagger_p` and :math:`a_p` are the usual
            fermionic ladder operators.
            Alternatively, one can provide a string such as 'c2', which
            is a Type 0 operator on mode 2, or 'd3', which is a Type 1
            operator on mode 3.
            Default will result in the zero operator.
        coefficient(complex or float, optional): The coefficient of the term.
            Default value is 1.0.

    Returns:
        FermionOperator
    """
    if not isinstance(coefficient, (int, float, complex)):
        raise ValueError('Coefficient must be scalar.')

    # If term is a string, convert it to a tuple
    if isinstance(term, string_types):
        operator_type = term[0]
        mode = int(term[1:])
        if operator_type == 'c':
            operator_type = 0
        elif operator_type == 'd':
            operator_type = 1
        else:
            raise ValueError('Invalid operator type: {}'.format(operator_type))
        term = (mode, operator_type)

    # Process term

    # Zero operator
    if term is None:
        return FermionOperator()

    # Tuple
    if isinstance(term, tuple):
        mode, operator_type = term

        if operator_type == 0:
            majorana_op = FermionOperator(((mode, 1), ), coefficient)
            majorana_op += FermionOperator(((mode, 0), ), coefficient)
        elif operator_type == 1:
            majorana_op = FermionOperator(((mode, 1), ), 1.j * coefficient)
            majorana_op -= FermionOperator(((mode, 0), ), 1.j * coefficient)
        else:
            raise ValueError('Invalid operator type: {}'.format(
                str(operator_type)))

        return majorana_op

    # Invalid input.
    else:
        raise ValueError('Operator specified incorrectly.')
Exemplo n.º 24
0
 def test_none_term(self):
     majorana_op = majorana_operator()
     self.assertTrue(majorana_operator().isclose(FermionOperator()))
Exemplo n.º 25
0
def hermitian_conjugated(operator):
    """Return Hermitian conjugate of operator."""
    # Handle FermionOperator
    if isinstance(operator, FermionOperator):
        conjugate_operator = FermionOperator()
        for term, coefficient in operator.terms.items():
            conjugate_term = tuple([(tensor_factor, 1 - action) for
                                    (tensor_factor, action) in reversed(term)])
            conjugate_operator.terms[conjugate_term] = coefficient.conjugate()

    # Handle BosonOperator
    elif isinstance(operator, BosonOperator):
        conjugate_operator = BosonOperator()
        for term, coefficient in operator.terms.items():
            conjugate_term = tuple([(tensor_factor, 1 - action) for
                                    (tensor_factor, action) in reversed(term)])
            # take into account that different indices commute
            conjugate_term = tuple(
                sorted(conjugate_term, key=lambda factor: factor[0]))
            conjugate_operator.terms[conjugate_term] = coefficient.conjugate()

    # Handle QubitOperator
    elif isinstance(operator, QubitOperator):
        conjugate_operator = QubitOperator()
        for term, coefficient in operator.terms.items():
            conjugate_operator.terms[term] = coefficient.conjugate()

    # Handle QuadOperator
    elif isinstance(operator, QuadOperator):
        conjugate_operator = QuadOperator()
        for term, coefficient in operator.terms.items():
            conjugate_term = reversed(term)
            # take into account that different indices commute
            conjugate_term = tuple(
                sorted(conjugate_term, key=lambda factor: factor[0]))
            conjugate_operator.terms[conjugate_term] = coefficient.conjugate()

    # Handle InteractionOperator
    elif isinstance(operator, InteractionOperator):
        conjugate_constant = operator.constant.conjugate()
        conjugate_one_body_tensor = hermitian_conjugated(
            operator.one_body_tensor)
        conjugate_two_body_tensor = hermitian_conjugated(
            operator.two_body_tensor)
        conjugate_operator = type(operator)(conjugate_constant,
            conjugate_one_body_tensor, conjugate_two_body_tensor)

    # Handle sparse matrix
    elif isinstance(operator, spmatrix):
        conjugate_operator = operator.getH()

    # Handle numpy array
    elif isinstance(operator, numpy.ndarray):
        conjugate_operator = operator.T.conj()

    # Unsupported type
    else:
        raise TypeError('Taking the hermitian conjugate of a {} is not '
                        'supported.'.format(type(operator).__name__))

    return conjugate_operator
Exemplo n.º 26
0
 def test_get_interaction_operator_too_few_qubits(self):
     with self.assertRaises(ValueError):
         get_interaction_operator(FermionOperator('3^ 2^ 1 0'), 3)
Exemplo n.º 27
0
def meanfield_dwave(x_dimension,
                    y_dimension,
                    tunneling,
                    sc_gap,
                    periodic=True,
                    verbose=False):
    """Return symbolic representation of a BCS mean-field d-wave Hamiltonian.

    Args:
        x_dimension: An integer giving the number of sites in width.
        y_dimension: An integer giving the number of sites in height.
        tunneling: A float giving the tunneling amplitude.
        sc_gap: A float giving the magnitude of the superconducting gap.
        periodic: If True, add periodic boundary conditions.
        verbose: An optional Boolean. If True, print all second quantized
            terms.

    Returns:
        meanfield_dwave_model: An instance of the FermionOperator class.
    """
    # Initialize fermion operator class.
    n_sites = x_dimension * y_dimension
    n_spin_orbitals = 2 * n_sites
    meanfield_dwave_model = FermionOperator()

    # Loop through sites and add terms.
    for site in range(n_sites):
        # 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(site), 1.), (up(right_neighbor), 0.))
            hopping_term = FermionOperator(operators, -tunneling)
            meanfield_dwave_model += hopping_term
            meanfield_dwave_model += hermitian_conjugated(hopping_term)
            # Add spin-down hopping term
            operators = ((down(site), 1.), (down(right_neighbor), 0.))
            hopping_term = FermionOperator(operators, -tunneling)
            meanfield_dwave_model += hopping_term
            meanfield_dwave_model += hermitian_conjugated(hopping_term)

            # Add pairing term
            operators = ((up(site), 1.), (down(right_neighbor), 1.))
            pairing_term = FermionOperator(operators, sc_gap / 2.)
            operators = ((down(site), 1.), (up(right_neighbor), 1.))
            pairing_term += FermionOperator(operators, -sc_gap / 2.)
            meanfield_dwave_model -= pairing_term
            meanfield_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(site), 1.), (up(bottom_neighbor), 0.))
            hopping_term = FermionOperator(operators, -tunneling)
            meanfield_dwave_model += hopping_term
            meanfield_dwave_model += hermitian_conjugated(hopping_term)
            # Add spin-down hopping term
            operators = ((down(site), 1.), (down(bottom_neighbor), 0.))
            hopping_term = FermionOperator(operators, -tunneling)
            meanfield_dwave_model += hopping_term
            meanfield_dwave_model += hermitian_conjugated(hopping_term)

            # Add pairing term
            operators = ((up(site), 1.), (down(bottom_neighbor), 1.))
            pairing_term = FermionOperator(operators, -sc_gap / 2.)
            operators = ((down(site), 1.), (up(bottom_neighbor), 1.))
            pairing_term += FermionOperator(operators, sc_gap / 2.)
            meanfield_dwave_model -= pairing_term
            meanfield_dwave_model -= hermitian_conjugated(pairing_term)
    # Return.
    return meanfield_dwave_model
Exemplo n.º 28
0
 def test_get_interaction_operator_bad_2body_term(self):
     with self.assertRaises(InteractionOperatorError):
         get_interaction_operator(FermionOperator('3^ 2 1 0'))
Exemplo n.º 29
0
 def test_sparse_matrix_zero_n_qubit(self):
     sparse_operator = get_sparse_operator(FermionOperator.zero(), 4)
     sparse_operator.eliminate_zeros()
     self.assertEqual(len(list(sparse_operator.data)), 0)
     self.assertEqual(sparse_operator.shape, (16, 16))
Exemplo n.º 30
0
 def test_commutes_number_operators(self):
     com = commutator(FermionOperator('4^ 3^ 4 3'), FermionOperator('2^ 2'))
     com = normal_ordered(com)
     self.assertTrue(com == FermionOperator.zero())