Esempio n. 1
0
def _jordan_wigner_interaction_op(iop, n_qubits=None):
    """Output InteractionOperator as QubitOperator class under JW transform.

    One could accomplish this very easily by first mapping to fermions and
    then mapping to qubits. We skip the middle step for the sake of speed.

    This only works for real InteractionOperators (no complex numbers).

    Returns:
        qubit_operator: An instance of the QubitOperator class.
    """
    if n_qubits is None:
        n_qubits = count_qubits(iop)
    if n_qubits < count_qubits(iop):
        raise ValueError('Invalid number of qubits specified.')

    # Initialize qubit operator as constant.
    qubit_operator = QubitOperator((), iop.constant)

    # Transform diagonal one-body terms
    for p in range(n_qubits):
        coefficient = iop[(p, 1), (p, 0)]
        qubit_operator += jordan_wigner_one_body(p, p, coefficient)

    # Transform other one-body terms and "diagonal" two-body terms
    for p, q in itertools.combinations(range(n_qubits), 2):
        # One-body
        coefficient = .5 * (iop[(p, 1), (q, 0)] + iop[(q, 1),
                                                      (p, 0)].conjugate())
        qubit_operator += jordan_wigner_one_body(p, q, coefficient)

        # Two-body
        coefficient = (iop[(p, 1), (q, 1), (p, 0),
                           (q, 0)] - iop[(p, 1), (q, 1), (q, 0),
                                         (p, 0)] - iop[(q, 1), (p, 1), (p, 0),
                                                       (q, 0)] + iop[(q, 1),
                                                                     (p, 1),
                                                                     (q, 0),
                                                                     (p, 0)])
        qubit_operator += jordan_wigner_two_body(p, q, p, q, coefficient)

    # Transform the rest of the two-body terms
    for (p, q), (r, s) in itertools.combinations(
            itertools.combinations(range(n_qubits), 2), 2):
        coefficient = 0.5 * (iop[(p, 1), (q, 1), (r, 0),
                                 (s, 0)] + iop[(s, 1), (r, 1), (q, 0),
                                               (p, 0)].conjugate() -
                             iop[(p, 1), (q, 1), (s, 0),
                                 (r, 0)] - iop[(r, 1), (s, 1), (q, 0),
                                               (p, 0)].conjugate() -
                             iop[(q, 1), (p, 1), (r, 0),
                                 (s, 0)] - iop[(s, 1), (r, 1), (p, 0),
                                               (q, 0)].conjugate() +
                             iop[(q, 1), (p, 1), (s, 0),
                                 (r, 0)] + iop[(r, 1), (s, 1), (p, 0),
                                               (q, 0)].conjugate())
        qubit_operator += jordan_wigner_two_body(p, q, r, s, coefficient)

    return qubit_operator
Esempio n. 2
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)
def qubit_operator_to_pauli_sum(
        operator: QubitOperator,
        qubits: Optional[Sequence[cirq.Qid]] = None) -> cirq.PauliSum:
    """
    Convert QubitOperator to a sum of PauliString.

    Args:
        operator (QubitOperator): operator to convert.
        qubits (List): Optional list of qubit names.
            If `None` a list of `cirq.LineQubit` of length number of qubits
            in operator is created.

    Returns:
        pauli_sum (PauliSum): cirq PauliSum object.

    Raises:
        TypeError: if qubit_op is not a QubitOpertor.
    """
    if not isinstance(operator, QubitOperator):
        raise TypeError('Input must be a QubitOperator.')

    if qubits is None:
        qubits = cirq.LineQubit.range(count_qubits(operator))

    pauli_sum = cirq.PauliSum()
    for pauli in operator.terms.items():
        pauli_sum += _qubit_operator_term_to_pauli_string(pauli, qubits)

    return pauli_sum
Esempio n. 4
0
def _jordan_wigner_diagonal_coulomb_hamiltonian(operator):
    n_qubits = count_qubits(operator)
    qubit_operator = QubitOperator((), operator.constant)

    # Transform diagonal one-body terms
    for p in range(n_qubits):
        coefficient = operator.one_body[p, p] + operator.two_body[p, p]
        qubit_operator += QubitOperator(((p, 'Z'),), -.5 * coefficient)
        qubit_operator += QubitOperator((), .5 * coefficient)

    # Transform other one-body terms and two-body terms
    for p, q in itertools.combinations(range(n_qubits), 2):
        # One-body
        real_part = numpy.real(operator.one_body[p, q])
        imag_part = numpy.imag(operator.one_body[p, q])
        parity_string = [(i, 'Z') for i in range(p + 1, q)]
        qubit_operator += QubitOperator([(p, 'X')] + parity_string + [(q, 'X')],
                                        .5 * real_part)
        qubit_operator += QubitOperator([(p, 'Y')] + parity_string + [(q, 'Y')],
                                        .5 * real_part)
        qubit_operator += QubitOperator([(p, 'Y')] + parity_string + [(q, 'X')],
                                        .5 * imag_part)
        qubit_operator += QubitOperator([(p, 'X')] + parity_string + [(q, 'Y')],
                                        -.5 * imag_part)

        # Two-body
        coefficient = operator.two_body[p, q]
        qubit_operator += QubitOperator(((p, 'Z'), (q, 'Z')), .5 * coefficient)
        qubit_operator += QubitOperator((p, 'Z'), -.5 * coefficient)
        qubit_operator += QubitOperator((q, 'Z'), -.5 * coefficient)
        qubit_operator += QubitOperator((), .5 * coefficient)

    return qubit_operator
Esempio n. 5
0
def get_interaction_rdm(qubit_operator, n_qubits=None):
    """Build an InteractionRDM from measured qubit operators.

    Returns: An InteractionRDM object.
    """

    check_no_sympy(qubit_operator)

    # Avoid circular import.
    from openfermion.transforms import jordan_wigner
    if n_qubits is None:
        n_qubits = count_qubits(qubit_operator)
    one_rdm = numpy.zeros((n_qubits, ) * 2, dtype=complex)
    two_rdm = numpy.zeros((n_qubits, ) * 4, dtype=complex)

    # One-RDM.
    for i, j in itertools.product(range(n_qubits), repeat=2):
        transformed_operator = jordan_wigner(FermionOperator(((i, 1), (j, 0))))
        for term, coefficient in transformed_operator.terms.items():
            if term in qubit_operator.terms:
                one_rdm[i, j] += coefficient * qubit_operator.terms[term]

    # Two-RDM.
    for i, j, k, l in itertools.product(range(n_qubits), repeat=4):
        transformed_operator = jordan_wigner(
            FermionOperator(((i, 1), (j, 1), (k, 0), (l, 0))))
        for term, coefficient in transformed_operator.terms.items():
            if term in qubit_operator.terms:
                two_rdm[i, j, k, l] += coefficient * qubit_operator.terms[term]

    return InteractionRDM(one_rdm, two_rdm)
    def test_qasm_string_Controlled_XYZ(self):
        # Testing for correct QASM string output w/ Pauli-{X,Y,Z}
        # QubitOperator('X0 Z1 Y3', 0.5) and a controlled ancilla

        # Number of qubits
        qasmstr = str(count_qubits(self.opA) + 1) + "\n"

        # Write each QASM operation
        qasmstr += "\n".join(
            trotterize_exp_qubop_to_qasm(self.opA, ancilla='ancilla'))

        # Correct string
        strcorrect = '''5
H 0
Rx 1.5707963267948966 3
CNOT 0 1
CNOT 1 3
C-Phase -1.0 ancilla 3
Rz 0.5 ancilla
CNOT 1 3
CNOT 0 1
H 0
Rx -1.5707963267948966 3'''

        self.assertEqual(qasmstr, strcorrect)
    def test_qasm_string_Controlled_XYZ_ancilla_list(self):
        # Testing for correct QASM string output w/ Pauli-{X,Y,Z}
        # QubitOperator('X0 Z1 Y3', 0.5) and a controlled ancilla

        # Number of qubits
        qasmstr = str(count_qubits(self.opA) + 1) + "\n"

        qubit_list = ['q0', 'q1', 'q2', 'q3']

        # Write each QASM operation
        qasmstr += "\n".join(
            trotterize_exp_qubop_to_qasm(self.opA,
                                         ancilla='ancilla',
                                         qubit_list=qubit_list))

        # Correct string
        strcorrect = '''5
H q0
Rx 1.5707963267948966 q3
CNOT q0 q1
CNOT q1 q3
C-Phase -1.0 ancilla q3
Rz 0.5 ancilla
CNOT q1 q3
CNOT q0 q1
H q0
Rx -1.5707963267948966 q3'''

        self.assertEqual(qasmstr, strcorrect)
    def test_qasm_string_identity(self):
        qasmstr = str(count_qubits(self.op_id) + 1) + "\n"

        # Write each QASM operation
        qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.op_id))

        strcorrect = '''1\n'''

        self.assertEqual(qasmstr, strcorrect)
Esempio n. 9
0
def _diagonal_coulomb_hamiltonian_to_fermion_operator(operator):
    fermion_operator = FermionOperator()
    n_qubits = op_utils.count_qubits(operator)
    fermion_operator += FermionOperator((), operator.constant)
    for p, q in itertools.product(range(n_qubits), repeat=2):
        fermion_operator += FermionOperator(((p, 1), (q, 0)),
                                            operator.one_body[p, q])
        fermion_operator += FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)),
                                            operator.two_body[p, q])
    return fermion_operator
Esempio n. 10
0
    def test_case_one_body_op_success(self):
        # Case A: Simplest class of operators
        # (Number operators and Excitation operators)
        for i in range(self.test_range):
            for j in range(i):
                ham = self.two_op(i, j) + self.two_op(j, i)
                n_qubits = count_qubits(ham)

                opf_ham = bravyi_kitaev(ham, n_qubits)
                custom = bravyi_kitaev(get_interaction_operator(ham))

                assert custom == opf_ham
Esempio n. 11
0
def _bravyi_kitaev_fermion_operator(operator, n_qubits):
    # Compute the number of qubits.
    N = count_qubits(operator)
    if n_qubits is None:
        n_qubits = N
    if n_qubits < N:
        raise ValueError('Invalid number of qubits specified.')

    # Compute transformed operator.
    transformed_terms = (_transform_operator_term(
        term=term, coefficient=operator.terms[term], n_qubits=n_qubits)
                         for term in operator.terms)
    return inline_sum(summands=transformed_terms, seed=QubitOperator())
    def test_sparse_numerically(self):

        # Check FCI energy.
        energy, wavefunction = get_ground_state(self.hamiltonian_matrix)
        self.assertAlmostEqual(energy, self.molecule.fci_energy)
        expected_energy = expectation(self.hamiltonian_matrix, wavefunction)
        self.assertAlmostEqual(expected_energy, energy)

        # Make sure you can reproduce Hartree energy.
        hf_state = jw_hartree_fock_state(self.molecule.n_electrons,
                                         count_qubits(self.qubit_hamiltonian))
        hf_density = get_density_matrix([hf_state], [1.])

        # Make sure you can reproduce Hartree-Fock energy.
        hf_state = jw_hartree_fock_state(self.molecule.n_electrons,
                                         count_qubits(self.qubit_hamiltonian))
        hf_density = get_density_matrix([hf_state], [1.])
        expected_hf_density_energy = expectation(self.hamiltonian_matrix,
                                                 hf_density)
        expected_hf_energy = expectation(self.hamiltonian_matrix, hf_state)
        self.assertAlmostEqual(expected_hf_energy, self.molecule.hf_energy)
        self.assertAlmostEqual(expected_hf_density_energy,
                               self.molecule.hf_energy)
Esempio n. 13
0
def _bravyi_kitaev_majorana_operator(operator, n_qubits):
    # Compute the number of qubits.
    N = op_utils.count_qubits(operator)
    if n_qubits is None:
        n_qubits = N
    if n_qubits < N:
        raise ValueError('Invalid number of qubits specified.')

    # Compute transformed operator.
    transformed_terms = (_transform_majorana_term(term=term,
                                                  coefficient=coeff,
                                                  n_qubits=n_qubits)
                         for term, coeff in operator.terms.items())
    return op_utils.inline_sum(summands=transformed_terms,
                               seed=QubitOperator())
    def test_qasm_string_Z(self):
        # Testing for correct QASM string output w/ Pauli-X

        # Number of qubits
        qasmstr = str(count_qubits(self.opB)) + "\n"

        # Write each QASM operation
        qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.opB))

        # Correct string
        strcorrect = '''5
CNOT 3 4
Rz 0.6 4
CNOT 3 4'''

        self.assertEqual(qasmstr, strcorrect)
Esempio n. 15
0
    def test_double_excitation_op_success(self):
        # Case D: Double-excitation operator
        for i in range(self.test_range):
            for j in range(self.test_range):
                for k in range(self.test_range):
                    for l in range(self.test_range):
                        if len({i, j, k, l}) == 4:
                            print(i, j, k, l)
                            ham = self.four_op(i, j, k, l) + self.four_op(
                                k, l, i, j)
                            n_qubits = count_qubits(ham)

                            opf_ham = bravyi_kitaev(ham, n_qubits)
                            custom = bravyi_kitaev(
                                get_interaction_operator(ham))

                            assert custom == opf_ham
    def test_qasm_string_multiple_operator(self):
        # Testing for correct QASM string output
        # w/ sum of Pauli-{X,Y,Z} and Pauli-{Z}
        # QubitOperator('X0 Z1 Y3', 0.5) + QubitOperator('Z3 Z4', 0.6)

        # Number of qubits
        qasmstr = str(count_qubits(self.qo1)) + "\n"

        # Write each QASM operation
        qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.qo1))

        # Two possibilities for the correct string depending on
        # the order in which the operators loop.
        strcorrect1 = '''5
CNOT 3 4
Rz 0.6 4
CNOT 3 4
H 0
Rx 1.5707963267948966 3
CNOT 0 1
CNOT 1 3
Rz 0.5 3
CNOT 1 3
CNOT 0 1
H 0
Rx -1.5707963267948966 3'''

        strcorrect2 = '''5
H 0
Rx 1.5707963267948966 3
CNOT 0 1
CNOT 1 3
Rz 0.5 3
CNOT 1 3
CNOT 0 1
H 0
Rx -1.5707963267948966 3
CNOT 3 4
Rz 0.6 4
CNOT 3 4'''
        try:
            self.assertEqual(qasmstr, strcorrect1)
        except:
            self.assertEqual(qasmstr, strcorrect2)
    def __init__(self, qubit_operator, n_qubits=None):
        """
        Args:
            qubit_operator(QubitOperator): A qubit operator to be applied on
                vectors.
            n_qubits(int): The total number of qubits
        """
        calculated_n_qubits = op_utils.count_qubits(qubit_operator)
        if n_qubits is None:
            n_qubits = calculated_n_qubits
        elif n_qubits < calculated_n_qubits:
            raise ValueError('Invalid number of qubits specified '
                             '{} < {}.'.format(n_qubits, calculated_n_qubits))

        n_hilbert = 2**n_qubits
        super(LinearQubitOperator, self).__init__(shape=(n_hilbert, n_hilbert),
                                                  dtype=complex)
        self.qubit_operator = qubit_operator
        self.n_qubits = n_qubits
    def test_qasm_string_XYZ(self):
        # Testing for correct QASM string output w/ Pauli-{X,Y,Z}
        # QubitOperator('X0 Z1 Y3', 0.5)

        # Number of qubits
        qasmstr = str(count_qubits(self.opA)) + "\n"

        # Write each QASM operation
        qasmstr += "\n".join(trotterize_exp_qubop_to_qasm(self.opA))

        # Correct string
        strcorrect = '''4
H 0
Rx 1.5707963267948966 3
CNOT 0 1
CNOT 1 3
Rz 0.5 3
CNOT 1 3
CNOT 0 1
H 0
Rx -1.5707963267948966 3'''

        self.assertEqual(qasmstr, strcorrect)
    def __init__(self, qubit_operator, n_qubits=None, options=None):
        """
        Args:
            qubit_operator(QubitOperator): A qubit operator to be applied on
                vectors.
            n_qubits(int): The total number of qubits
            options(LinearQubitOperatorOptions): Options for the LinearOperator.
        """
        n_qubits = n_qubits or op_utils.count_qubits(qubit_operator)
        n_hilbert = 2**n_qubits
        super(ParallelLinearQubitOperator,
              self).__init__(shape=(n_hilbert, n_hilbert), dtype=complex)

        self.qubit_operator = qubit_operator
        self.n_qubits = n_qubits
        self.options = options or LinearQubitOperatorOptions()

        self.qubit_operator_groups = list(
            qubit_operator.get_operator_groups(self.options.processes))
        self.linear_operators = [
            LinearQubitOperator(operator, n_qubits)
            for operator in self.qubit_operator_groups
        ]
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
    def test_tappering_stabilizer_more_qubits(self):
        """Test for stabilizer with more qubits than operator."""
        hamiltonian = QubitOperator('Y0 Y1', 1.0)
        stab = QubitOperator('X0 X1 X2', -1.0)

        num_qubits = max(count_qubits(hamiltonian), count_qubits(stab))
        tap_ham = taper_off_qubits(hamiltonian, stab)
        num_qubits_tap = count_qubits(tap_ham)

        self.assertFalse(num_qubits == num_qubits_tap)

        hamiltonian = QubitOperator('X0 X1', 1.0)
        stab = QubitOperator('Y0 Y1 Y2', -1.0)

        num_qubits = max(count_qubits(hamiltonian), count_qubits(stab))
        tap_ham = taper_off_qubits(hamiltonian, stab)
        num_qubits_tap = count_qubits(tap_ham)
        self.assertFalse(num_qubits == num_qubits_tap)
Esempio n. 22
0
 def test_n_qubits_interaction_operator(self):
     self.assertEqual(self.n_qubits,
                      count_qubits(self.interaction_operator))
Esempio n. 23
0
 def test_n_qubits_qubit_operator(self):
     self.assertEqual(self.n_qubits, count_qubits(self.qubit_operator))
Esempio n. 24
0
 def test_n_qubits_fermion_operator(self):
     self.assertEqual(self.n_qubits, count_qubits(self.fermion_operator))
Esempio n. 25
0
 def test_n_qubits_single_fermion_term(self):
     self.assertEqual(self.n_qubits, count_qubits(self.fermion_term))
Esempio n. 26
0
 def test_n_qubits_majorana_operator(self):
     self.assertEqual(self.n_qubits, count_qubits(self.majorana_operator))
def taper_off_qubits(operator,
                     stabilizers,
                     manual_input=False,
                     fixed_positions=None,
                     output_tapered_positions=False):
    r"""
    Remove qubits from given operator.

    Qubits are removed by eliminating an equivalent number of
    stabilizer conditions. Which qubits that are can either be determined
    automatically or their positions can be set manually.

    Qubits can be disregarded from the Hamiltonian when the effect of all its
    terms on them is rendered trivial. This algorithm employs a stabilizers
    like :math:`\pm X \otimes p` to fix the action of every Pauli
    string on the first qubit to :math:`Z` or the identity. A string
    :math:`X \otimes h` would for instance be multiplied with the stabilizer
    to obtain :math:`1 \otimes (\pm h\cdot p)` while a string
    :math:`Z \otimes h^\prime` would pass without correction. The first
    qubit can subsequently be removed as it must be in the computational basis
    in Hamiltonian eigenstates.
    For stabilizers acting as :math:`Y` (:math:`Z`) on selected qubits,
    the algorithm would fix the action of every Hamiltonian string to
    :math:`Z` (:math:`X`). Updating also the list of remaining stabilizer
    generators, the algorithm is run iteratively.

    Args:
        operator (QubitOperator): Operator of which qubits will be removed.
        stabilizers (QubitOperator): Stabilizer generators for the tapering.
                                     Can also be passed as a list of
                                     QubitOperator.
        manual_input (Boolean): Option to pass the list of fixed qubits
                                positions manually. Set to False by default.
        fixed_positions (list): (optional) List of fixed qubit positions.
                                Passing a list is only effective if
                                manual_input is True.
        output_tapered_positions (Boolean): Option to output the positions of
                                            qubits that have been removed.
    Returns:
        skimmed_operator (QubitOperator): Operator with fewer qubits.
        removed_positions (list): (optional) List of removed qubit positions.
                                  For the qubits to be gone in the qubit count,
                                  the remaining qubits have been moved up to
                                  those indices.
    """
    if isinstance(stabilizers, (list, tuple, numpy.ndarray)):
        n_qbits_stabs = 0
        for ent in stabilizers:
            if op_utils.count_qubits(ent) > n_qbits_stabs:
                n_qbits_stabs = op_utils.count_qubits(ent)
    else:
        n_qbits_stabs = op_utils.count_qubits(stabilizers)

    n_qbits = max(op_utils.count_qubits(operator), n_qbits_stabs)

    (ham_to_update,
     qbts_to_rm) = reduce_number_of_terms(operator,
                                          stabilizers,
                                          maintain_length=False,
                                          manual_input=manual_input,
                                          fixed_positions=fixed_positions,
                                          output_fixed_positions=True)

    # Gets a list of the order of the qubits after tapering
    qbit_order = list(numpy.arange(n_qbits - len(qbts_to_rm), dtype=int))
    # Save the original list before it gets ordered
    removed_positions = qbts_to_rm
    qbts_to_rm.sort()
    for x in qbts_to_rm:
        qbit_order.insert(x, 'remove')

    # Remove the qubits
    skimmed_operator = QubitOperator()
    for term, coef in ham_to_update.terms.items():
        if term == ():
            skimmed_operator += QubitOperator('', coef)
            continue
        tap_tpls = []
        for p in term:
            if qbit_order[p[0]] != 'remove':
                tap_tpls.append((qbit_order[p[0]].item(), p[1]))

        skimmed_operator += QubitOperator(tuple(tap_tpls), coef)

    if output_tapered_positions:
        return skimmed_operator, removed_positions
    else:
        return skimmed_operator
Esempio n. 28
0
 def test_n_qubits_bad_type(self):
     with self.assertRaises(TypeError):
         count_qubits('twelve')
Esempio n. 29
0
    def test_all(self):

        # Test reverse Jordan-Wigner.
        fermion_hamiltonian = reverse_jordan_wigner(self.qubit_hamiltonian)
        fermion_hamiltonian = normal_ordered(fermion_hamiltonian)
        self.assertTrue(self.fermion_hamiltonian == fermion_hamiltonian)

        # Test mapping to interaction operator.
        fermion_hamiltonian = get_fermion_operator(self.molecular_hamiltonian)
        fermion_hamiltonian = normal_ordered(fermion_hamiltonian)
        self.assertTrue(self.fermion_hamiltonian == fermion_hamiltonian)

        # Test RDM energy.
        fci_rdm_energy = self.nuclear_repulsion
        fci_rdm_energy += numpy.sum(self.fci_rdm.one_body_tensor *
                                    self.one_body)
        fci_rdm_energy += numpy.sum(self.fci_rdm.two_body_tensor *
                                    self.two_body)
        self.assertAlmostEqual(fci_rdm_energy, self.molecule.fci_energy)

        # Confirm expectation on qubit Hamiltonian using reverse JW matches.
        qubit_rdm = self.fci_rdm.get_qubit_expectations(self.qubit_hamiltonian)
        qubit_energy = 0.0
        for term, coefficient in qubit_rdm.terms.items():
            qubit_energy += coefficient * self.qubit_hamiltonian.terms[term]
        self.assertAlmostEqual(qubit_energy, self.molecule.fci_energy)

        # Confirm fermionic RDMs can be built from measured qubit RDMs.
        new_fermi_rdm = get_interaction_rdm(qubit_rdm)
        new_fermi_rdm.expectation(self.molecular_hamiltonian)
        self.assertAlmostEqual(fci_rdm_energy, self.molecule.fci_energy)

        # Test sparse matrices.
        energy, wavefunction = get_ground_state(self.hamiltonian_matrix)
        self.assertAlmostEqual(energy, self.molecule.fci_energy)
        expected_energy = expectation(self.hamiltonian_matrix, wavefunction)
        self.assertAlmostEqual(expected_energy, energy)

        # Make sure you can reproduce Hartree-Fock energy.
        hf_state = jw_hartree_fock_state(self.molecule.n_electrons,
                                         count_qubits(self.qubit_hamiltonian))
        hf_density = get_density_matrix([hf_state], [1.])
        expected_hf_density_energy = expectation(self.hamiltonian_matrix,
                                                 hf_density)
        expected_hf_energy = expectation(self.hamiltonian_matrix, hf_state)
        self.assertAlmostEqual(expected_hf_energy, self.molecule.hf_energy)
        self.assertAlmostEqual(expected_hf_density_energy,
                               self.molecule.hf_energy)

        # Check that frozen core result matches frozen core FCI from psi4.
        # Recore frozen core result from external calculation.
        self.frozen_core_fci_energy = -7.8807607374168
        no_core_fci_energy = numpy.linalg.eigh(
            self.hamiltonian_matrix_no_core.toarray())[0][0]
        self.assertAlmostEqual(no_core_fci_energy, self.frozen_core_fci_energy)

        # Check that the freeze_orbitals function has the same effect as the
        # as the occupied_indices option of get_molecular_hamiltonian.
        frozen_hamiltonian = freeze_orbitals(
            get_fermion_operator(self.molecular_hamiltonian), [0, 1])
        self.assertTrue(frozen_hamiltonian == get_fermion_operator(
            self.molecular_hamiltonian_no_core))
Esempio n. 30
0
def bravyi_kitaev_fast_interaction_op(iop):
    r"""
    Transform from InteractionOperator to QubitOperator for Bravyi-Kitaev fast
    algorithm.

    The electronic Hamiltonian is represented in terms of creation and
    annihilation operators. These creation and annihilation operators could be
    used to define Majorana modes as follows:
        c_{2i} = a_i + a^{\dagger}_i,
        c_{2i+1} = (a_i - a^{\dagger}_{i})/(1j)
    These Majorana modes can be used to define edge operators B_i and A_{ij}:
        B_i=c_{2i}c_{2i+1},
        A_{ij}=c_{2i}c_{2j}
    using these edge operators the fermionic algebra can be generated and
    hence all the terms in the electronic Hamiltonian can be expressed in
    terms of edge operators. The terms in electronic Hamiltonian can be
    divided into five types (arXiv 1208.5986). We can find the edge operator
    expression for each of those five types. For example, the excitation
    operator term in Hamiltonian when represented in terms of edge operators
    becomes:
        a_i^{\dagger}a_j+a_j^{\dagger}a_i = (-1j/2)*(A_ij*B_i+B_j*A_ij)
    For the sake of brevity the reader is encouraged to look up the
    expressions of other terms from the code below. The variables for edge
    operators are chosen according to the nomenclature defined above
    (B_i and A_ij). A detailed description of these operators and the terms
    of the electronic Hamiltonian are provided in (arXiv 1712.00446).

    Args:
        iop (Interaction Operator):
        n_qubit (int): Number of qubits

    Returns:
        qubit_operator: An instance of the QubitOperator class.
    """
    n_qubits = count_qubits(iop)

    # Initialize qubit operator as constant.
    qubit_operator = QubitOperator((), iop.constant)
    edge_matrix = bravyi_kitaev_fast_edge_matrix(iop)
    edge_matrix_indices = numpy.array(
        numpy.nonzero(
            numpy.triu(edge_matrix) - numpy.diag(numpy.diag(edge_matrix))))
    # Loop through all indices.
    for p in range(n_qubits):
        for q in range(n_qubits):
            # Handle one-body terms.
            coefficient = complex(iop[(p, 1), (q, 0)])
            if coefficient and p >= q:
                qubit_operator += (coefficient *
                                   one_body(edge_matrix_indices, p, q))

            # Keep looping for the two-body terms.
            for r in range(n_qubits):
                for s in range(n_qubits):
                    coefficient = complex(iop[(p, 1), (q, 1), (r, 0), (s, 0)])

                    # Skip zero terms.
                    if (not coefficient) or (p == q) or (r == s):
                        # coverage: ignore
                        continue

                    # Identify and skip one of the complex conjugates.
                    if [p, q, r, s] != [s, r, q, p]:
                        if len(set([p, q, r, s])) == 4:
                            if min(r, s) < min(p, q):
                                continue
                        # Handle case of 3 unique indices
                        elif len(set([p, q, r, s])) == 3:
                            transformed_term = two_body(edge_matrix_indices, p,
                                                        q, r, s)
                            transformed_term *= .5 * coefficient
                            qubit_operator += transformed_term
                            continue
                        elif p != r and q < p:
                            # TODO: remove pragma if reachable continue
                            continue  # pragma: no cover

                    # Handle the two-body terms.
                    transformed_term = two_body(edge_matrix_indices, p, q, r, s)
                    transformed_term *= coefficient
                    qubit_operator += transformed_term
    return qubit_operator