The QubitConverter Migration Guide#
The QubitConverter
class has been deprecated as part of
version 0.6 of Qiskit Nature. Instead of wrapping this class around one of the available
QubitMapper
classes, the latter can now instead be used
directly. To support this, the mappers have been improved and now ship with more advanced
functionality out of the box.
To keep this guide simple, we will focus on examples on how to replace specific use cases of the
QubitConverter
. For more details on the mappers we suggest
you check out the tutorial on qubit mappers.
Setup#
For the examples in this guide, we will always be using the following
FermionicOp
:
from qiskit_nature.second_q.drivers import PySCFDriver
driver = PySCFDriver(atom="H 0 0 0; H 0 0 0.735")
problem = driver.run()
hamiltonian = problem.hamiltonian.second_q_op()
for label, coeff in sorted(hamiltonian.items()):
print(f"{coeff:+.8f} * '{label}'")
+0.33785508 * '+_0 +_0 -_0 -_0'
+0.09046560 * '+_0 +_0 -_1 -_1'
+0.09046560 * '+_0 +_1 -_0 -_1'
+0.33229087 * '+_0 +_1 -_1 -_0'
+0.33785508 * '+_0 +_2 -_2 -_0'
+0.09046560 * '+_0 +_2 -_3 -_1'
+0.09046560 * '+_0 +_3 -_2 -_1'
+0.33229087 * '+_0 +_3 -_3 -_0'
-1.25633907 * '+_0 -_0'
+0.33229087 * '+_1 +_0 -_0 -_1'
+0.09046560 * '+_1 +_0 -_1 -_0'
+0.09046560 * '+_1 +_1 -_0 -_0'
+0.34928686 * '+_1 +_1 -_1 -_1'
+0.33229087 * '+_1 +_2 -_2 -_1'
+0.09046560 * '+_1 +_2 -_3 -_0'
+0.09046560 * '+_1 +_3 -_2 -_0'
+0.34928686 * '+_1 +_3 -_3 -_1'
-0.47189601 * '+_1 -_1'
+0.33785508 * '+_2 +_0 -_0 -_2'
+0.09046560 * '+_2 +_0 -_1 -_3'
+0.09046560 * '+_2 +_1 -_0 -_3'
+0.33229087 * '+_2 +_1 -_1 -_2'
+0.33785508 * '+_2 +_2 -_2 -_2'
+0.09046560 * '+_2 +_2 -_3 -_3'
+0.09046560 * '+_2 +_3 -_2 -_3'
+0.33229087 * '+_2 +_3 -_3 -_2'
-1.25633907 * '+_2 -_2'
+0.33229087 * '+_3 +_0 -_0 -_3'
+0.09046560 * '+_3 +_0 -_1 -_2'
+0.09046560 * '+_3 +_1 -_0 -_2'
+0.34928686 * '+_3 +_1 -_1 -_3'
+0.33229087 * '+_3 +_2 -_2 -_3'
+0.09046560 * '+_3 +_2 -_3 -_2'
+0.09046560 * '+_3 +_3 -_2 -_2'
+0.34928686 * '+_3 +_3 -_3 -_3'
-0.47189601 * '+_3 -_3'
PauliSumOp vs. SparsePauliOp#
One more note: the qiskit.opflow
module is going to be deprecated in the future. In order to
transition from the PauliSumOp
class and its widespread use to the
qiskit.quantum_info
module and its SparsePauliOp
class, you can
now set the value of use_pauli_sum_op
to
False
. This will become the default in a later release.
To ensure that we can consistently rely on using the SparsePauliOp
in
the following parts of this guide, we are applying this setting here:
from qiskit_nature import settings
settings.use_pauli_sum_op = False
As a consequence of this upcoming deprecation, Qiskit Nature now fully supports the use
SparsePauliOp
instances in all places which previously allowed
PauliSumOp
objects. In order to leverage this, it is not
required to change the setting above. Thus, it is recommended that you switch to using
SparsePauliOp
.
For more in-depth information about the qiskit.opflow
deprecation please refer to its
migration guide.
Simplest cases#
In the simplest cases, all you did was pass a QubitMapper
object into the QubitConverter
. For example, somewhat like
this:
from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter
mapper = JordanWignerMapper()
converter = QubitConverter(mapper)
All you need to do in order to update your code, is stop doing that and simply pass the mapper
object from the example above into whichever place you were using it before.
If you were working directly with some SparseLabelOp
like
so:
qubit_op = converter.convert(hamiltonian)
for pauli, coeff in sorted(qubit_op.label_iter()):
print(f"{coeff.real:+.8f} * {pauli}")
-0.81054798 * IIII
+0.17218393 * IIIZ
-0.22575349 * IIZI
+0.12091263 * IIZZ
+0.17218393 * IZII
+0.16892754 * IZIZ
+0.16614543 * IZZI
+0.04523280 * XXXX
+0.04523280 * XXYY
+0.04523280 * YYXX
+0.04523280 * YYYY
-0.22575349 * ZIII
+0.16614543 * ZIIZ
+0.17464343 * ZIZI
+0.12091263 * ZZII
You should now directly use the mapper
again, but its method is called .map
:
qubit_op = mapper.map(hamiltonian)
for pauli, coeff in sorted(qubit_op.label_iter()):
print(f"{coeff.real:+.8f} * {pauli}")
-0.81054798 * IIII
+0.17218393 * IIIZ
-0.22575349 * IIZI
+0.12091263 * IIZZ
+0.17218393 * IZII
+0.16892754 * IZIZ
+0.16614543 * IZZI
+0.04523280 * XXXX
+0.04523280 * XXYY
+0.04523280 * YYXX
+0.04523280 * YYYY
-0.22575349 * ZIII
+0.16614543 * ZIIZ
+0.17464343 * ZIZI
+0.12091263 * ZZII
This is likely what you were doing when you were working with any of the following mappers:
The ParityMapper#
However, when you were using the ParityMapper
, you were
able to use the two_qubit_reduction=True
option of the
QubitConverter
. This ability, which has always been unique
to the ParityMapper
, is now directly built into said
mapper. So if you were doing something along these lines:
from qiskit_nature.second_q.mappers import ParityMapper
converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)
reduced_op = converter.convert(hamiltonian, num_particles=problem.num_particles)
for pauli, coeff in sorted(reduced_op.label_iter()):
print(f"{coeff.real:+.8f} * {pauli}")
-1.05237325 * II
+0.39793742 * IZ
+0.18093120 * XX
-0.39793742 * ZI
-0.01128010 * ZZ
The equivalent code now looks like the following:
mapper = ParityMapper(num_particles=problem.num_particles)
reduced_op = mapper.map(hamiltonian)
for pauli, coeff in sorted(reduced_op.label_iter()):
print(f"{coeff.real:+.8f} * {pauli}")
-1.05237325 * II
+0.39793742 * IZ
+0.18093120 * XX
-0.39793742 * ZI
-0.01128010 * ZZ
Z2Symmetries#
Finally, the QubitConverter
class also supported further
reduction of qubit resources by exploiting the Z2Symmetries
implemented in the qiskit.opflow
module. Although we did extend the deprecated class to also
support the updated implementation in the qiskit.quantum_info
module (which has the same
name: Z2Symmetries
), you should now use the new
TaperedQubitMapper
instead.
In the past, you would have enabled this like so:
mapper = JordanWignerMapper()
converter = QubitConverter(mapper, z2symmetry_reduction="auto")
which would then later use
symmetry_sector_locator()
to find the symmetry
sector of the Hilbert space in which the solution of your problem lies. This was only supported by
the ElectronicStructureProblem
. Below is a quick example:
tapered_op = converter.convert(
hamiltonian,
num_particles=problem.num_particles,
sector_locator=problem.symmetry_sector_locator,
)
for pauli, coeff in sorted(tapered_op.label_iter()):
print(f"{coeff.real:+.8f} * {pauli}")
-1.04109314 * I
+0.18093120 * X
-0.79587485 * Z
Now, all you need to do is the use the
get_tapered_mapper()
method and provide the
original mapper which you would like to wrap:
tapered_mapper = problem.get_tapered_mapper(mapper)
tapered_op = tapered_mapper.map(hamiltonian)
for pauli, coeff in sorted(tapered_op.label_iter()):
print(f"{coeff.real:+.8f} * {pauli}")
-1.04109314 * I
+0.18093120 * X
-0.79587485 * Z
If you were not using the automatic symmetry detection but instead provided a custom symmetry
sector, you can construct your TaperedQubitMapper
instance
directly. Be sure to check out its documentation for more details.