Esempio n. 1
0
    def __init__(self, orbprop, orb_qubit_map=None, symm_eigval_map=None):
        ham_fop = construct_exact_ham(orbprop)
        ham_qop = openfermion.jordan_wigner(ham_fop)
        n_site = openfermion.count_qubits(ham_qop)

        if symm_eigval_map is not None:
            symm_paulistr = symm.symmetry_pauli_string(
                orbprop, operation_list=list(symm_eigval_map))
            for symmop, qop in symm_paulistr.items():
                print('[ham_qop, {:5s}] = [ham_qop, {}] = {}'.format(
                    symmop, str(qop), openfermion.commutator(ham_qop, qop)))

            self.remover = symm.SymmRemover(n_site,
                                            list(symm_paulistr.values()))
            self.remover.set_eigvals(
                [symm_eigval_map[symm] for symm in symm_paulistr])
            ham_qop_tapered = self.remover.remove_symm_qubits(ham_qop)
            print(self.remover)
            print('len(ham         ) = ', len(ham_qop.terms))
            print('len(ham[tapered]) = ', len(ham_qop_tapered.terms))

            ham_qop = ham_qop_tapered

        self.ham_fop = ham_fop
        self.ham_qop = ham_qop
        self.ham_tensor = openfermion.get_sparse_operator(ham_qop)
        self.n_site = n_site
        self.n_qubit = openfermion.count_qubits(ham_qop)

        nterm = [0] * (
            n_site + 1
        )  # number of paulistrs which length is n (n=0,1,..,n_site).
        for k in ham_qop.terms:
            nterm[len(k)] += 1
        self.nterm = nterm
Esempio n. 2
0
def reverse_qubit_order(qubit_operator: QubitOperator, n_qubits: Optional[int] = None):
    """Reverse the order of qubit indices in a qubit operator.

    Args:
        qubit_operator (openfermion.QubitOperator): the operator
        n_qubits (int): total number of qubits. Needs to be provided when
                    the size of the system of interest is greater than the size of qubit operator (optional)

    Returns:
        reversed_op (openfermion.ops.QubitOperator)
    """

    reversed_op = QubitOperator()

    if n_qubits is None:
        n_qubits = count_qubits(qubit_operator)
    if n_qubits < count_qubits(qubit_operator):
        raise ValueError("Invalid number of qubits specified.")

    for term in qubit_operator.terms:
        new_term = []
        for factor in term:
            new_factor = list(factor)
            new_factor[0] = n_qubits - 1 - new_factor[0]
            new_term.append(tuple(new_factor))
        reversed_op += QubitOperator(tuple(new_term), qubit_operator.terms[term])
    return reversed_op
Esempio n. 3
0
def test_load_molecular_hamiltonian():
    geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., 1.4))]

    lih_hamiltonian = openfermionpyscf.generate_molecular_hamiltonian(
        geometry, 'sto-3g', 1, 0, 2, 2)
    assert openfermion.count_qubits(lih_hamiltonian) == 4

    lih_hamiltonian = openfermionpyscf.generate_molecular_hamiltonian(
        geometry, 'sto-3g', 1, 0, 2, 3)
    assert openfermion.count_qubits(lih_hamiltonian) == 6

    lih_hamiltonian = openfermionpyscf.generate_molecular_hamiltonian(
        geometry, 'sto-3g', 1, 0, None, None)
    assert openfermion.count_qubits(lih_hamiltonian) == 12
def produce_simulation_test_parameters(
        hamiltonian: Hamiltonian,
        time: float,
        seed: Optional[int] = None) -> Tuple[numpy.ndarray, numpy.ndarray]:
    """Produce objects for testing Hamiltonian simulation.

    Produces a random initial state and evolves it under the given Hamiltonian
    for the specified amount of time. Returns the initial state and final
    state.

    Args:
        hamiltonian: The Hamiltonian to evolve under.
        time: The time to evolve for
        seed: An RNG seed.
    """

    n_qubits = openfermion.count_qubits(hamiltonian)

    # Construct a random initial state
    initial_state = openfermion.haar_random_vector(2**n_qubits, seed)

    # Simulate exact evolution
    hamiltonian_sparse = openfermion.get_sparse_operator(hamiltonian)
    exact_state = scipy.sparse.linalg.expm_multiply(
        -1j * time * hamiltonian_sparse, initial_state)

    # Make sure the time is not too small
    assert fidelity(exact_state, initial_state) < .95

    return initial_state, exact_state
Esempio n. 5
0
def qubitop_to_paulisum(
    qubit_operator: QubitOperator,
    qubits: Union[List[cirq.GridQubit], List[cirq.LineQubit]] = None,
) -> cirq.PauliSum:
    """Convert and openfermion QubitOperator to a cirq PauliSum

    Args:
        qubit_operator (openfermion.QubitOperator): The openfermion operator to convert
        qubits()

    Returns:
        cirq.PauliSum
    """
    operator_map = {"X": cirq.X, "Y": cirq.Y, "Z": cirq.Z}

    if qubits is None:
        qubits = [cirq.GridQubit(i, 0) for i in range(count_qubits(qubit_operator))]

    converted_sum = cirq.PauliSum()
    for term, coefficient in qubit_operator.terms.items():

        # Identity term
        if len(term) == 0:
            converted_sum += coefficient
            continue

        cirq_term = cirq.PauliString()
        for qubit_index, operator in term:
            cirq_term *= operator_map[operator](qubits[qubit_index])
        converted_sum += cirq_term * coefficient

    return converted_sum
Esempio n. 6
0
def _get_diagonal_component_polynomial_tensor(polynomial_tensor):
    """Get the component of an interaction operator that is
    diagonal in the computational basis under Jordan-Wigner
    transformation (i.e., the terms that can be expressed
    as products of number operators).
    Args:
        interaction_operator (openfermion.ops.InteractionOperator): the operator

    Returns:
        tuple: two openfermion.ops.InteractionOperator objects. The first is the
            diagonal component, and the second is the remainder.
    """
    n_modes = count_qubits(polynomial_tensor)
    remainder_tensors = {}
    diagonal_tensors = {}

    diagonal_tensors[()] = polynomial_tensor.constant
    for key in polynomial_tensor.n_body_tensors:
        if key == ():
            continue
        remainder_tensors[key] = np.zeros((n_modes,) * len(key), complex)
        diagonal_tensors[key] = np.zeros((n_modes,) * len(key), complex)

        for indices in itertools.product(range(n_modes), repeat=len(key)):
            creation_counts = {}
            annihilation_counts = {}

            for meta_index, index in enumerate(indices):
                if key[meta_index] == 0:
                    if annihilation_counts.get(index) is None:
                        annihilation_counts[index] = 1
                    else:
                        annihilation_counts[index] += 1
                elif key[meta_index] == 1:
                    if creation_counts.get(index) is None:
                        creation_counts[index] = 1
                    else:
                        creation_counts[index] += 1

            term_is_diagonal = True
            for index in creation_counts:
                if creation_counts[index] != annihilation_counts.get(index):
                    term_is_diagonal = False
                    break
            if term_is_diagonal:
                for index in annihilation_counts:
                    if annihilation_counts[index] != creation_counts.get(index):
                        term_is_diagonal = False
                        break
            if term_is_diagonal:
                diagonal_tensors[key][indices] = polynomial_tensor.n_body_tensors[key][
                    indices
                ]
            else:
                remainder_tensors[key][indices] = polynomial_tensor.n_body_tensors[key][
                    indices
                ]

    return PolynomialTensor(diagonal_tensors), PolynomialTensor(remainder_tensors)
Esempio n. 7
0
 def do_transform(self, fermion_operator: openfermion.FermionOperator,
                  *args, **kwargs) -> openfermion.QubitOperator:
     if openfermion.count_qubits(fermion_operator) != self.n_orbitals * 2:
         raise Exception(
             "TaperedBravyiKitaev not ready for UCC generators yet")
     return openfermion.symmetry_conserving_bravyi_kitaev(
         fermion_operator,
         active_orbitals=self.active_orbitals,
         active_fermions=self.active_fermions)
Esempio n. 8
0
 def do_transform(self, fermion_operator: openfermion.FermionOperator,
                  *args, **kwargs) -> openfermion.QubitOperator:
     n_qubits = openfermion.count_qubits(fermion_operator)
     if n_qubits != self.n_orbitals * 2:
         raise Exception(
             "BravyiKitaevFast transformation currently only possible for full Hamiltonians (no UCC generators).\nfermion_operator was {}"
             .format(fermion_operator))
     op = openfermion.get_interaction_operator(fermion_operator)
     return openfermion.bravyi_kitaev_fast(op)
Esempio n. 9
0
def block_reduce(qubit_operator: openfermion.QubitOperator):
    """
    Reduce the qubit operator into blocks.

    If a qubit in `qubit_operator` is only acted by the identity operator or
    one kind of Pauli operator (or both of them), it is considered to be
    "generally stationary", and can be used to reduce the operator into several
    blocks.

    See also:
        `reduce`
        `inactivate_stationary_qubits`

    :param qubit_operator:
        The qubit operator to be block-reduced.

    :return:
        The reduced blocks.

    Example:

    >>> op = (openfermion.QubitOperator("X0 Y1 Z3   ", 0.1) +
    ...       openfermion.QubitOperator("Y0 Z1 Z3 Y4", 0.2) +
    ...       openfermion.QubitOperator("X0 Z1      ", 0.3) +
    ...       openfermion.QubitOperator("Y0       X4", 0.4))

    We can find that qubit 3 is "generally stationary", and thus we can use it
    to do the block reduction:
    >>> block_reduce(op)
    {((2, 'I'), (3, 'Z')): 0.1 [X0 Y1] +
    0.2 [Y0 Z1 Y2], ((2, 'I'), (3, 'I')): 0.3 [X0 Z1] +
    0.4 [Y0 X2]}

    Here, `0.1 [X0 Y1] + 0.2 [Y0 Z1]` and `0.3 [X0 Z1] + 0.4 [Y0]` are the two
    blocks corresponding to "I2 Z3" and "I2 I3".

    """
    n_qubits = openfermion.count_qubits(qubit_operator)
    appeared_paulis = [set() for _ in range(n_qubits)]

    for term, _ in qubit_operator.terms.items():
        for qubit_index, pauli in term:
            appeared_paulis[qubit_index].add(pauli)

    reducible_qubits = []
    for i, paulis in enumerate(appeared_paulis):
        if len(paulis) <= 1:
            reducible_qubits.append(i)

    grouped = group(qubit_operator, by_qubit_indices=reducible_qubits)
    reduced = {}
    for paulis_at_reducible_qubits, op in grouped.items():
        reduced[paulis_at_reducible_qubits] = reduce_inactive_qubits(
            op, reducible_qubits)[0]

    return reduced
Esempio n. 10
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)
Esempio n. 11
0
def get_ground_state_rdm_from_qubit_op(
    qubit_operator: QubitOperator, n_particles: int
) -> InteractionRDM:
    """Diagonalize operator and compute the ground state 1- and 2-RDM

    Args:
        qubit_operator (openfermion.QubitOperator): The openfermion operator to diagonalize
        n_particles (int): number of particles in the target ground state

    Returns:
        rdm (openfermion.InteractionRDM): interaction RDM of the ground state with the particle
            number n_particles
    """

    sparse_operator = get_sparse_operator(qubit_operator)
    e, ground_state_wf = jw_get_ground_state_at_particle_number(
        sparse_operator, n_particles
    )  # float/np.array pair
    n_qubits = count_qubits(qubit_operator)

    one_body_tensor = []
    for i in range(n_qubits):
        for j in range(n_qubits):
            idag_j = get_sparse_operator(
                FermionOperator(f"{i}^ {j}"), n_qubits=n_qubits
            )
            idag_j = idag_j.toarray()
            one_body_tensor.append(
                np.conjugate(ground_state_wf) @ idag_j @ ground_state_wf
            )

    one_body_tensor = np.array(one_body_tensor)
    one_body_tensor = one_body_tensor.reshape(n_qubits, n_qubits)

    two_body_tensor = np.zeros((n_qubits,) * 4, dtype=complex)
    for p in range(n_qubits):
        for q in range(0, p + 1):
            for r in range(n_qubits):
                for s in range(0, r + 1):
                    pdag_qdag_r_s = get_sparse_operator(
                        FermionOperator(f"{p}^ {q}^ {r} {s}"), n_qubits=n_qubits
                    )
                    pdag_qdag_r_s = pdag_qdag_r_s.toarray()
                    rdm_element = (
                        np.conjugate(ground_state_wf) @ pdag_qdag_r_s @ ground_state_wf
                    )
                    two_body_tensor[p, q, r, s] = rdm_element
                    two_body_tensor[q, p, r, s] = -rdm_element
                    two_body_tensor[q, p, s, r] = rdm_element
                    two_body_tensor[p, q, s, r] = -rdm_element

    return InteractionRDM(one_body_tensor, two_body_tensor)
Esempio n. 12
0
def test_expectation_values_paulisum(qubitop, state_binary):
    """Test PauliSum and QubitOperator expectation value."""
    n_qubits = openfermion.count_qubits(qubitop)
    state = numpy.zeros(2**n_qubits, dtype='complex64')
    state[int(state_binary, 2)] = 1.0
    qubit_map = {cirq.LineQubit(i): i for i in range(n_qubits)}

    pauli_str = qubit_operator_to_pauli_sum(qubitop, list(qubit_map.keys()))
    op_mat = openfermion.get_sparse_operator(qubitop, n_qubits)

    expct_qop = openfermion.expectation(op_mat, state)
    expct_pauli = pauli_str.expectation_from_state_vector(state, qubit_map)

    numpy.testing.assert_allclose(expct_qop, expct_pauli)
def test_simulate_trotter_simulate(hamiltonian, time, initial_state,
                                   exact_state, order, n_steps, algorithm,
                                   result_fidelity):

    n_qubits = openfermion.count_qubits(hamiltonian)
    qubits = cirq.LineQubit.range(n_qubits)

    start_state = initial_state

    circuit = cirq.Circuit(
        simulate_trotter(qubits, hamiltonian, time, n_steps, order, algorithm))

    final_state = circuit.apply_unitary_effect_to_state(start_state)
    correct_state = exact_state
    assert fidelity(final_state, correct_state) > result_fidelity
    # Make sure the time wasn't too small
    assert fidelity(final_state, start_state) < 0.95 * result_fidelity
Esempio n. 14
0
    def test_cirq_to_fqe_single(self):
        """Wrap together all the routines to build a wavefuntion that can be
        read by the fqe by simply passing a cirq wavefunction.
        """
        cof = numpy.array([0.3901112 - 0.1543j, 0.01213 + 0.79120j],
                          dtype=numpy.complex128)
        cof /= numpy.sqrt(numpy.vdot(cof, cof))
        wfn_ops = cof[0] * (ladder_op(0, 1) * ladder_op(1, 1) * ladder_op(2, 1))
        wfn_ops += cof[1] * (ladder_op(0, 1) * ladder_op(2, 1) *
                             ladder_op(3, 1))
        qpu = LineQubit.range(count_qubits(wfn_ops))
        cirq_wfn = qubit_wavefunction_from_vacuum(wfn_ops, qpu)
        fqe_wfn = transform.cirq_to_fqe_single(cirq_wfn, 3, 1, None)
        fqe_jw = jordan_wigner(fqe_wfn)
        test_key = list(wfn_ops.terms.keys())
        for keyval in list(fqe_jw.terms.keys()):
            self.assertTrue(keyval in test_key)

        for i in fqe_jw.terms:
            self.assertAlmostEqual(fqe_jw.terms[i], wfn_ops.terms[i])
def test_simulate_trotter_simulate_controlled(
        hamiltonian, time, initial_state, exact_state, order, n_steps,
        algorithm, result_fidelity):

    n_qubits = openfermion.count_qubits(hamiltonian)
    qubits = cirq.LineQubit.range(n_qubits)

    control = cirq.LineQubit(-1)
    zero = [1, 0]
    one = [0, 1]
    start_state = (numpy.kron(zero, initial_state)
                   + numpy.kron(one, initial_state)) / numpy.sqrt(2)

    circuit = cirq.Circuit.from_ops(simulate_trotter(
        qubits, hamiltonian, time, n_steps, order, algorithm, control))

    final_state = circuit.apply_unitary_effect_to_state(start_state)
    correct_state = (numpy.kron(zero, initial_state)
                     + numpy.kron(one, exact_state)) / numpy.sqrt(2)
    assert fidelity(final_state, correct_state) > result_fidelity
    # Make sure the time wasn't too small
    assert fidelity(final_state, start_state) < 0.95 * result_fidelity
Esempio n. 16
0
def test_low_rank_trotter_ansatz_params():

    n = openfermion.count_qubits(lih_hamiltonian)
    final_rank = 2
    ansatz = LowRankTrotterAnsatz(lih_hamiltonian,
                                  final_rank=final_rank,
                                  include_all_cz=True,
                                  include_all_z=True)
    assert len(list(ansatz.params())) == n + final_rank * (n + n *
                                                           (n - 1) // 2)

    ansatz = LowRankTrotterAnsatz(lih_hamiltonian, final_rank=2)
    assert set(ansatz.params()) == {
        cirq.Symbol(name)
        for name in {
            'U_0_0', 'U_0_0_0', 'U_0_1_0', 'U_1_0', 'U_1_0_0', 'U_1_1_0',
            'U_2_0', 'U_2_0_0', 'U_2_1_0', 'U_3_0', 'U_3_0_0', 'U_3_1_0',
            'V_0_1_0_0', 'V_0_1_1_0', 'V_0_2_0_0', 'V_0_2_1_0', 'V_0_3_0_0',
            'V_0_3_1_0', 'V_1_2_0_0', 'V_1_2_1_0', 'V_1_3_0_0', 'V_1_3_1_0',
            'V_2_3_0_0', 'V_2_3_1_0'
        }
    }
def qubitop_to_qiskitpauli(
        qubit_operator: QubitOperator) -> WeightedPauliOperator:
    """Convert a OpenFermion QubitOperator to a WeightedPauliOperator.

    Args:
        qubit_operator: OpenFermion QubitOperator to convert

    Returns:
        WeightedPauliOperator representing the qubit operator
    """
    if not isinstance(qubit_operator, QubitOperator):
        raise TypeError(
            "qubit_operator must be an OpenFermion QubitOperator object")

    terms = []
    for qubit_terms, coefficient in qubit_operator.terms.items():
        string_term = "I" * count_qubits(qubit_operator)
        for i, (term_qubit, term_pauli) in enumerate(qubit_terms):
            string_term = (string_term[:term_qubit] + term_pauli +
                           string_term[term_qubit + 1:])
        terms.append([coefficient, Pauli.from_label(string_term)])

    return WeightedPauliOperator(terms)
Esempio n. 18
0
def bit_mask_of_modes_acted_on_by_fermionic_terms(fermion_term_list,
                                                  n_qubits=None):
    """Create a mask of which modes of the system are acted on by which terms.

    Args:
        fermion_term_list (list of FermionOperators): A list of fermionic terms
            to calculate the bitmask for.
        n_qubits (int): The number of qubits (modes) in the system. If not
                        specified, defaults to the maximum of any term in
                        fermion_term_list.

    Returns:
        An n_qubits x len(fermion_term_list) boolean numpy array of whether
        each term acts on the given mode index.

    Raises:
        ValueError: if n_qubits is too small for the given terms.
    """
    if n_qubits is None:
        n_qubits = 0
        for term in fermion_term_list:
            n_qubits = max(n_qubits, count_qubits(term))

    mask = numpy.zeros((n_qubits, len(fermion_term_list)), dtype=bool)

    for term_number, term in enumerate(fermion_term_list):
        actions = term.terms
        for action in actions:
            for single_operator in action:
                mode = single_operator[0]
                try:
                    mask[mode][term_number] = True
                except IndexError:
                    raise ValueError('Bad n_qubits: must be greater than '
                                     'highest mode in any FermionOperator.')

    return mask
Esempio n. 19
0
    def test_jellium_hamiltonian_correctly_broken_up(self):
        grid = Grid(2, 3, 1.)

        hamiltonian = jellium_model(grid, spinless=True, plane_wave=False)

        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())

        true_potential = dual_basis_jellium_model(grid, spinless=True,
                                                  kinetic=False)
        true_kinetic = dual_basis_jellium_model(grid, spinless=True,
                                                potential=False)
        for i in range(count_qubits(true_kinetic)):
            coeff = true_kinetic.terms.get(((i, 1), (i, 0)))
            if coeff:
                true_kinetic -= FermionOperator(((i, 1), (i, 0)), coeff)
                true_potential += FermionOperator(((i, 1), (i, 0)), coeff)

        self.assertEqual(potential, true_potential)
        self.assertEqual(kinetic, true_kinetic)
Esempio n. 20
0
def reduce_inactive_qubits(
    qubit_operator: openfermion.QubitOperator,
    inactive_qubits: typing.Optional[typing.Iterable[int]] = None
) -> typing.Tuple[openfermion.QubitOperator, int]:
    """
    Reduce the inactive qubits in a qubit operator.

    :param qubit_operator:
        The qubit operator to be simplified (i.e. reduced).
    :param inactive_qubits:
        The qubits to be reduced from the qubit operator.

        If given out explicitly, the qubits in `inactive_qubits` will all be
        removed from the qubit operator (no matter whether it is really
        "inactive").

        If left to be default, the inactive qubits to be removed will be
        automatically determined. Here, "inactive" means that there are not
        any effective Pauli operators ("X", "Y", "Z") acting on the qubits in
        `qubit_operator`.

    :return:
        The reduced qubit operator, and the number of reduced qubits.

    Example:

    >>> op = (openfermion.QubitOperator("X0 Y1", 0.1) +
    ...       openfermion.QubitOperator("Z0 Y1 X3", 0.2))
    >>> reduce_inactive_qubits(op)
    (0.1 [X0 Y1] +
    0.2 [Z0 Y1 X2], 1)
    >>> reduce_inactive_qubits(op, (0, 2))
    (0.1 [Y0] +
    0.2 [Y0 X1], 2)
    >>> reduce_inactive_qubits(op, (0,))
    (0.1 [Y0] +
    0.2 [Y0 X2], 1)

    """
    if inactive_qubits is None:
        active_qubits = set()
        for term in qubit_operator.terms.keys():
            for qubit_index, pauli in term:
                active_qubits.add(qubit_index)
        active_qubits = sorted(active_qubits)
    else:
        active_qubits = sorted(
            q for q in range(0, openfermion.count_qubits(qubit_operator))
            if q not in inactive_qubits)

    reduced_qubit_op = openfermion.QubitOperator()
    for term, coefficient in qubit_operator.terms.items():
        reduced_term = tuple((active_qubits.index(qubit_index), pauli)
                             for qubit_index, pauli in term
                             if qubit_index in active_qubits)
        _op = openfermion.QubitOperator()
        _op.terms[reduced_term] = coefficient
        reduced_qubit_op += _op

    if inactive_qubits is None:  # if in the default case:
        assert len(active_qubits) == openfermion.count_qubits(reduced_qubit_op)
    else:
        # For cases like:
        #
        #     reduce_inactive_qubits(
        #         openfermion.QubitOperator("Z0 Y1 X3", 0.2),
        #         (3,)
        #     )
        #
        # active_qubits == (0, 1, 2), but the reduced operator is
        #
        #     openfermion.QubitOperator("Z0 Y1", 0.2)
        #
        # counting whose qubits will give out 2 instead of 3.
        pass

    num_reduced_qubits = \
        openfermion.count_qubits(qubit_operator) - len(active_qubits)

    return reduced_qubit_op, num_reduced_qubits
Esempio n. 21
0
def get_qubit_hamiltonian(g, basis, charge=0, spin=1, qubit_transf='jw'):

    ## Create OpenFermion molecule
    #mol = gto.Mole()
    #mol.atom = g
    #mol.basis = basis
    #mol.spin = spin
    #mol.charge = charge
    #mol.symmetry = False
    ##mol.max_memory = 1024
    #mol.build()

    multiplicity = spin + 1  # spin here is 2S ?
    mol = MolecularData(g, basis, multiplicity, charge)
    #mol.load()

    # Convert to PySCF molecule and run SCF
    print("Running run_pyscf...")
    print(f"Time: {time()}")
    print("=" * 20)
    mol = run_pyscf(mol)

    # Freeze some orbitals?
    occupied_indices = None
    active_indices = None

    #import pdb; pdb.set_trace()

    # Try:
    occupied_indices = range(183)
    active_indices = range(15)
    # when it stops lagging

    # Get Hamiltonian
    print("Running get_molecular_hamiltonian...")
    print(f"Time: {time()}")
    print("=" * 20)
    ham = mol.get_molecular_hamiltonian(occupied_indices=occupied_indices,
                                        active_indices=active_indices)

    print("Running get_fermion_operator...")
    print(f"Time: {time()}")
    print("=" * 20)
    hamf = get_fermion_operator(ham)

    print(f"Running {qubit_transf}...")
    print(f"Time: {time()}")
    print("=" * 20)

    if qubit_transf == 'bk':
        hamq = bravyi_kitaev(hamf)
    elif qubit_transf == 'jw':
        hamq = jordan_wigner(hamf)
    else:
        raise (ValueError(qubit_transf, 'Unknown transformation specified'))

    # Adapted from 10.5281/zenodo.2880550
    hamd = openfermion.get_diagonal_coulomb_hamiltonian(
        hamf, ignore_incompatible_terms=True)
    nqubits = openfermion.count_qubits(hamd)
    ansatz = openfermioncirq.SwapNetworkTrotterAnsatz(hamd, iterations=3)
    print(ansatz.circuit.to_text_diagram(transpose=True))

    nelectrons = 8  # TODO: CHECK WHICH ORBITALS ARE SAMPLED!!

    prep_circuit = cirq.Circuit(
        openfermioncirq.prepare_gaussian_state(
            ansatz.qubits, openfermion.QuadraticHamiltonian(
                hamd.one_body)))  # TODO: Verify this line
    objective = openfermioncirq.HamiltonianObjective(hamd)
    study = openfermioncirq.VariationalStudy(name='hamil',
                                             ansatz=ansatz,
                                             objective=objective,
                                             preparation_circuit=prep_circuit)

    print("The energy of the default initial guess is {}.".format(
        study.value_of(ansatz.default_initial_params())))
    print()

    alg = openfermioncirq.optimization.ScipyOptimizationAlgorithm(
        kwargs={'method': 'COBYLA'}, uses_bounds=False)
    opt_params = openfermioncirq.optimization.OptimizationParams(
        algorith=alg, initial_guess=ansat.default_initial_params())
    result = study.optimize(opt_param)

    print("Optimized energy: {}".format(result.optimal_value))
    print()
Esempio n. 22
0
 def _generate_qubits(self) -> Sequence[cirq.QubitId]:
     return cirq.LineQubit.range(openfermion.count_qubits(self.hamiltonian))
Esempio n. 23
0
 def _generate_qubits(self) -> Sequence[cirq.QubitId]:
     """Produce qubits that can be used by the ansatz circuit."""
     return cirq.LineQubit.range(openfermion.count_qubits(self.hamiltonian))
Esempio n. 24
0
def fermionic_swap_trotter_error_operator_diagonal_two_body(hamiltonian):
    """Compute the fermionic swap network Trotter error of a diagonal
    two-body Hamiltonian.

    Args:
        hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to
                                       compute the Trotter error for.

    Returns:
        error_operator: The second-order Trotter error operator.

    Notes:
        Follows Eq 9 of Poulin et al., arXiv:1406.4920, applied to the
        Trotter step detailed in Kivlichan et al., arxiv:1711.04789.
    """
    single_terms = numpy.array(
        simulation_ordered_grouped_low_depth_terms_with_info(hamiltonian)[0])

    # Cache the halved terms for use in the second commutator.
    halved_single_terms = single_terms / 2.0

    term_mode_mask = bit_mask_of_modes_acted_on_by_fermionic_terms(
        single_terms, count_qubits(hamiltonian))

    error_operator = FermionOperator.zero()

    for beta, term_beta in enumerate(single_terms):
        modes_acted_on_by_term_beta = set()
        for beta_action in term_beta.terms:
            modes_acted_on_by_term_beta.update(
                set(operator[0] for operator in beta_action))

        beta_mode_mask = numpy.logical_or.reduce(
            [term_mode_mask[mode] for mode in modes_acted_on_by_term_beta])

        # alpha_prime indices that could have a nonzero commutator, i.e.
        # there's overlap between the modes the corresponding terms act on.
        valid_alpha_primes = numpy.where(beta_mode_mask)[0]

        # Only alpha_prime < beta enters the error operator; filter for this.
        valid_alpha_primes = valid_alpha_primes[valid_alpha_primes < beta]

        for alpha_prime in valid_alpha_primes:
            term_alpha_prime = single_terms[alpha_prime]

            inner_commutator_term = (
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    term_beta, term_alpha_prime))

            modes_acted_on_by_inner_commutator = set()
            for inner_commutator_action in inner_commutator_term.terms:
                modes_acted_on_by_inner_commutator.update(
                    set(operator[0] for operator in inner_commutator_action))

            # If the inner commutator has no action, the commutator is zero.
            if not modes_acted_on_by_inner_commutator:
                continue

            inner_commutator_mask = numpy.logical_or.reduce([
                term_mode_mask[mode]
                for mode in modes_acted_on_by_inner_commutator
            ])

            # alpha indices that could have a nonzero commutator.
            valid_alphas = numpy.where(inner_commutator_mask)[0]
            # Filter so alpha <= beta in the double commutator.
            valid_alphas = valid_alphas[valid_alphas <= beta]

            for alpha in valid_alphas:
                # If alpha = beta, only use half the term.
                if alpha != beta:
                    outer_term_alpha = single_terms[alpha]
                else:
                    outer_term_alpha = halved_single_terms[alpha]

                # Add the partial double commutator to the error operator.
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    outer_term_alpha,
                    inner_commutator_term,
                    prior_terms=error_operator)

    # Divide by 12 to match the error operator definition.
    error_operator /= 12.0
    return error_operator
Esempio n. 25
0
 def _generate_qubits(self) -> Sequence[cirq.Qid]:
     """Specifies the number of required qubits based on the molecule hamiltonian"""
     qubit_count = of.count_qubits(self._get_hamiltonian())
     row = int(np.ceil(np.sqrt(qubit_count)))
     return [cirq.GridQubit(i, j) for i in range(row) for j in range(row)]
Esempio n. 26
0
def estimate_correlation(ham_qop,
                         state,
                         dt,
                         max_trotter_step,
                         outputter,
                         savefilename=None):
    const_term = ham_qop.terms[()]
    n_site = openfermion.count_qubits(ham_qop)
    outputter.n_qubit = n_site
    outputter.n_trott_step = max_trotter_step

    #circuit_time_evo = qulacs.QuantumCircuit(n_site)
    #trotter.trotter_step_2nd_order(circuit_time_evo, -1j * dt * ham_qop)
    trotterstep = trotter.TrotterStep(n_site, -1j * dt * ham_qop)
    circuit_time_evo = trotterstep._circuit
    outputter.ngate = trotterstep.count_gates()
    print(trotterstep)
    print(circuit_time_evo)

    ham_tensor = openfermion.get_sparse_operator(ham_qop)

    state_vec_exact = convert_state_vector(n_site, state.get_vector())

    steps = np.arange(max_trotter_step + 1)

    save_state_vec_exact = np.empty((len(steps), 2**n_site), np.complex128)
    save_state_vec_trotter = np.empty((len(steps), 2**n_site), np.complex128)
    save_time_energy_fidelity = np.empty((len(steps), 4), float)
    save_time_energy_fidelity[:, 0] = steps * dt

    time_sim = 0

    for istep, n_trotter_step in enumerate(steps):
        state_vec_trotter = convert_state_vector(n_site, state.get_vector())

        # calculate energy and fidelity
        energy_exact = openfermion.expectation(ham_tensor, state_vec_exact)
        energy_trotter = openfermion.expectation(ham_tensor, state_vec_trotter)
        fidelity = np.abs(
            np.dot(np.conjugate(state_vec_exact), state_vec_trotter))**2

        # save
        save_state_vec_exact[istep] = state_vec_exact
        save_state_vec_trotter[istep] = state_vec_trotter
        save_time_energy_fidelity[istep, 1] = energy_exact.real
        save_time_energy_fidelity[istep, 2] = energy_trotter.real
        save_time_energy_fidelity[istep, 3] = fidelity

        # time propergation
        time_bgn = time()
        circuit_time_evo.update_quantum_state(state)
        time_sim += time() - time_bgn
        state_vec_exact = scipy.sparse.linalg.expm_multiply(
            -1j * dt * ham_tensor, state_vec_exact)

    corr_exact = np.dot(save_state_vec_exact[0], save_state_vec_exact.T)
    corr_trotter = np.dot(
        save_state_vec_trotter[0],
        save_state_vec_trotter.T)  # * np.exp(-1j * dt * steps * const_term)

    outputter.time_sim = time_sim

    if savefilename:
        np.save(savefilename + '_ham_tensor', ham_tensor.todense())
        np.save(savefilename + '_corr_exact.npy', corr_exact)
        np.save(savefilename + '_corr_trotter.npy', corr_trotter)
        np.save(savefilename + '_state_vec_exact.npy', save_state_vec_exact)
        np.save(savefilename + '_state_vec_trotter.npy',
                save_state_vec_trotter)
        np.save(savefilename + '_time_energy_fidelity.npy',
                save_time_energy_fidelity)

    return EstimateCorrelationResult(corr_exact, corr_trotter,
                                     save_state_vec_exact,
                                     save_state_vec_trotter,
                                     save_time_energy_fidelity)
Esempio n. 27
0
def estimate_corr_new__(ham,
                        dt,
                        max_trotter_step,
                        outputter,
                        savefilename=None):
    n_site = openfermion.count_qubits(ham.ham_qop)
    #state = ham.state
    #state_vec_exact = convert_state_vector(n_site, state.get_vector())

    steps = np.arange(max_trotter_step + 1)

    save_state_vec_exact = np.empty((len(steps), 2**n_site), np.complex128)
    save_state_vec_trotter = np.empty((len(steps), 2**n_site), np.complex128)
    save_time_energy_fidelity = np.empty((len(steps), 4), float)
    save_time_energy_fidelity[:, 0] = steps * dt

    time_sim = 0

    ham.init_propagate(dt)
    ham_tensor = openfermion.get_sparse_operator(ham.ham_qop)

    for istep, n_trotter_step in enumerate(steps):
        state_vec_trotter = convert_state_vector(n_site,
                                                 ham.state.get_vector())

        # calculate energy and fidelity
        energy_exact = openfermion.expectation(ham_tensor, state_vec_exact)
        energy_trotter = openfermion.expectation(ham_tensor, state_vec_trotter)
        fidelity = np.abs(
            np.dot(np.conjugate(state_vec_exact), state_vec_trotter))**2

        # save
        save_state_vec_exact[istep] = state_vec_exact
        save_state_vec_trotter[istep] = state_vec_trotter
        save_time_energy_fidelity[istep, 1] = energy_exact.real
        save_time_energy_fidelity[istep, 2] = energy_trotter.real
        save_time_energy_fidelity[istep, 3] = fidelity

        # time propergation
        time_bgn = time()
        ham.propagate()
        time_sim += time() - time_bgn
        state_vec_exact = scipy.sparse.linalg.expm_multiply(
            -1j * dt * ham_tensor, state_vec_exact)

    corr_exact = np.dot(save_state_vec_exact[0], save_state_vec_exact.T)
    corr_trotter = np.dot(save_state_vec_trotter[0], save_state_vec_trotter.T)

    outputter.n_qubit = n_site
    outputter.n_trott_step = max_trotter_step
    outputter.ngate = ham.trotterstep.count_gates()
    outputter.time_sim = time_sim

    if savefilename:
        np.save(savefilename + '_ham_tensor', ham_tensor.todense())
        np.save(savefilename + '_corr_exact.npy', corr_exact)
        np.save(savefilename + '_corr_trotter.npy', corr_trotter)
        np.save(savefilename + '_state_vec_exact.npy', save_state_vec_exact)
        np.save(savefilename + '_state_vec_trotter.npy',
                save_state_vec_trotter)
        np.save(savefilename + '_time_energy_fidelity.npy',
                save_time_energy_fidelity)

    return EstimateCorrelationResult(corr_exact, corr_trotter,
                                     save_state_vec_exact,
                                     save_state_vec_trotter,
                                     save_time_energy_fidelity)
Esempio n. 28
0
    def kevin_lih_cirq(self):
        x_dim = 3
        y_dim = 2
        n_sites = x_dim * y_dim
        n_modes = 2 * n_sites

        # Create LiH Hamiltonian
        # ----------------------
        bond_length = 1.45
        geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., bond_length))]
        n_active_electrons = 4
        n_active_orbitals = 4
        lih_hamiltonian = of.load_molecular_hamiltonian(
            geometry, 'sto-3g', 1, format(bond_length), n_active_electrons,
            n_active_orbitals)

        # Generate qubits
        n_qubits = of.count_qubits(lih_hamiltonian)
        qubits = cirq.LineQubit.range(n_qubits)

        # State preparation circuit for eigenstate of one-body term
        # ---------------------------------------------------------
        # Set the pseudo-particle orbitals to fill
        occupied_orbitals = range(n_qubits // 2)

        # State preparation circuit for eigenstate of one-body term
        # ---------------------------------------------------------
        # Set the pseudo-particle orbitals to fill
        up_orbitals = range(n_sites // 2)
        down_orbitals = range(n_sites // 2)

        # Create the circuit
        lih_state_preparation_circuit = cirq.Circuit.from_ops(
            ofc.prepare_gaussian_state(
                qubits,
                of.QuadraticHamiltonian(lih_hamiltonian.one_body_tensor),
                occupied_orbitals=(up_orbitals, down_orbitals)),
            strategy=cirq.InsertStrategy.EARLIEST)

        # Trotter simulation circuit
        # --------------------------
        n_steps = 10
        order = 0

        lih_simulation_circuit = cirq.Circuit.from_ops(
            ofc.simulate_trotter(qubits,
                                 lih_hamiltonian,
                                 time=1.0,
                                 n_steps=n_steps,
                                 order=order,
                                 algorithm=ofc.trotter.LOW_RANK),
            strategy=cirq.InsertStrategy.EARLIEST)

        t0 = time.time()
        self.kevin_optimize_circuit(lih_state_preparation_circuit)
        self.kevin_optimize_circuit(lih_simulation_circuit)
        t1 = time.time()

        # print('Optimizing circuits took {} seconds'.format(t1 - t0))
        # print(lih_state_preparation_circuit.to_text_diagram(transpose=True))

        return lih_state_preparation_circuit, lih_simulation_circuit
Esempio n. 29
0
def split_operator_trotter_error_operator_diagonal_two_body(
        hamiltonian, order):
    """Compute the split-operator Trotter error of a diagonal two-body
    Hamiltonian.

    Args:
        hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to
                                       compute the Trotter error for.
        order (str): Whether to simulate the split-operator Trotter step
                     with the kinetic energy T first (order='T+V') or with
                     the potential energy V first (order='V+T').

    Returns:
        error_operator: The second-order Trotter error operator.

    Notes:
        The second-order split-operator Trotter error is calculated from the
        double commutator [T, [V, T]] + [V, [V, T]] / 2 when T is simulated
        before V (i.e. exp(-iTt/2) exp(-iVt) exp(-iTt/2)), and from the
        double commutator [V, [T, V]] + [T, [T, V]] / 2 when V is simulated
        before T, following Equation 9 of "The Trotter Step Size Required for
        Accurate Quantum Simulation of Quantum Chemistry" by Poulin et al.
        The Trotter error operator is then obtained by dividing by 12.
    """
    n_qubits = count_qubits(hamiltonian)

    potential_terms, kinetic_terms = (
        diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian))

    # Cache halved potential and kinetic terms for the second commutator.
    halved_potential_terms = potential_terms / 2.0
    halved_kinetic_terms = kinetic_terms / 2.0

    # Assign the outer term of the second commutator based on the ordering.
    outer_potential_terms = (halved_potential_terms
                             if order == 'T+V' else potential_terms)
    outer_kinetic_terms = (halved_kinetic_terms
                           if order == 'V+T' else kinetic_terms)

    potential_mask = bit_mask_of_modes_acted_on_by_fermionic_terms(
        potential_terms, n_qubits)
    kinetic_mask = bit_mask_of_modes_acted_on_by_fermionic_terms(
        kinetic_terms, n_qubits)

    error_operator = FermionOperator.zero()

    for potential_term in potential_terms:
        modes_acted_on_by_potential_term = set()

        for potential_term_action in potential_term.terms:
            modes_acted_on_by_potential_term.update(
                set(operator[0] for operator in potential_term_action))

        if not modes_acted_on_by_potential_term:
            continue

        potential_term_mode_mask = numpy.logical_or.reduce(
            [kinetic_mask[mode] for mode in modes_acted_on_by_potential_term])

        for kinetic_term in kinetic_terms[potential_term_mode_mask]:
            inner_commutator_term = (
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    potential_term, kinetic_term))

            modes_acted_on_by_inner_commutator = set()
            for inner_commutator_action in inner_commutator_term.terms:
                modes_acted_on_by_inner_commutator.update(
                    set(operator[0] for operator in inner_commutator_action))

            if not modes_acted_on_by_inner_commutator:
                continue

            inner_commutator_mode_mask = numpy.logical_or.reduce([
                potential_mask[mode]
                for mode in modes_acted_on_by_inner_commutator
            ])

            # halved_potential_terms for T+V order, potential_terms for V+T
            for outer_potential_term in outer_potential_terms[
                    inner_commutator_mode_mask]:
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    outer_potential_term,
                    inner_commutator_term,
                    prior_terms=error_operator)

            inner_commutator_mode_mask = numpy.logical_or.reduce([
                kinetic_mask[qubit]
                for qubit in modes_acted_on_by_inner_commutator
            ])

            # kinetic_terms for T+V order, halved_kinetic_terms for V+T
            for outer_kinetic_term in outer_kinetic_terms[
                    inner_commutator_mode_mask]:
                commutator_ordered_diagonal_coulomb_with_two_body_operator(
                    outer_kinetic_term,
                    inner_commutator_term,
                    prior_terms=error_operator)

    # Divide by 12 to match the error operator definition.
    # If order='V+T', also flip the sign to account for inner_commutator_term
    # not flipping between the different orderings.
    if order == 'T+V':
        error_operator /= 12.0
    else:
        error_operator /= -12.0

    return error_operator