Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e162f47
Implement reverse_map() method.
MarcoBarroca Feb 25, 2024
8d7486b
Created unit test for reverse_map() and added release note.
MarcoBarroca Feb 25, 2024
b7fe618
Implement reverse_map() method in jordan_wigner_mapper.py
MarcoBarroca Feb 25, 2024
3efd4ca
Update jordan_wigner_mapper.py to fix automated checks
MarcoBarroca Feb 27, 2024
0da3ec8
Add Pyscf check in test_reverse_map() for automated checks.
MarcoBarroca Feb 27, 2024
4c15ca7
Complex inside FermionicOP to fix mypy error
MarcoBarroca Feb 27, 2024
98c6451
Merge branch 'main' into main
MarcoBarroca Mar 1, 2024
9a48d53
Update releasenotes/notes/reverse-jordan-wigner-mapping-be0e0ab217967…
MarcoBarroca Mar 2, 2024
b41f04f
Update releasenotes/notes/reverse-jordan-wigner-mapping-be0e0ab217967…
MarcoBarroca Mar 2, 2024
bc52fb5
Update reverse-jordan-wigner-mapping-be0e0ab217967f61.yaml
MarcoBarroca Mar 2, 2024
657cc12
Update releasenotes/notes/reverse-jordan-wigner-mapping-be0e0ab217967…
MarcoBarroca Mar 2, 2024
fb3ad15
Make invert_pauli_terms() private.
MarcoBarroca Mar 2, 2024
aa53bfd
Add note to use reverse_map() ony on JW operators.
MarcoBarroca Mar 2, 2024
0ca421d
Changed wording on the note.
MarcoBarroca Mar 2, 2024
9a7223a
Update jordan_wigner_mapper.py
MarcoBarroca Mar 2, 2024
79a739a
FIx style formatting.
MarcoBarroca Mar 2, 2024
8bd7562
Update qiskit_nature/second_q/mappers/jordan_wigner_mapper.py
MarcoBarroca Mar 6, 2024
bfe70ea
Merge branch 'main' into main
MarcoBarroca Apr 12, 2024
904c889
Merge branch 'main' into main
MarcoBarroca May 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion qiskit_nature/second_q/mappers/jordan_wigner_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import numpy as np

from qiskit.quantum_info.operators import Pauli
from qiskit.quantum_info import SparsePauliOp

from qiskit_nature.second_q.operators import FermionicOp
from .fermionic_mapper import FermionicMapper
from .mode_based_mapper import ModeBasedMapper, PauliType

Expand All @@ -44,5 +46,65 @@ def _pauli_table(register_length: int) -> list[tuple[PauliType, PauliType]]:
# c_x = np.asarray([0] * register_length, dtype=bool)
pauli_table.append((Pauli((a_z, a_x)), Pauli((b_z, b_x))))
# TODO add Pauli 3-tuple to lookup table

return pauli_table

@classmethod
def reverse_map(cls, qubit_op: SparsePauliOp) -> FermionicOp:
"""Maps a qubit operator ``SparsePauliOp`` back into the second quantized
operator ``FermionicOp``. While it'll provide an output for any ``SparsePauliOp``
operator, it should be used on operators that were created with
``JordanWignerMapper`` to ensure a meaningful result.

Args:
qubit_op: The qubit operator ``SparsePauliOp`` to be mapped.

Returns:
The second quantized operator ``FermionicOp`` corresponding to
the Hamiltonian in the Fermionic space.
"""
num_qubits = (
qubit_op.num_qubits
) # get number of qubits from input second quantized operator
qubit_op = cls.__invert_pauli_terms(qubit_op)
total_fermionic_op = FermionicOp.zero()
for term in qubit_op:
coef_term = term.coeffs[0]
target_pauli_op = term.paulis[0]
ferm_term_ops = []
for i in range(num_qubits):
one_pauli = target_pauli_op[num_qubits - 1 - i]
pauli_char = one_pauli.to_label()
if pauli_char == "Z": # dealing Pauli Z op
ferm_op_pauli = FermionicOp({"": 1, f"+_{i} -_{i}": -2})
elif pauli_char == "X": # dealing Pauli X op
ferm_op_pauli = FermionicOp({f"+_{i}": 1, f"-_{i}": 1})
target_pauli_op = Pauli("I" * (i + 1) + "Z" * (num_qubits - i - 1)).compose(
target_pauli_op
)
elif one_pauli.to_label() == "Y": # dealing Pauli Y op
ferm_op_pauli = FermionicOp({f"+_{i}": -1j, f"-_{i}": 1j})
target_pauli_op = Pauli("I" * (i + 1) + "Z" * (num_qubits - i - 1)).compose(
target_pauli_op
)
else:
ferm_op_pauli = FermionicOp.one()
ferm_term_ops.append(ferm_op_pauli)
term_fermionic_op = FermionicOp.one()
for op in ferm_term_ops:
term_fermionic_op = term_fermionic_op @ op
if target_pauli_op.phase == 1:
coef_term *= -1j
elif target_pauli_op.phase == 2:
coef_term *= -1
elif target_pauli_op.phase == 3:
coef_term *= 1j
total_fermionic_op += coef_term * term_fermionic_op
return total_fermionic_op.normal_order()

@staticmethod
def __invert_pauli_terms(sparse_pauli_op: SparsePauliOp) -> SparsePauliOp:
"""Utility to invert the order of Pauli operators in each term of a SparsePauliOp."""
inverted_labels = [label[::-1] for label in sparse_pauli_op.paulis.to_labels()]
# Create a new SparsePauliOp with the inverted labels but same coefficients
inverted_sparse_pauli_op = SparsePauliOp(inverted_labels, sparse_pauli_op.coeffs)
return inverted_sparse_pauli_op
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
features:
- |
Adds a new method :meth:`.JordanWignerMapper.reverse_map` for :class:`.JordanWignerMapper` that does
a reverse mapping to create a fermionic operator from a qubit operator.
15 changes: 14 additions & 1 deletion test/second_q/mappers/test_jordan_wigner_mapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2021, 2023.
# (C) Copyright IBM 2021, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand Down Expand Up @@ -142,6 +142,19 @@ def test_mapping_overwrite_reg_len(self):
mapper = JordanWignerMapper()
self.assertEqual(mapper.map(op, register_length=3), mapper.map(expected))

@unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.")
def test_reverse_map(self):
"""Test reverse mapping from qubit operator back to fermionic operator."""
driver = PySCFDriver()
driver_result = driver.run()
fermionic_op, _ = driver_result.second_q_ops()
mapper = JordanWignerMapper()
qubit_op = mapper.map(fermionic_op)
recovered_fermionic_op = mapper.reverse_map(qubit_op)
fermionic_op = fermionic_op.normal_order()
recovered_fermionic_op = recovered_fermionic_op.normal_order()
self.assertTrue(fermionic_op.equiv(recovered_fermionic_op))


if __name__ == "__main__":
unittest.main()