def test_integration_jellium_hamiltonian_with_negation(self):
        hamiltonian = normal_ordered(
            jellium_model(Grid(2, 3, 1.), plane_wave=False))

        part_a = FermionOperator.zero()
        part_b = FermionOperator.zero()

        add_to_a_or_b = 0  # add to a if 0; add to b if 1
        for term, coeff in hamiltonian.terms.items():
            # Partition terms in the Hamiltonian into part_a or part_b
            if add_to_a_or_b:
                part_a += FermionOperator(term, coeff)
            else:
                part_b += FermionOperator(term, coeff)
            add_to_a_or_b ^= 1

        reference = normal_ordered(commutator(part_a, part_b))
        result = commutator_ordered_diagonal_coulomb_with_two_body_operator(
            part_a, part_b)

        self.assertTrue(result.isclose(reference))

        negative = commutator_ordered_diagonal_coulomb_with_two_body_operator(
            part_b, part_a)
        result += negative

        self.assertTrue(result.isclose(FermionOperator.zero()))
예제 #2
0
    def two_body_sz_adapted(self):
        """
        Doubles generators each with distinct Sz expectation value.

        G^{isigma, jtau, ktau, lsigma) for sigma, tau in 0, 1
        """
        for i, j, k, l in product(range(self.norbs), repeat=4):
            if i < j and k < l:
                op_aa = ((2 * i, 1), (2 * j, 1), (2 * k, 0), (2 * l, 0))
                op_bb = ((2 * i + 1, 1), (2 * j + 1, 1), (2 * k + 1, 0),
                         (2 * l + 1, 0))
                fop_aa = of.FermionOperator(op_aa)
                fop_aa = fop_aa - of.hermitian_conjugated(fop_aa)
                fop_bb = of.FermionOperator(op_bb)
                fop_bb = fop_bb - of.hermitian_conjugated(fop_bb)
                fop_aa = of.normal_ordered(fop_aa)
                fop_bb = of.normal_ordered(fop_bb)
                self.op_pool.append(fop_aa)
                self.op_pool.append(fop_bb)

            op_ab = ((2 * i, 1), (2 * j + 1, 1), (2 * k + 1, 0), (2 * l, 0))
            fop_ab = of.FermionOperator(op_ab)
            fop_ab = fop_ab - of.hermitian_conjugated(fop_ab)
            fop_ab = of.normal_ordered(fop_ab)
            if not np.isclose(fop_ab.induced_norm(), 0):
                self.op_pool.append(fop_ab)
예제 #3
0
def test_op_pool():
    op = OperatorPool(2, [0], [1])
    op.singlet_t2()
    true_generator = of.FermionOperator(((1, 1), (0, 1), (3, 0), (2, 0))) - \
                     of.FermionOperator(((3, 1), (2, 1), (1, 0), (0, 0)))

    assert np.isclose(
        of.normal_ordered(op.op_pool[0] - true_generator).induced_norm(), 0)

    op = OperatorPool(2, [0], [1])
    op.two_body_sz_adapted()
    assert len(op.op_pool) == 20

    true_generator0 = of.FermionOperator('1^ 0^ 2 1') - \
                      of.FermionOperator('2^ 1^ 1 0')
    assert np.isclose(
        of.normal_ordered(op.op_pool[0] - true_generator0).induced_norm(), 0)

    true_generator_end = of.FermionOperator('3^ 0^ 3 2') - \
                         of.FermionOperator('3^ 2^ 3 0')
    assert np.isclose(
        of.normal_ordered(op.op_pool[-1] - true_generator_end).induced_norm(),
        0)

    op = OperatorPool(2, [0], [1])
    op.one_body_sz_adapted()
    for gen in op.op_pool:
        for ladder_idx, _ in gen.terms.items():
            # check if one body terms are generated
            assert len(ladder_idx) == 2
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 _ 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
def test_interaction_operator_interconversion(n_modes, seed):
    operator = openfermion.random_interaction_operator(n_modes,
                                                       real=False,
                                                       seed=seed)
    gates = ofc.fermionic_simulation_gates_from_interaction_operator(operator)
    other_operator = sum_of_interaction_operator_gate_generators(
        n_modes, gates)
    operator = openfermion.normal_ordered(operator)
    other_operator = openfermion.normal_ordered(other_operator)
    assert operator == other_operator
    def test_integration_random_diagonal_coulomb_hamiltonian(self):
        hamiltonian1 = normal_ordered(
            get_fermion_operator(
                random_diagonal_coulomb_hamiltonian(n_qubits=7)))
        hamiltonian2 = normal_ordered(
            get_fermion_operator(
                random_diagonal_coulomb_hamiltonian(n_qubits=7)))

        reference = normal_ordered(commutator(hamiltonian1, hamiltonian2))
        result = commutator_ordered_diagonal_coulomb_with_two_body_operator(
            hamiltonian1, hamiltonian2)

        self.assertTrue(result.isclose(reference))
예제 #7
0
def get_one_qubit_hydrogen_hamiltonian(
    interaction_operator: Union[InteractionOperator, str]
):
    """Generate a one qubit H2 hamiltonian from a corresponding interaction operator.

    Original H2 hamiltonian will be reduced to a 2 x 2 matrix defined on a subspace
    spanned by |0011> and |1100> and expanded in terms of I, X, Y, and Z matrices

    Args:
        interaction_operator: The input interaction operator
    """
    if isinstance(interaction_operator, str):
        interaction_operator = load_interaction_operator(interaction_operator)

    fermion_h = get_fermion_operator(interaction_operator)

    # H00
    H00 = normal_ordered(FermionOperator("0 1") * fermion_h * FermionOperator("1^ 0^"))
    H00 = H00.terms[()]

    # H11
    H11 = normal_ordered(FermionOperator("2 3") * fermion_h * FermionOperator("3^ 2^"))
    H11 = H11.terms[()]

    # H10
    H10 = normal_ordered(FermionOperator("2 3") * fermion_h * FermionOperator("1^ 0^"))
    H10 = H10.terms[()]

    # H01
    H01 = np.conj(H10)

    one_qubit_h_matrix = np.array([[H00, H01], [H10, H11]])
    pauli_x = np.array([[0.0, 1.0], [1.0, 0.0]])
    pauli_y = np.array([[0.0, -1.0j], [1.0j, 0.0]])
    pauli_z = np.array([[1.0, 0.0], [0.0, -1.0]])

    r_id = 0.5 * np.trace(one_qubit_h_matrix)
    r_x = 0.5 * np.trace(one_qubit_h_matrix @ pauli_x)
    r_y = 0.5 * np.trace(one_qubit_h_matrix @ pauli_y)
    r_z = 0.5 * np.trace(one_qubit_h_matrix @ pauli_z)

    one_qubit_h = (
        r_id * QubitOperator("")
        + r_x * QubitOperator("X0")
        + r_y * QubitOperator("Y0")
        + r_z * QubitOperator("Z0")
    )

    save_qubit_operator(one_qubit_h, "qubit-operator.json")
예제 #8
0
 def one_body_sz_adapted(self):
     # alpha-alpha rotation
     # beta-beta rotation
     for i, j in product(range(self.norbs), repeat=2):
         if i > j:
             op_aa = ((2 * i, 1), (2 * j, 0))
             op_bb = ((2 * i + 1, 1), (2 * j + 1, 0))
             fop_aa = of.FermionOperator(op_aa)
             fop_aa = fop_aa - of.hermitian_conjugated(fop_aa)
             fop_bb = of.FermionOperator(op_bb)
             fop_bb = fop_bb - of.hermitian_conjugated(fop_bb)
             fop_aa = of.normal_ordered(fop_aa)
             fop_bb = of.normal_ordered(fop_bb)
             self.op_pool.append(fop_aa)
             self.op_pool.append(fop_bb)
예제 #9
0
def test_erpa_eom_ham_h2():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    rha_fermion = of.get_fermion_operator(reduced_ham)
    permuted_hijkl = np.einsum('ijlk', reduced_ham.two_body_tensor)
    opdm = np.diag([1] * molecule.n_electrons + [0] *
                   (molecule.n_qubits - molecule.n_electrons))
    tpdm = 2 * of.wedge(opdm, opdm, (1, 1), (1, 1))
    rdms = of.InteractionRDM(opdm, tpdm)
    dim = reduced_ham.one_body_tensor.shape[0] // 2
    full_basis = {}  # erpa basis.  A, B basis in RPA language
    cnt = 0
    for p, q in product(range(dim), repeat=2):
        if p < q:
            full_basis[(p, q)] = cnt
            full_basis[(q, p)] = cnt + dim * (dim - 1) // 2
            cnt += 1
    for rkey in full_basis.keys():
        p, q = rkey
        for ckey in full_basis.keys():
            r, s = ckey
            for sigma, tau in product([0, 1], repeat=2):
                test = erpa_eom_hamiltonian(permuted_hijkl, tpdm,
                                            2 * q + sigma, 2 * p + sigma,
                                            2 * r + tau, 2 * s + tau).real
                qp_op = of.FermionOperator(
                    ((2 * q + sigma, 1), (2 * p + sigma, 0)))
                rs_op = of.FermionOperator(
                    ((2 * r + tau, 1), (2 * s + tau, 0)))
                erpa_op = of.normal_ordered(
                    of.commutator(qp_op, of.commutator(rha_fermion, rs_op)))
                true = rdms.expectation(of.get_interaction_operator(erpa_op))
                assert np.isclose(true, test)
    def test_commutator(self):
        operator_a = (
            FermionOperator('0^ 0', 0.3) + FermionOperator('1^ 1', 0.1j) +
            FermionOperator('1^ 0^ 1 0', -0.2) + FermionOperator('1^ 3') +
            FermionOperator('3^ 0') + FermionOperator('3^ 2', 0.017) -
            FermionOperator('2^ 3', 1.99) + FermionOperator('3^ 1^ 3 1', .09) +
            FermionOperator('2^ 0^ 2 0', .126j) +
            FermionOperator('4^ 2^ 4 2') + FermionOperator('3^ 0^ 3 0'))

        operator_b = (
            FermionOperator('3^ 1', 0.7) + FermionOperator('1^ 3', -9.) +
            FermionOperator('1^ 0^ 3 0', 0.1) -
            FermionOperator('3^ 0^ 1 0', 0.11) + FermionOperator('3^ 2^ 3 2') +
            FermionOperator('3^ 1^ 3 1', -1.37) +
            FermionOperator('4^ 2^ 4 2') + FermionOperator('4^ 1^ 4 1') +
            FermionOperator('1^ 0^ 4 0', 16.7) +
            FermionOperator('1^ 0^ 4 3', 1.67) +
            FermionOperator('4^ 3^ 5 2', 1.789j) +
            FermionOperator('6^ 5^ 4 1', -11.789j))

        reference = normal_ordered(commutator(operator_a, operator_b))
        result = commutator_ordered_diagonal_coulomb_with_two_body_operator(
            operator_a, operator_b)

        diff = result - reference
        self.assertTrue(diff.isclose(FermionOperator.zero()))
예제 #11
0
    def singlet_t2(self):
        """
        Generate singlet rotations
        T_{ij}^{ab} = T^(v1a, v2b)_{o1a, o2b} + T^(v1b, v2a)_{o1b, o2a} +
                      T^(v1a, v2a)_{o1a, o2a} + T^(v1b, v2b)_{o1b, o2b}

        where v1,v2 are indices of the virtual obritals and o1, o2 are
        indices of the occupied orbitals with respect to the Hartree-Fock
        reference.
        """
        for oo_i in self.occ:
            for oo_j in self.occ:
                for vv_a in self.virt:
                    for vv_b in self.virt:
                        term = of.FermionOperator()
                        for sigma, tau in product(range(2), repeat=2):
                            op = ((2 * vv_a + sigma, 1), (2 * vv_b + tau, 1),
                                  (2 * oo_j + tau, 0), (2 * oo_i + sigma, 0))
                            if (2 * vv_a + sigma == 2 * vv_b + tau
                                    or 2 * oo_j + tau == 2 * oo_i + sigma):
                                continue
                            fop = of.FermionOperator(op, coefficient=0.5)
                            fop = fop - of.hermitian_conjugated(fop)
                            fop = of.normal_ordered(fop)
                            term += fop
                        self.op_pool.append(term)
예제 #12
0
def get_one_qubit_hydrogen_hamiltonian(
        interaction_operator: Union[InteractionOperator, str]):
    """Generate a one qubit H2 hamiltonian from a corresponding interaction operator. 

    Original H2 hamiltonian will be reduced to a 2 x 2 matrix defined on a subspace spanned
    by |0011> and |1100> and expanded in terms of I, X, Y, and Z matrices

    ARGS:
        interaction_operator (Union[InteractionOperator, str]): The input interaction operator
    """
    if isinstance(interaction_operator, str):
        interaction_operator = load_interaction_operator(interaction_operator)

    fermion_h = get_fermion_operator(interaction_operator)

    # H00
    H00 = normal_ordered(
        FermionOperator('0 1') * fermion_h * FermionOperator('1^ 0^'))
    H00 = H00.terms[()]

    # H11
    H11 = normal_ordered(
        FermionOperator('2 3') * fermion_h * FermionOperator('3^ 2^'))
    H11 = H11.terms[()]

    # H10
    H10 = normal_ordered(
        FermionOperator('2 3') * fermion_h * FermionOperator('1^ 0^'))
    H10 = H10.terms[()]

    # H01
    H01 = np.conj(H10)

    one_qubit_h_matrix = np.array([[H00, H01], [H10, H11]])
    pauli_x = np.array([[0., 1.], [1., 0.]])
    pauli_y = np.array([[0., -1.j], [1.j, 0.]])
    pauli_z = np.array([[1., 0.], [0., -1.]])

    r_id = 0.5 * np.trace(one_qubit_h_matrix)
    r_x = 0.5 * np.trace(one_qubit_h_matrix @ pauli_x)
    r_y = 0.5 * np.trace(one_qubit_h_matrix @ pauli_y)
    r_z = 0.5 * np.trace(one_qubit_h_matrix @ pauli_z)

    one_qubit_h = r_id * QubitOperator('') + r_x * QubitOperator(
        'X0') + r_y * QubitOperator('Y0') + r_z * QubitOperator('Z0')

    save_qubit_operator(one_qubit_h, 'qubit-operator.json')
예제 #13
0
def to_cirq_ncr(wfn: 'wavefunction.Wavefunction') -> numpy.ndarray:
    nqubit = wfn.norb() * 2
    ops = normal_ordered(fqe_to_fermion_operator(wfn))
    wf = numpy.zeros(2**nqubit, dtype=numpy.complex128)
    for term, coeff in ops.terms.items():
        occ_idx = sum([2**(nqubit - oo[0] - 1) for oo in term])
        wf[occ_idx] = coeff
    return wf
def assert_interaction_operator_consistent(gate):
    interaction_op = gate.interaction_operator_generator()
    other_gate = gate.from_interaction_operator(operator=interaction_op)
    if other_gate is None:
        assert np.allclose(gate.weights, 0)
    else:
        assert cirq.approx_eq(gate, other_gate)
    interaction_op = openfermion.normal_ordered(interaction_op)
    other_interaction_op = openfermion.InteractionOperator.zero(
        interaction_op.n_qubits)
    super(type(gate),
          gate).interaction_operator_generator(operator=other_interaction_op)
    other_interaction_op = openfermion.normal_ordered(interaction_op)
    assert interaction_op == other_interaction_op

    other_interaction_op = super(type(gate),
                                 gate).interaction_operator_generator()
    other_interaction_op = openfermion.normal_ordered(interaction_op)
    assert interaction_op == other_interaction_op
def benchmark_commutator_diagonal_coulomb_operators_2D_spinless_jellium(
        side_length):
    """Test speed of computing commutators using specialized functions.

    Args:
        side_length: The side length of the 2D jellium grid. There are
            side_length ** 2 qubits, and O(side_length ** 4) terms in the
            Hamiltonian.

    Returns:
        runtime_commutator: The time it takes to compute a commutator, after
            partitioning the terms and normal ordering, using the regular
            commutator function.
        runtime_diagonal_commutator: The time it takes to compute the same
            commutator using methods restricted to diagonal Coulomb operators.
    """
    hamiltonian = normal_ordered(
        jellium_model(Grid(2, side_length, 1.), plane_wave=False))

    part_a = FermionOperator.zero()
    part_b = FermionOperator.zero()
    add_to_a_or_b = 0  # add to a if 0; add to b if 1
    for term, coeff in hamiltonian.terms.items():
        # Partition terms in the Hamiltonian into part_a or part_b
        if add_to_a_or_b:
            part_a += FermionOperator(term, coeff)
        else:
            part_b += FermionOperator(term, coeff)
        add_to_a_or_b ^= 1

    start = time.time()
    _ = normal_ordered(commutator(part_a, part_b))
    end = time.time()
    runtime_commutator = end - start

    start = time.time()
    _ = commutator_ordered_diagonal_coulomb_with_two_body_operator(
        part_a, part_b)
    end = time.time()
    runtime_diagonal_commutator = end - start

    return runtime_commutator, runtime_diagonal_commutator
예제 #16
0
    def test_split_operator_error_operator_VT_order_against_definition(self):
        hamiltonian = (normal_ordered(fermi_hubbard(3, 3, 1., 4.0)) -
                       2.3 * FermionOperator.identity())
        potential_terms, kinetic_terms = (
            diagonal_coulomb_potential_and_kinetic_terms_as_arrays(
                hamiltonian))
        potential = sum(potential_terms, FermionOperator.zero())
        kinetic = sum(kinetic_terms, FermionOperator.zero())

        error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='V+T'))

        # V-then-T ordered double commutators: [V, [T, V]] + [T, [T, V]] / 2
        inner_commutator = normal_ordered(commutator(kinetic, potential))
        error_operator_definition = normal_ordered(
            commutator(potential, inner_commutator))
        error_operator_definition += normal_ordered(
            commutator(kinetic, inner_commutator)) / 2.0
        error_operator_definition /= 12.0

        self.assertEqual(error_operator, error_operator_definition)
예제 #17
0
 def _get_fermionic_operators(self) -> [of.FermionOperator]:
     """Returns second-quantization Hamiltonian in fermionic operator form"""
     # UCC-Single amplitudes
     _single_amp = self.molecule.ccsd_single_amps
     # UCC-Double amplitudes
     _double_amp = self.molecule.ccsd_double_amps
     # Creation/Annihilation operators
     _ucc_operator = of.normal_ordered(
         of.uccsd_generator(_single_amp, _double_amp))
     # check that _ucc_operator is either a FermionOperator or a list of FermionOperators
     if isinstance(_ucc_operator, of.FermionOperator):
         _ucc_operator = list(_ucc_operator)
     return _ucc_operator
예제 #18
0
def test_fermionic_hamiltonian_from_integrals(g, n_qubits):
    rg = RichardsonGaudin(g, n_qubits)
    #hc, hr1, hr2 = rg.hc, rg.hr1, rg.hr2
    doci = rg
    constant = doci.constant
    reference_constant = 0

    doci_qubit_op = doci.qubit_operator
    doci_mat = get_sparse_operator(doci_qubit_op).toarray()
    doci_eigvals = np.linalg.eigh(doci_mat)[0]

    tensors = doci.n_body_tensors
    one_body_tensors, two_body_tensors = tensors[(1, 0)], tensors[(1, 1, 0, 0)]

    fermion_op = get_fermion_operator(
        InteractionOperator(constant, one_body_tensors, 0.5 * two_body_tensors))
    fermion_op = normal_ordered(fermion_op)
    fermion_mat = get_sparse_operator(fermion_op).toarray()
    fermion_eigvals = np.linalg.eigh(fermion_mat)[0]

    one_body_tensors2, two_body_tensors2 = rg.get_antisymmetrized_tensors()
    fermion_op2 = get_fermion_operator(
        InteractionOperator(reference_constant, one_body_tensors2,
                            0.5 * two_body_tensors2))
    fermion_op2 = normal_ordered(fermion_op2)
    fermion_mat2 = get_sparse_operator(fermion_op2).toarray()
    fermion_eigvals2 = np.linalg.eigh(fermion_mat2)[0]

    for eigval in doci_eigvals:
        assert any(abs(fermion_eigvals -
                       eigval) < 1e-6), "The DOCI spectrum should have \
        been contained in the spectrum of the fermionic operator constructed via the \
        DOCIHamiltonian class"

    for eigval in doci_eigvals:
        assert any(abs(fermion_eigvals2 -
                       eigval) < 1e-6), "The DOCI spectrum should have \
예제 #19
0
    def test_strong_interaction_hubbard_VT_order_gives_larger_error(self):
        hamiltonian = normal_ordered(fermi_hubbard(4, 4, 1., 10.0))

        TV_error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='T+V'))
        TV_error_bound = numpy.sum(numpy.absolute(
            list(TV_error_operator.terms.values())))

        VT_error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='V+T'))
        VT_error_bound = numpy.sum(numpy.absolute(
            list(VT_error_operator.terms.values())))

        self.assertGreater(VT_error_bound, TV_error_bound)
예제 #20
0
    def test_intermediate_interaction_hubbard_TV_order_has_larger_error(self):
        hamiltonian = normal_ordered(fermi_hubbard(4, 4, 1., 4.0))

        TV_error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='T+V'))
        TV_error_bound = numpy.sum(numpy.absolute(
            list(TV_error_operator.terms.values())))

        VT_error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='V+T'))
        VT_error_bound = numpy.sum(numpy.absolute(
            list(VT_error_operator.terms.values())))

        self.assertAlmostEqual(TV_error_bound, 1706.66666666666)
        self.assertAlmostEqual(VT_error_bound, 1365.33333333333)
    def test_warning_on_bad_input_first_arg(self):
        with warnings.catch_warnings(record=True) as w:
            operator_a = FermionOperator('4^ 3^ 2 1')
            operator_b = FermionOperator('3^ 2^ 3 2')

            reference = normal_ordered(commutator(operator_a, operator_b))
            result = (
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    operator_a, operator_b))

            self.assertTrue(len(w) == 1)
            self.assertIn('Defaulted to standard commutator evaluation',
                          str(w[-1].message))

            # Result should still be correct in this case.
            diff = result - reference
            self.assertTrue(diff.isclose(FermionOperator.zero()))
예제 #22
0
    def test_hubbard_trotter_error_matches_low_depth_trotter_error(self):
        hamiltonian = normal_ordered(fermi_hubbard(3, 3, 1., 2.3))

        error_operator = (
            fermionic_swap_trotter_error_operator_diagonal_two_body(
                hamiltonian))
        error_operator.compress()

        # Unpack result into terms, indices they act on, and whether
        # they're hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian)
        terms, indices, is_hopping = result

        old_error_operator = low_depth_second_order_trotter_error_operator(
            terms, indices, is_hopping, jellium_only=True)

        old_error_operator -= error_operator
        self.assertEqual(old_error_operator, FermionOperator.zero())
예제 #23
0
    def test_1d_jellium_wigner_seitz_10_VT_order_gives_larger_error(self):
        hamiltonian = normal_ordered(jellium_model(
            hypercube_grid_with_given_wigner_seitz_radius_and_filling(
                1, 5, wigner_seitz_radius=10.,
                spinless=True), spinless=True, plane_wave=False))

        TV_error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='T+V'))
        TV_error_bound = numpy.sum(numpy.absolute(
            list(TV_error_operator.terms.values())))

        VT_error_operator = (
            split_operator_trotter_error_operator_diagonal_two_body(
                hamiltonian, order='V+T'))
        VT_error_bound = numpy.sum(numpy.absolute(
            list(VT_error_operator.terms.values())))

        self.assertGreater(VT_error_bound, TV_error_bound)
예제 #24
0
def get_polynomial_tensor(fermion_operator, n_qubits=None):
    r"""Convert a fermionic operator to a Polynomial Tensor.

    Args:
        fermion_operator (openferion.ops.FermionOperator): The operator.
        n_qubits (int): The number of qubits to be included in the
            PolynomialTensor. Must be at least equal to the number of qubits
            that are acted on by fermion_operator. If None, then the number of
            qubits is inferred from fermion_operator.

    Returns:
        openfermion.ops.PolynomialTensor: The tensor representation of the
            operator.
    """
    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):
        raise ValueError("Invalid number of qubits specified.")

    # Normal order the terms and initialize.
    fermion_operator = normal_ordered(fermion_operator)
    tensor_dict = {}

    # 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:
            tensor_dict[()] = coefficient

        else:
            key = tuple([operator[1] for operator in term])
            if tensor_dict.get(key) is None:
                tensor_dict[key] = np.zeros((n_qubits,) * len(key), complex)

            indices = tuple([operator[0] for operator in term])
            tensor_dict[key][indices] = coefficient

    return PolynomialTensor(tensor_dict)
예제 #25
0
def diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian):
    """Give the potential and kinetic terms of a diagonal Coulomb Hamiltonian
    as arrays.

    Args:
        hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to
                                       separate the potential and kinetic terms
                                       for. Identity is arbitrarily chosen
                                       to be part of the potential.

    Returns:
        Tuple of (potential_terms, kinetic_terms). Both elements of the tuple
        are numpy arrays of FermionOperators.
    """
    if not isinstance(hamiltonian, FermionOperator):
        try:
            hamiltonian = normal_ordered(get_fermion_operator(hamiltonian))
        except TypeError:
            raise TypeError('hamiltonian must be either a FermionOperator '
                            'or DiagonalCoulombHamiltonian.')

    potential = FermionOperator.zero()
    kinetic = FermionOperator.zero()

    for term, coeff in iteritems(hamiltonian.terms):
        acted = set(term[i][0] for i in range(len(term)))
        if len(acted) == len(term) / 2:
            potential += FermionOperator(term, coeff)
        else:
            kinetic += FermionOperator(term, coeff)

    potential_terms = numpy.array([
        FermionOperator(term, coeff)
        for term, coeff in iteritems(potential.terms)
    ])

    kinetic_terms = numpy.array([
        FermionOperator(term, coeff)
        for term, coeff in iteritems(kinetic.terms)
    ])

    return (potential_terms, kinetic_terms)
예제 #26
0
    def test_1D_jellium_trotter_error_matches_low_depth_trotter_error(self):
        hamiltonian = normal_ordered(jellium_model(
            hypercube_grid_with_given_wigner_seitz_radius_and_filling(
                1, 5, wigner_seitz_radius=10.,
                spinless=True), spinless=True, plane_wave=False))

        error_operator = (
            fermionic_swap_trotter_error_operator_diagonal_two_body(
                hamiltonian))
        error_operator.compress()

        # Unpack result into terms, indices they act on, and whether
        # they're hopping operators.
        result = simulation_ordered_grouped_low_depth_terms_with_info(
            hamiltonian)
        terms, indices, is_hopping = result

        old_error_operator = low_depth_second_order_trotter_error_operator(
            terms, indices, is_hopping, jellium_only=True)

        old_error_operator -= error_operator
        self.assertEqual(old_error_operator, FermionOperator.zero())
예제 #27
0
def commutator_ordered_diagonal_coulomb_with_two_body_operator(
        operator_a, operator_b, prior_terms=None):
    """Compute the commutator of two-body operators provided that both are
    normal-ordered and that the first only has diagonal Coulomb interactions.

    Args:
        operator_a: The first FermionOperator argument of the commutator.
            All terms must be normal-ordered, and furthermore either hopping
            operators (i^ j) or diagonal Coulomb operators (i^ i or i^ j^ i j).
        operator_b: The second FermionOperator argument of the commutator.
            operator_b can be any arbitrary two-body operator.
        prior_terms (optional): The initial FermionOperator to add to.

    Returns:
        The commutator, or the commutator added to prior_terms if provided.

    Notes:
        The function could be readily extended to the case of arbitrary
        two-body operator_a given that operator_b has the desired form;
        however, the extra check slows it down without desirable added utility.
    """
    if prior_terms is None:
        prior_terms = FermionOperator.zero()

    for term_a in operator_a.terms:
        coeff_a = operator_a.terms[term_a]
        for term_b in operator_b.terms:
            coeff_b = operator_b.terms[term_b]

            coefficient = coeff_a * coeff_b

            # If term_a == term_b the terms commute, nothing to add.
            if term_a == term_b or not term_a or not term_b:
                continue

            # Case 1: both operators are two-body, operator_a is i^ j^ i j.
            if (len(term_a) == len(term_b) == 4
                    and term_a[0][0] == term_a[2][0]
                    and term_a[1][0] == term_a[3][0]):
                _commutator_two_body_diagonal_with_two_body(
                    term_a, term_b, coefficient, prior_terms)

            # Case 2: commutator of a 1-body and a 2-body operator
            elif (len(term_b) == 4
                  and len(term_a) == 2) or (len(term_a) == 4
                                            and len(term_b) == 2):
                _commutator_one_body_with_two_body(term_a, term_b, coefficient,
                                                   prior_terms)

            # Case 3: both terms are one-body operators (both length 2)
            elif len(term_a) == 2 and len(term_b) == 2:
                _commutator_one_body_with_one_body(term_a, term_b, coefficient,
                                                   prior_terms)

            # Final case (case 4): violation of the input promise. Still
            # compute the commutator, but warn the user.
            else:
                warnings.warn('Defaulted to standard commutator evaluation '
                              'due to an out-of-spec operator.')
                additional = FermionOperator.zero()
                additional.terms[term_a + term_b] = coefficient
                additional.terms[term_b + term_a] = -coefficient
                additional = normal_ordered(additional)

                prior_terms += additional

    return prior_terms
예제 #28
0
def build_hamiltonian(ops: Union[FermionOperator, hamiltonian.Hamiltonian],
                      norb: int = 0,
                      conserve_number: bool = True,
                      e_0: complex = 0. + 0.j) -> 'hamiltonian.Hamiltonian':
    """Build a Hamiltonian object for the fqe

    Args:
        ops (FermionOperator, hamiltonian.Hamiltonian) - input operator as \
            FermionOperator.  If a Hamiltonian is passed as an argument, \
            this function returns as is.

        norb (int) - the number of orbitals in the system

        conserve_number (bool) - whether the operator conserves the number

        e_0 (complex) - the scalar part of the operator

    Returns:
        (hamiltonian.Hamiltonian) - General Hamiltonian that is created from ops
    """
    if isinstance(ops, hamiltonian.Hamiltonian):
        return ops

    if isinstance(ops, tuple):
        validate_tuple(ops)

        return general_hamiltonian.General(ops, e_0=e_0)

    if not isinstance(ops, FermionOperator):
        raise TypeError('Expected FermionOperator' \
                        ' but received {}.'.format(type(ops)))

    assert is_hermitian(ops)

    out: Any
    if len(ops.terms) <= 2:
        out = sparse_hamiltonian.SparseHamiltonian(ops, e_0=e_0)

    else:
        if not conserve_number:
            ops = transform_to_spin_broken(ops)

        ops = normal_ordered(ops)

        ops_rank, e_0 = split_openfermion_tensor(ops)  # type: ignore

        if norb == 0:
            for term in ops_rank.values():
                ablk, bblk = largest_operator_index(term)
                norb = max(norb, ablk // 2 + 1, bblk // 2 + 1)
        else:
            norb = norb

        ops_mat = {}
        maxrank = 0
        for rank, term in ops_rank.items():
            index = rank // 2 - 1
            ops_mat[index] = fermionops_tomatrix(term, norb)
            maxrank = max(index, maxrank)

        if len(ops_mat) == 1 and (0 in ops_mat):
            out = process_rank2_matrix(ops_mat[0], norb=norb, e_0=e_0)
        elif len(ops_mat) == 1 and \
            (1 in ops_mat) and \
            check_diagonal_coulomb(ops_mat[1]):
            out = diagonal_coulomb.DiagonalCoulomb(ops_mat[1], e_0=e_0)

        else:
            dtypes = [xx.dtype for xx in ops_mat.values()]
            dtypes = numpy.unique(dtypes)
            if len(dtypes) != 1:
                raise TypeError(
                    "Non-unique coefficient types for input operator")

            for i in range(maxrank + 1):
                if i not in ops_mat:
                    mat_dim = tuple([2 * norb for _ in range((i + 1) * 2)])
                    ops_mat[i] = numpy.zeros(mat_dim, dtype=dtypes[0])

            ops_mat2 = []
            for i in range(maxrank + 1):
                ops_mat2.append(ops_mat[i])

            out = general_hamiltonian.General(tuple(ops_mat2), e_0=e_0)

    out._conserve_number = conserve_number
    return out
예제 #29
0
def test_first_factorization():
    molecule = build_lih_moleculardata()
    oei, tei = molecule.get_integrals()
    lrt_obj = LowRankTrotter(oei=oei, tei=tei)
    (
        eigenvalues,
        one_body_squares,
        one_body_correction,
    ) = lrt_obj.first_factorization()

    # get true op
    n_qubits = molecule.n_qubits
    fermion_tei = of.FermionOperator()
    test_tei_tensor = np.zeros((n_qubits, n_qubits, n_qubits, n_qubits))
    for p, q, r, s in product(range(oei.shape[0]), repeat=4):
        if np.abs(tei[p, q, r, s]) < EQ_TOLERANCE:
            coefficient = 0.0
        else:
            coefficient = tei[p, q, r, s] / 2.0

        for sigma, tau in product(range(2), repeat=2):
            if 2 * p + sigma == 2 * q + tau or 2 * r + tau == 2 * s + sigma:
                continue
            term = (
                (2 * p + sigma, 1),
                (2 * q + tau, 1),
                (2 * r + tau, 0),
                (2 * s + sigma, 0),
            )
            fermion_tei += of.FermionOperator(term, coefficient=coefficient)
            test_tei_tensor[2 * p + sigma, 2 * q + tau, 2 * r + tau, 2 * s +
                            sigma] = coefficient

    mol_ham = of.InteractionOperator(
        one_body_tensor=np.zeros((n_qubits, n_qubits)),
        two_body_tensor=test_tei_tensor,
        constant=0,
    )

    # check induced norm on operator
    checked_op = of.FermionOperator()
    for (
            p,
            q,
    ) in product(range(n_qubits), repeat=2):
        term = ((p, 1), (q, 0))
        coefficient = one_body_correction[p, q]
        checked_op += of.FermionOperator(term, coefficient)

    # Build back two-body component.
    for l in range(one_body_squares.shape[0]):
        one_body_operator = of.FermionOperator()
        for p, q in product(range(n_qubits), repeat=2):
            term = ((p, 1), (q, 0))
            coefficient = one_body_squares[l, p, q]
            one_body_operator += of.FermionOperator(term, coefficient)
        checked_op += eigenvalues[l] * (one_body_operator**2)

    true_fop = of.normal_ordered(of.get_fermion_operator(mol_ham))
    difference = of.normal_ordered(checked_op - true_fop)
    assert np.isclose(0, difference.induced_norm())
예제 #30
0
def test_generalized_doubles():
    generator = generate_antisymm_generator(2)
    nso = generator.shape[0]
    for p, q, r, s in product(range(nso), repeat=4):
        if p < q and s < r:
            assert np.isclose(generator[p, q, r, s], -generator[q, p, r, s])

    ul, vl, one_body_residual, ul_ops, vl_ops, one_body_op = \
        doubles_factorization_svd(generator)

    generator_mat = np.reshape(np.transpose(generator, [0, 3, 1, 2]),
                               (nso**2, nso**2)).astype(np.float)
    one_body_residual_test = -np.einsum('pqrq->pr', generator)
    assert np.allclose(generator_mat, generator_mat.T)
    assert np.allclose(one_body_residual, one_body_residual_test)

    tgenerator_mat = np.zeros_like(generator_mat)
    for row_gem, col_gem in product(range(nso**2), repeat=2):
        p, s = row_gem // nso, row_gem % nso
        q, r = col_gem // nso, col_gem % nso
        tgenerator_mat[row_gem, col_gem] = generator[p, q, r, s]

    assert np.allclose(tgenerator_mat, generator_mat)

    u, sigma, vh = np.linalg.svd(generator_mat)

    fop = copy.deepcopy(one_body_op)
    fop2 = copy.deepcopy(one_body_op)
    fop3 = copy.deepcopy(one_body_op)
    fop4 = copy.deepcopy(one_body_op)
    for ll in range(len(sigma)):
        ul.append(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso)))
        ul_ops.append(
            get_fermion_op(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso))))
        vl.append(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso)))
        vl_ops.append(
            get_fermion_op(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso))))
        Smat = ul[ll] + vl[ll]
        Dmat = ul[ll] - vl[ll]

        S = ul_ops[ll] + vl_ops[ll]
        Sd = of.hermitian_conjugated(S)
        D = ul_ops[ll] - vl_ops[ll]
        Dd = of.hermitian_conjugated(D)
        op1 = S + 1j * of.hermitian_conjugated(S)
        op2 = S - 1j * of.hermitian_conjugated(S)
        op3 = D + 1j * of.hermitian_conjugated(D)
        op4 = D - 1j * of.hermitian_conjugated(D)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op1, of.hermitian_conjugated(op1))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op2, of.hermitian_conjugated(op2))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op3, of.hermitian_conjugated(op3))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op4, of.hermitian_conjugated(op4))).induced_norm(), 0)

        fop3 += (1 / 8) * ((S**2 - Sd**2) - (D**2 - Dd**2))
        fop4 += (1 / 16) * ((op1**2 + op2**2) - (op3**2 + op4**2))

        op1mat = Smat + 1j * Smat.T
        op2mat = Smat - 1j * Smat.T
        op3mat = Dmat + 1j * Dmat.T
        op4mat = Dmat - 1j * Dmat.T

        assert np.allclose(of.commutator(op1mat, op1mat.conj().T), 0)
        assert np.allclose(of.commutator(op2mat, op2mat.conj().T), 0)
        assert np.allclose(of.commutator(op3mat, op3mat.conj().T), 0)
        assert np.allclose(of.commutator(op4mat, op4mat.conj().T), 0)

        # check that we have normal operators and that the outer product
        # of their eigenvalues is imaginary. Also check vv is unitary
        if not np.isclose(sigma[ll], 0):
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op1mat) - op1).induced_norm(),
                0)
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op2mat) - op2).induced_norm(),
                0)
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op3mat) - op3).induced_norm(),
                0)
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op4mat) - op4).induced_norm(),
                0)

            ww, vv = np.linalg.eig(op1mat)
            eye = np.eye(nso)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)
            ww, vv = np.linalg.eig(op2mat)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)
            ww, vv = np.linalg.eig(op3mat)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)
            ww, vv = np.linalg.eig(op4mat)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)

        fop2 += 0.25 * ul_ops[ll] * vl_ops[ll]
        fop2 += 0.25 * vl_ops[ll] * ul_ops[ll]
        fop2 += -0.25 * of.hermitian_conjugated(
            vl_ops[ll]) * of.hermitian_conjugated(ul_ops[ll])
        fop2 += -0.25 * of.hermitian_conjugated(
            ul_ops[ll]) * of.hermitian_conjugated(vl_ops[ll])

        fop += vl_ops[ll] * ul_ops[ll]

    true_fop = get_fermion_op(generator)
    assert np.isclose(of.normal_ordered(fop - true_fop).induced_norm(), 0)
    assert np.isclose(of.normal_ordered(fop2 - true_fop).induced_norm(), 0)
    assert np.isclose(of.normal_ordered(fop3 - true_fop).induced_norm(), 0)
    assert np.isclose(of.normal_ordered(fop4 - true_fop).induced_norm(), 0)