Exemplo n.º 1
0
    def test_ccr_offsite_even_aa(self):
        a2 = FermionOperator(((2, 0), ))
        a4 = FermionOperator(((4, 0), ))
        self.assertTrue(
            normal_ordered(a2 * a4).isclose(normal_ordered(-a4 * a2)))

        self.assertTrue(
            jordan_wigner(a2 * a4).isclose(jordan_wigner(-a4 * a2)))
Exemplo n.º 2
0
    def test_ccr_offsite_even_cc(self):
        c2 = FermionOperator(((2, 1), ))
        c4 = FermionOperator(((4, 1), ))
        self.assertTrue(
            normal_ordered(c2 * c4).isclose(normal_ordered(-c4 * c2)))

        self.assertTrue(
            jordan_wigner(c2 * c4).isclose(jordan_wigner(-c4 * c2)))
Exemplo n.º 3
0
    def test_ccr_offsite_odd_cc(self):
        c1 = FermionOperator(((1, 1), ))
        c4 = FermionOperator(((4, 1), ))
        self.assertTrue(
            normal_ordered(c1 * c4).isclose(normal_ordered(-c4 * c1)))

        self.assertTrue(
            jordan_wigner(c1 * c4).isclose(jordan_wigner(-c4 * c1)))
Exemplo n.º 4
0
    def test_ccr_offsite_odd_aa(self):
        a1 = FermionOperator(((1, 0), ))
        a4 = FermionOperator(((4, 0), ))
        self.assertTrue(
            normal_ordered(a1 * a4).isclose(normal_ordered(-a4 * a1)))

        self.assertTrue(
            jordan_wigner(a1 * a4).isclose(jordan_wigner(-a4 * a1)))
Exemplo n.º 5
0
    def test_get_molecular_operator(self):
        coefficient = 3.
        operators = ((2, 1), (3, 0), (0, 0), (3, 1))
        op = FermionOperator(operators, coefficient)

        molecular_operator = get_interaction_operator(op)
        fermion_operator = get_fermion_operator(molecular_operator)
        fermion_operator = normal_ordered(fermion_operator)
        self.assertTrue(normal_ordered(op).isclose(fermion_operator))
Exemplo n.º 6
0
def benchmark_fermion_math_and_normal_order(n_qubits, term_length, power):
    """Benchmark both arithmetic operators and normal ordering on fermions.

    The idea is we generate two random FermionTerms, A and B, each acting
    on n_qubits with term_length operators. We then compute
    (A + B) ** power. This is costly that is the first benchmark. The second
    benchmark is in normal ordering whatever comes out.

    Args:
        n_qubits: The number of qubits on which these terms act.
        term_length: The number of operators in each term.
        power: Int, the exponent to which to raise sum of the two terms.

    Returns:
        runtime_math: The time it takes to perform (A + B) ** power
        runtime_normal_order: The time it takes to perform
            FermionOperator.normal_order()
    """
    # Generate random operator strings.
    operators_a = [(numpy.random.randint(n_qubits), numpy.random.randint(2))]
    operators_b = [(numpy.random.randint(n_qubits), numpy.random.randint(2))]
    for operator_number in range(term_length):

        # Make sure the operator is not trivially zero.
        operator_a = (numpy.random.randint(n_qubits), numpy.random.randint(2))
        while operator_a == operators_a[-1]:
            operator_a = (numpy.random.randint(n_qubits),
                          numpy.random.randint(2))
        operators_a += [operator_a]

        # Do the same for the other operator.
        operator_b = (numpy.random.randint(n_qubits), numpy.random.randint(2))
        while operator_b == operators_b[-1]:
            operator_b = (numpy.random.randint(n_qubits),
                          numpy.random.randint(2))
        operators_b += [operator_b]

    # Initialize FermionTerms and then sum them together.
    fermion_term_a = FermionOperator(tuple(operators_a),
                                     float(numpy.random.randn()))
    fermion_term_b = FermionOperator(tuple(operators_b),
                                     float(numpy.random.randn()))
    fermion_operator = fermion_term_a + fermion_term_b

    # Exponentiate.
    start_time = time.time()
    fermion_operator **= power
    runtime_math = time.time() - start_time

    # Normal order.
    start_time = time.time()
    normal_ordered(fermion_operator)
    runtime_normal_order = time.time() - start_time

    # Return.
    return runtime_math, runtime_normal_order
Exemplo n.º 7
0
 def test_ccr_onsite(self):
     c1 = FermionOperator(((1, 1), ))
     a1 = hermitian_conjugated(c1)
     self.assertTrue(
         normal_ordered(
             c1 *
             a1).isclose(FermionOperator(()) - normal_ordered(a1 * c1)))
     self.assertTrue(
         jordan_wigner(
             c1 * a1).isclose(QubitOperator(()) - jordan_wigner(a1 * c1)))
Exemplo n.º 8
0
    def test_jordan_wigner_twobody_interaction_op_allunique(self):
        test_op = FermionOperator('1^ 2^ 3 4')
        test_op += hermitian_conjugated(test_op)

        retransformed_test_op = reverse_jordan_wigner(
            jordan_wigner(get_interaction_operator(test_op)))

        self.assertTrue(
            normal_ordered(retransformed_test_op).isclose(
                normal_ordered(test_op)))
Exemplo n.º 9
0
 def test_inverse_fourier_transform_2d(self):
     grid = Grid(dimensions=2, scale=1.5, length=3)
     spinless = True
     geometry = [('H', (0, 0)), ('H', (0.5, 0.8))]
     h_plane_wave = plane_wave_hamiltonian(grid, geometry, spinless, True)
     h_dual_basis = plane_wave_hamiltonian(grid, geometry, spinless, False)
     h_dual_basis_t = inverse_fourier_transform(h_dual_basis, grid,
                                                spinless)
     self.assertTrue(
         normal_ordered(h_dual_basis_t).isclose(
             normal_ordered(h_plane_wave)))
Exemplo n.º 10
0
    def test_xy(self):
        xy = QubitOperator(((4, 'X'), (5, 'Y')), -2.j)
        transmed_xy = reverse_jordan_wigner(xy)
        retransmed_xy = jordan_wigner(transmed_xy)

        expected1 = -2j * (FermionOperator(((4, 1), ), 1j) - FermionOperator(
            ((4, 0), ), 1j))
        expected2 = (FermionOperator(((5, 1), )) - FermionOperator(((5, 0), )))
        expected = expected1 * expected2

        self.assertTrue(xy.isclose(retransmed_xy))
        self.assertTrue(
            normal_ordered(transmed_xy).isclose(normal_ordered(expected)))
Exemplo n.º 11
0
    def test_yy(self):
        yy = QubitOperator(((2, 'Y'), (3, 'Y')), 2.)
        transmed_yy = reverse_jordan_wigner(yy)
        retransmed_yy = jordan_wigner(transmed_yy)

        expected1 = -(FermionOperator(((2, 1), ), 2.) + FermionOperator(
            ((2, 0), ), 2.))
        expected2 = (FermionOperator(((3, 1), )) - FermionOperator(((3, 0), )))
        expected = expected1 * expected2

        self.assertTrue(yy.isclose(retransmed_yy))
        self.assertTrue(
            normal_ordered(transmed_yy).isclose(normal_ordered(expected)))
Exemplo n.º 12
0
 def test_fourier_transform(self):
     grid = Grid(dimensions=1, scale=1.5, length=3)
     spinless_set = [True, False]
     geometry = [('H', (0, )), ('H', (0.5, ))]
     for spinless in spinless_set:
         h_plane_wave = plane_wave_hamiltonian(grid, geometry, spinless,
                                               True)
         h_dual_basis = plane_wave_hamiltonian(grid, geometry, spinless,
                                               False)
         h_plane_wave_t = fourier_transform(h_plane_wave, grid, spinless)
         self.assertTrue(
             normal_ordered(h_plane_wave_t).isclose(
                 normal_ordered(h_dual_basis)))
Exemplo n.º 13
0
    def test_xx(self):
        xx = QubitOperator(((3, 'X'), (4, 'X')), 2.)
        transmed_xx = reverse_jordan_wigner(xx)
        retransmed_xx = jordan_wigner(transmed_xx)

        expected1 = (FermionOperator(((3, 1), ), 2.) - FermionOperator(
            ((3, 0), ), 2.))
        expected2 = (FermionOperator(((4, 1), ), 1.) + FermionOperator(
            ((4, 0), ), 1.))
        expected = expected1 * expected2

        self.assertTrue(xx.isclose(retransmed_xx))
        self.assertTrue(
            normal_ordered(transmed_xx).isclose(normal_ordered(expected)))
Exemplo n.º 14
0
    def test_yx(self):
        yx = QubitOperator(((0, 'Y'), (1, 'X')), -0.5)
        transmed_yx = reverse_jordan_wigner(yx)
        retransmed_yx = jordan_wigner(transmed_yx)

        expected1 = 1j * (FermionOperator(((0, 1), )) + FermionOperator(
            ((0, 0), )))
        expected2 = -0.5 * (FermionOperator(((1, 1), )) + FermionOperator(
            ((1, 0), )))
        expected = expected1 * expected2

        self.assertTrue(yx.isclose(retransmed_yx))
        self.assertTrue(
            normal_ordered(transmed_yx).isclose(normal_ordered(expected)))
Exemplo n.º 15
0
def dual_basis_jellium_hamiltonian(grid_length,
                                   dimension=3,
                                   wigner_seitz_radius=10.,
                                   n_particles=None,
                                   spinless=True):
    """Return the jellium Hamiltonian with the given parameters.

    Args:
        grid_length (int): The number of spatial orbitals per dimension.
        dimension (int): The dimension of the system.
        wigner_seitz_radius (float): The radius per particle in Bohr.
        n_particles (int): The number of particles in the system.
                           Defaults to half filling if not specified.
    """
    n_qubits = grid_length**dimension
    if not spinless:
        n_qubits *= 2

    if n_particles is None:
        # Default to half filling fraction.
        n_particles = n_qubits // 2

    if not (0 <= n_particles <= n_qubits):
        raise ValueError('n_particles must be between 0 and the number of'
                         ' spin-orbitals.')

    # Compute appropriate length scale.
    length_scale = wigner_seitz_length_scale(wigner_seitz_radius, n_particles,
                                             dimension)

    grid = Grid(dimension, grid_length, length_scale)
    hamiltonian = jellium_model(grid, spinless=spinless, plane_wave=False)
    hamiltonian = normal_ordered(hamiltonian)
    hamiltonian.compress()
    return hamiltonian
Exemplo n.º 16
0
def double_commutator(op1,
                      op2,
                      op3,
                      indices2=None,
                      indices3=None,
                      is_hopping_operator2=None,
                      is_hopping_operator3=None):
    """Return the double commutator [op1, [op2, op3]].

    Assumes the operators are from the dual basis Hamiltonian.

    Args:
        op1, op2, op3 (FermionOperators): operators for the commutator.
        indices2, indices3 (set): The indices op2 and op3 act on.
        is_hopping_operator2 (bool): Whether op2 is a hopping operator.
        is_hopping_operator3 (bool): Whether op3 is a hopping operator.

    Returns:
        The double commutator of the given operators.
    """
    if is_hopping_operator2 and is_hopping_operator3:
        indices2 = set(indices2)
        indices3 = set(indices3)
        # Determine which indices both op2 and op3 act on.
        try:
            intersection, = indices2.intersection(indices3)
        except ValueError:
            return FermionOperator.zero()

        # Remove the intersection from the set of indices, since it will get
        # cancelled out in the final result.
        indices2.remove(intersection)
        indices3.remove(intersection)

        # Find the indices of the final output hopping operator.
        index2, = indices2
        index3, = indices3
        coeff2 = op2.terms[list(op2.terms)[0]]
        coeff3 = op3.terms[list(op3.terms)[0]]
        commutator23 = (FermionOperator(
            ((index2, 1), (index3, 0)), coeff2 * coeff3) + FermionOperator(
                ((index3, 1), (index2, 0)), -coeff2 * coeff3))
    else:
        commutator23 = normal_ordered(commutator(op2, op3))

    return normal_ordered(commutator(op1, commutator23))
Exemplo n.º 17
0
    def get_qubit_expectations(self, qubit_operator):
        """Return expectations of QubitOperator in new QubitOperator.

        Args:
            qubit_operator: QubitOperator instance to be evaluated on
                this InteractionRDM.

        Returns:
            QubitOperator: QubitOperator with coefficients
            corresponding to expectation values of those operators.

        Raises:
            InteractionRDMError: Observable not contained in 1-RDM or 2-RDM.
        """
        from fermilib.transforms import reverse_jordan_wigner
        qubit_operator_expectations = copy.deepcopy(qubit_operator)
        del qubit_operator_expectations.terms[()]
        for qubit_term in qubit_operator_expectations.terms:
            expectation = 0.

            # Map qubits back to fermions.
            reversed_fermion_operators = reverse_jordan_wigner(
                QubitOperator(qubit_term), self.n_qubits)
            reversed_fermion_operators = normal_ordered(
                reversed_fermion_operators)

            # Loop through fermion terms.
            for fermion_term in reversed_fermion_operators.terms:
                coefficient = reversed_fermion_operators.terms[fermion_term]

                # Handle molecular term.
                if FermionOperator(fermion_term).is_molecular_term():
                    if not fermion_term:
                        expectation += coefficient
                    else:
                        indices = [operator[0] for operator in fermion_term]
                        rdm_element = self[indices]
                        expectation += rdm_element * coefficient

                # Handle non-molecular terms.
                elif len(fermion_term) > 4:
                    raise InteractionRDMError('Observable not contained '
                                              'in 1-RDM or 2-RDM.')
            qubit_operator_expectations.terms[qubit_term] = expectation
        return qubit_operator_expectations
Exemplo n.º 18
0
def get_interaction_operator(fermion_operator, n_qubits=None):
    """Convert a 2-body fermionic operator to InteractionOperator.

    This function should only be called on fermionic operators which
    consist of only a_p^\dagger a_q and a_p^\dagger a_q^\dagger a_r a_s
    terms. The one-body terms are stored in a matrix, one_body[p, q], and
    the two-body terms are stored in a tensor, two_body[p, q, r, s].

    Returns:
       interaction_operator: An instance of the InteractionOperator class.

    Raises:
        TypeError: Input must be a FermionOperator.
        TypeError: FermionOperator does not map to InteractionOperator.

    Warning:
        Even assuming that each creation or annihilation operator appears
        at most a constant number of times in the original operator, the
        runtime of this method is exponential in the number of qubits.
    """
    if not isinstance(fermion_operator, FermionOperator):
        raise TypeError('Input must be a FermionOperator.')

    if n_qubits is None:
        n_qubits = count_qubits(fermion_operator)
    if n_qubits < count_qubits(fermion_operator):
        n_qubits = count_qubits(fermion_operator)

    # Normal order the terms and initialize.
    fermion_operator = normal_ordered(fermion_operator)
    constant = 0.
    one_body = numpy.zeros((n_qubits, n_qubits), complex)
    two_body = numpy.zeros((n_qubits, n_qubits, n_qubits, n_qubits), complex)

    # Loop through terms and assign to matrix.
    for term in fermion_operator.terms:
        coefficient = fermion_operator.terms[term]

        # Handle constant shift.
        if len(term) == 0:
            constant = coefficient

        elif len(term) == 2:
            # Handle one-body terms.
            if [operator[1] for operator in term] == [1, 0]:
                p, q = [operator[0] for operator in term]
                one_body[p, q] = coefficient
            else:
                raise InteractionOperatorError('FermionOperator does not map '
                                               'to InteractionOperator.')

        elif len(term) == 4:
            # Handle two-body terms.
            if [operator[1] for operator in term] == [1, 1, 0, 0]:
                p, q, r, s = [operator[0] for operator in term]
                two_body[p, q, r, s] = coefficient
            else:
                raise InteractionOperatorError('FermionOperator does not map '
                                               'to InteractionOperator.')

        else:
            # Handle non-molecular Hamiltonian.
            raise InteractionOperatorError('FermionOperator does not map '
                                           'to InteractionOperator.')

    # Form InteractionOperator and return.
    interaction_operator = InteractionOperator(constant, one_body, two_body)
    return interaction_operator
Exemplo n.º 19
0
def hartree_fock_state_jellium(grid,
                               n_electrons,
                               spinless=True,
                               plane_wave=False):
    """Give the Hartree-Fock state of jellium.

    Args:
        grid (Grid): The discretization to use.
        n_electrons (int): Number of electrons in the system.
        spinless (bool): Whether to use the spinless model or not.
        plane_wave (bool): Whether to return the Hartree-Fock state in
                           the plane wave (True) or dual basis (False).

    Notes:
        The jellium model is built up by filling the lowest-energy
        single-particle states in the plane-wave Hamiltonian until
        n_electrons states are filled.
    """
    # Get the jellium Hamiltonian in the plane wave basis.
    hamiltonian = jellium_model(grid, spinless, plane_wave=True)
    hamiltonian = normal_ordered(hamiltonian)
    hamiltonian.compress()

    # Enumerate the single-particle states.
    n_single_particle_states = (grid.length**grid.dimensions)
    if not spinless:
        n_single_particle_states *= 2

    # Compute the energies for each of the single-particle states.
    single_particle_energies = numpy.zeros(n_single_particle_states,
                                           dtype=float)
    for i in range(n_single_particle_states):
        single_particle_energies[i] = hamiltonian.terms.get(((i, 1), (i, 0)),
                                                            0.0)

    # The number of occupied single-particle states is the number of electrons.
    # Those states with the lowest single-particle energies are occupied first.
    occupied_states = single_particle_energies.argsort()[:n_electrons]

    if plane_wave:
        # In the plane wave basis the HF state is a single determinant.
        hartree_fock_state_index = numpy.sum(2**occupied_states)
        hartree_fock_state = csr_matrix(
            ([1.0], ([hartree_fock_state_index], [0])),
            shape=(2**n_single_particle_states, 1))

    else:
        # Inverse Fourier transform the creation operators for the state to get
        # to the dual basis state, then use that to get the dual basis state.
        hartree_fock_state_creation_operator = FermionOperator.identity()
        for state in occupied_states[::-1]:
            hartree_fock_state_creation_operator *= (FermionOperator(
                ((int(state), 1), )))
        dual_basis_hf_creation_operator = inverse_fourier_transform(
            hartree_fock_state_creation_operator, grid, spinless)

        dual_basis_hf_creation = normal_ordered(
            dual_basis_hf_creation_operator)

        # Initialize the HF state as a sparse matrix.
        hartree_fock_state = csr_matrix(([], ([], [])),
                                        shape=(2**n_single_particle_states, 1),
                                        dtype=complex)

        # Populate the elements of the HF state in the dual basis.
        for term in dual_basis_hf_creation.terms:
            index = 0
            for operator in term:
                index += 2**operator[0]
            hartree_fock_state[index, 0] = dual_basis_hf_creation.terms[term]

    return hartree_fock_state