Beispiel #1
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 (a^\dagger_{i, \\uparrow} a_{i, \\uparrow} +
                         a^\dagger_{i, \downarrow} a_{i, \downarrow})
             - 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}
            + h \sum_{k=1}^N (-1)^k a_k^\dagger a_k
            - \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 (site + 1) % 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
Beispiel #2
0
 def test_trivially_commutes_both_single_number_operators(self):
     self.assertTrue(
         trivially_commutes_dual_basis(FermionOperator('3^ 3'),
                                       FermionOperator('3^ 3')))
Beispiel #3
0
 def test_trivially_commutes_one_double_number_operators(self):
     self.assertTrue(
         trivially_commutes_dual_basis(FermionOperator('3^ 2^ 3 2'),
                                       FermionOperator('3^ 3')))
Beispiel #4
0
 def test_double_commutator_no_intersection_with_union_of_second_two(self):
     com = double_commutator(FermionOperator('4^ 3^ 6 5'),
                             FermionOperator('2^ 1 0'),
                             FermionOperator('0^'))
     self.assertTrue(com.isclose(FermionOperator.zero()))
Beispiel #5
0
 def test_trivially_commutes_no_intersection(self):
     self.assertTrue(
         trivially_commutes_dual_basis(FermionOperator('3^ 2^ 3 2'),
                                       FermionOperator('4^ 1')))
 def test_none_term(self):
     majorana_operator()
     self.assertEqual(majorana_operator(), FermionOperator())
 def test_s_minus_operator(self):
     op = s_minus_operator(3)
     expected = (FermionOperator(((1, 1), (0, 0))) +
                 FermionOperator(((3, 1), (2, 0))) +
                 FermionOperator(((5, 1), (4, 0))))
     self.assertEqual(op, expected)
Beispiel #8
0
 def test_jw_sparse_0create_2annihilate(self):
     expected = csc_matrix(([-1j, 1j], ([4, 6], [1, 3])), shape=(8, 8))
     self.assertTrue(
         numpy.allclose(
             jordan_wigner_sparse(FermionOperator('0^ 2', -1j)).A,
             expected.A))
Beispiel #9
0
    def test_1d5_with_spin_7particles(self):
        dimension = 1
        grid_length = 5
        n_spatial_orbitals = grid_length**dimension
        wigner_seitz_radius = 9.3

        spinless = False
        n_qubits = n_spatial_orbitals
        if not spinless:
            n_qubits *= 2
        n_particles_big = 7

        length_scale = wigner_seitz_length_scale(wigner_seitz_radius,
                                                 n_particles_big, dimension)

        self.grid3 = Grid(dimension, grid_length, length_scale)
        # Get the occupied orbitals of the plane-wave basis Hartree-Fock state.
        hamiltonian = jellium_model(self.grid3, spinless, plane_wave=True)
        hamiltonian = normal_ordered(hamiltonian)
        hamiltonian.compress()

        occupied_states = numpy.array(
            lowest_single_particle_energy_states(hamiltonian, n_particles_big))
        self.hf_state_index3 = numpy.sum(2**occupied_states)

        self.hf_state3 = csc_matrix(([1.0], ([self.hf_state_index3], [0])),
                                    shape=(2**n_qubits, 1))

        self.orbital_occupations3 = [
            digit == '1' for digit in bin(self.hf_state_index3)[2:]
        ][::-1]
        self.occupied_orbitals3 = [
            index for index, occupied in enumerate(self.orbital_occupations3)
            if occupied
        ]

        self.reversed_occupied_orbitals3 = list(self.occupied_orbitals3)
        for i in range(len(self.reversed_occupied_orbitals3)):
            self.reversed_occupied_orbitals3[i] = -1 + int(
                numpy.log2(self.hf_state3.shape[0])
            ) - self.reversed_occupied_orbitals3[i]

        self.reversed_hf_state_index3 = sum(
            2**index for index in self.reversed_occupied_orbitals3)

        operator = (FermionOperator('6^ 0^ 1^ 3 5 4', 2) +
                    FermionOperator('7^ 2^ 4 1') +
                    FermionOperator('3^ 3', 2.1) +
                    FermionOperator('5^ 3^ 1 0', 7.3))
        operator = normal_ordered(operator)
        transformed_operator = normal_ordered(
            fourier_transform(operator, self.grid3, spinless))

        expected = 1.66 - 0.0615536707435j
        # Calculated with expected = expectation(get_sparse_operator(
        #    transformed_operator), self.hf_state3)
        actual = expectation_db_operator_with_pw_basis_state(
            operator, self.reversed_occupied_orbitals3, n_spatial_orbitals,
            self.grid3, spinless)

        self.assertAlmostEqual(expected, actual)
Beispiel #10
0
 def test_jw_sparse_1annihilate(self):
     expected = csc_matrix(([1, -1], ([0, 2], [1, 3])), shape=(4, 4))
     self.assertTrue(
         numpy.allclose(
             jordan_wigner_sparse(FermionOperator('1')).A, expected.A))
Beispiel #11
0
    def test_expectation_state_is_list_single_number_terms(self):
        operator = FermionOperator('3^ 3', 1.9) + FermionOperator('2^ 1')
        state = [1, 1, 1, 1]

        self.assertAlmostEqual(
            expectation_computational_basis_state(operator, state), 1.9)
Beispiel #12
0
    def test_expectation_fermion_operator_single_number_terms(self):
        operator = FermionOperator('3^ 3', 1.9) + FermionOperator('2^ 1')
        state = csc_matrix(([1], ([15], [0])), shape=(16, 1))

        self.assertAlmostEqual(
            expectation_computational_basis_state(operator, state), 1.9)
Beispiel #13
0
 def test_jw_sparse_0create(self):
     expected = csc_matrix(([1], ([1], [0])), shape=(2, 2))
     self.assertTrue(
         numpy.allclose(
             jordan_wigner_sparse(FermionOperator('0^')).A, expected.A))
Beispiel #14
0
def mean_field_dwave(x_dimension,
                     y_dimension,
                     tunneling,
                     sc_gap,
                     periodic=True):
    """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::

        H = - t \sum_{\langle i,j \\rangle} \sum_\sigma
                (a^\dagger_{i, \sigma} a_{j, \sigma} +
                 a^\dagger_{j, \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})

    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.

    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`
        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):
        # 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
Beispiel #15
0
 def test_bk_n_qubits_too_small(self):
     with self.assertRaises(ValueError):
         bravyi_kitaev_tree(FermionOperator('2^ 3^ 5 0'), n_qubits=4)
Beispiel #16
0
 def test_jw_sparse_twobody(self):
     expected = csc_matrix(([1, 1], ([6, 14], [5, 13])), shape=(16, 16))
     self.assertTrue(
         numpy.allclose(
             jordan_wigner_sparse(FermionOperator('2^ 1^ 1 3')).A,
             expected.A))
    def test_fermion_number_operator_site(self):
        op = number_operator(3, 2, 1j, -1)
        self.assertEqual(op, FermionOperator(((2, 1), (2, 0))) * 1j)

        op = number_operator(3, 2, 1j, 1)
        self.assertTrue(op == BosonOperator(((2, 1), (2, 0))) * 1j)
Beispiel #18
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_s_plus_operator(self):
     op = s_plus_operator(2)
     expected = (FermionOperator(((0, 1), (1, 0))) +
                 FermionOperator(((2, 1), (3, 0))))
     self.assertEqual(op, expected)
Beispiel #20
0
 def test_get_interaction_operator_too_few_qubits(self):
     with self.assertRaises(ValueError):
         get_interaction_operator(FermionOperator('3^ 2^ 1 0'), 3)
Beispiel #21
0
 def test_anticommutator_not_same_type(self):
     with self.assertRaises(TypeError):
         anticommutator(FermionOperator(), QubitOperator())
Beispiel #22
0
 def test_get_interaction_operator_bad_2body_term(self):
     with self.assertRaises(InteractionOperatorError):
         get_interaction_operator(FermionOperator('3^ 2 1 0'))
Beispiel #23
0
 def setUp(self):
     self.fermion_term = FermionOperator('1^ 2^ 3 4', -3.17)
     self.fermion_operator = self.fermion_term + hermitian_conjugated(
         self.fermion_term)
     self.qubit_operator = jordan_wigner(self.fermion_operator)
Beispiel #24
0
 def test_get_interaction_operator_nonmolecular_term(self):
     with self.assertRaises(InteractionOperatorError):
         get_interaction_operator(FermionOperator('3^ 2 1'))
Beispiel #25
0
 def test_no_trivial_commute_with_intersection(self):
     self.assertFalse(
         trivially_commutes_dual_basis(FermionOperator('2^ 1'),
                                       FermionOperator('5^ 2^ 5 2')))
Beispiel #26
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.)
Beispiel #27
0
 def test_trivially_commutes_nonintersecting_single_number_operators(self):
     self.assertTrue(
         trivially_commutes_dual_basis(FermionOperator('2^ 2'),
                                       FermionOperator('3^ 3')))
Beispiel #28
0
 def test_bk_identity(self):
     self.assertTrue(bravyi_kitaev_tree(FermionOperator(())) ==
                     QubitOperator(()))
Beispiel #29
0
 def test_commutes_identity(self):
     com = commutator(FermionOperator.identity(),
                      FermionOperator('2^ 3', 2.3))
     self.assertTrue(com.isclose(FermionOperator.zero()))
Beispiel #30
0
 def test_bad_input(self):
     with self.assertRaises(TypeError):
         _bksf.bravyi_kitaev_fast(FermionOperator())