-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implements a reverse_map() method for JordanWignerMapper(). #1344
base: main
Are you sure you want to change the base?
Changes from 7 commits
e162f47
8d7486b
b7fe618
3efd4ca
0da3ec8
4c15ca7
98c6451
9a48d53
b41f04f
bc52fb5
657cc12
fb3ad15
aa53bfd
0ca421d
9a7223a
79a739a
8bd7562
bfe70ea
904c889
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
@@ -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 | ||
|
||
|
||
|
@@ -41,5 +43,63 @@ def pauli_table(cls, register_length: int) -> list[tuple[Pauli, Pauli]]: | |
# 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``. | ||
|
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am thinking this should be private method - ie something we do not want as part of supported public API. (If it was it would havte to have full docstring stating args, return etc. I think this is more intended as some private utility right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now I think I’m the only one asking for this. I don’t see why we shouldn’t have this on the public API though. If Open Fermion can do this and thinks it’s relevant why shouldn’t Qiskit Nature? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are talking just about the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! my bad. Sure, I can turn that into a private method. It's just a utility for the reverse_map(). |
||
"""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,4 @@ | ||
|
||
MarcoBarroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||
features: | ||
- | ||
MarcoBarroca marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Created a new method reverse_map() for JordanWignerMapper() that allows users to recover the fermionic operator from a qubit operator. | ||
MarcoBarroca marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a question around this. This seems to take qubit operator and maps it back regardless of what it really is. I get if I this is given a JW mapped Fermionic Op as a qubit operator it will reverse that. What happens to some other operator - does it always work and produce something even if its meaningless? Is there any check possible? Should we add any note here about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will work for any operator that is a SparsePauliOp and write that in terms of fermionic operators. But if a user tries to reverse an operator that was mapped with a different mapper(I.e. ParityMapper()) they will just get different ones.
It can also take any qubit operator and turn into a fermionic Op even if it doesn’t make sense, so it will always give a result.
Maybe a note making it clear that this works for JW? I wouldn’t know how to check if an qubit operator was transformed via Jordan Wigner or something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A note sounds like the best way then - that says it users responsibility to provide an operator that was mapped with JW to get a meaningful outcome as this does the reverse mapping for any operator on the given assumption it was done with a JW mapping in the first place - or however you want to phrase it,.