Skip to content

Commit 59d910e

Browse files
1ucian0Cryoris
andauthored
New atomic_evolution signature in ProductFormula subclasses (#13918)
* new atomic_evolution signature in ProductFormula subclasses * Add LieTrotter to reno --------- Co-authored-by: Julien Gacon <[email protected]>
1 parent fcb5d29 commit 59d910e

File tree

6 files changed

+25
-89
lines changed

6 files changed

+25
-89
lines changed

qiskit/synthesis/evolution/lie_trotter.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ def __init__(
5555
insert_barriers: bool = False,
5656
cx_structure: str = "chain",
5757
atomic_evolution: (
58-
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
59-
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
60-
| None
58+
Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
6159
) = None,
6260
wrap: bool = False,
6361
preserve_order: bool = True,
@@ -75,9 +73,6 @@ def __init__(
7573
three arguments: the circuit to append the evolution to, the Pauli operator to
7674
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
7775
into a chain of ``CX`` gates and a single ``RZ`` gate.
78-
Alternatively, the function can also take Pauli operator and evolution time as
79-
inputs and returns the circuit that will be appended to the overall circuit being
80-
built.
8176
wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
8277
effect when ``atomic_evolution is None``.
8378
preserve_order: If ``False``, allows reordering the terms of the operator to

qiskit/synthesis/evolution/product_formula.py

+1-22
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from qiskit.circuit.parameterexpression import ParameterExpression
2626
from qiskit.circuit.quantumcircuit import QuantumCircuit, ParameterValueType
2727
from qiskit.quantum_info import SparsePauliOp, Pauli
28-
from qiskit.utils.deprecation import deprecate_arg
2928
from qiskit._accelerate.circuit_library import pauli_evolution
3029

3130
from .evolution_synthesis import EvolutionSynthesis
@@ -42,31 +41,14 @@ class ProductFormula(EvolutionSynthesis):
4241
:obj:`.LieTrotter` and :obj:`.SuzukiTrotter` inherit from this class.
4342
"""
4443

45-
@deprecate_arg(
46-
name="atomic_evolution",
47-
since="1.2",
48-
predicate=lambda callable: callable is not None
49-
and len(inspect.signature(callable).parameters) == 2,
50-
deprecation_description=(
51-
"The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the "
52-
"'atomic_evolution' argument"
53-
),
54-
additional_msg=(
55-
"Instead you should update your 'atomic_evolution' function to be of the following "
56-
"type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'."
57-
),
58-
pending=True,
59-
)
6044
def __init__(
6145
self,
6246
order: int,
6347
reps: int = 1,
6448
insert_barriers: bool = False,
6549
cx_structure: str = "chain",
6650
atomic_evolution: (
67-
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
68-
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
69-
| None
51+
Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
7052
) = None,
7153
wrap: bool = False,
7254
preserve_order: bool = True,
@@ -85,9 +67,6 @@ def __init__(
8567
three arguments: the circuit to append the evolution to, the Pauli operator to
8668
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
8769
into a chain of ``CX`` gates and a single ``RZ`` gate.
88-
Alternatively, the function can also take Pauli operator and evolution time as
89-
inputs and returns the circuit that will be appended to the overall circuit being
90-
built.
9170
wrap: Whether to wrap the atomic evolutions into custom gate objects. Note that setting
9271
this to ``True`` is slower than ``False``. This only takes effect when
9372
``atomic_evolution is None``.

qiskit/synthesis/evolution/qdrift.py

+1-23
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@
1414

1515
from __future__ import annotations
1616

17-
import inspect
1817
import math
1918
import typing
2019
from itertools import chain
2120
from collections.abc import Callable
2221
import numpy as np
2322
from qiskit.circuit.quantumcircuit import QuantumCircuit
2423
from qiskit.quantum_info.operators import SparsePauliOp, Pauli
25-
from qiskit.utils.deprecation import deprecate_arg
2624
from qiskit.exceptions import QiskitError
2725

2826
from .product_formula import ProductFormula, reorder_paulis
@@ -41,30 +39,13 @@ class QDrift(ProductFormula):
4139
`arXiv:quant-ph/1811.08017 <https://arxiv.org/abs/1811.08017>`_
4240
"""
4341

44-
@deprecate_arg(
45-
name="atomic_evolution",
46-
since="1.2",
47-
predicate=lambda callable: callable is not None
48-
and len(inspect.signature(callable).parameters) == 2,
49-
deprecation_description=(
50-
"The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the "
51-
"'atomic_evolution' argument"
52-
),
53-
additional_msg=(
54-
"Instead you should update your 'atomic_evolution' function to be of the following "
55-
"type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'."
56-
),
57-
pending=True,
58-
)
5942
def __init__(
6043
self,
6144
reps: int = 1,
6245
insert_barriers: bool = False,
6346
cx_structure: str = "chain",
6447
atomic_evolution: (
65-
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
66-
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
67-
| None
48+
Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
6849
) = None,
6950
seed: int | None = None,
7051
wrap: bool = False,
@@ -83,9 +64,6 @@ def __init__(
8364
three arguments: the circuit to append the evolution to, the Pauli operator to
8465
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
8566
into a chain of ``CX`` gates and a single ``RZ`` gate.
86-
Alternatively, the function can also take Pauli operator and evolution time as
87-
inputs and returns the circuit that will be appended to the overall circuit being
88-
built.
8967
seed: An optional seed for reproducibility of the random sampling process.
9068
wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
9169
effect when ``atomic_evolution is None``.

qiskit/synthesis/evolution/suzuki_trotter.py

+1-23
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
from __future__ import annotations
1616

17-
import inspect
1817
import typing
1918
from collections.abc import Callable
2019
from itertools import chain
@@ -23,7 +22,6 @@
2322
from qiskit.circuit.parameterexpression import ParameterExpression
2423
from qiskit.circuit.quantumcircuit import QuantumCircuit
2524
from qiskit.quantum_info.operators import SparsePauliOp, Pauli
26-
from qiskit.utils.deprecation import deprecate_arg
2725

2826
from .product_formula import ProductFormula, reorder_paulis
2927

@@ -60,31 +58,14 @@ class SuzukiTrotter(ProductFormula):
6058
`arXiv:math-ph/0506007 <https://arxiv.org/pdf/math-ph/0506007.pdf>`_
6159
"""
6260

63-
@deprecate_arg(
64-
name="atomic_evolution",
65-
since="1.2",
66-
predicate=lambda callable: callable is not None
67-
and len(inspect.signature(callable).parameters) == 2,
68-
deprecation_description=(
69-
"The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the "
70-
"'atomic_evolution' argument"
71-
),
72-
additional_msg=(
73-
"Instead you should update your 'atomic_evolution' function to be of the following "
74-
"type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'."
75-
),
76-
pending=True,
77-
)
7861
def __init__(
7962
self,
8063
order: int = 2,
8164
reps: int = 1,
8265
insert_barriers: bool = False,
8366
cx_structure: str = "chain",
8467
atomic_evolution: (
85-
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
86-
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
87-
| None
68+
Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None] | None
8869
) = None,
8970
wrap: bool = False,
9071
preserve_order: bool = True,
@@ -102,9 +83,6 @@ def __init__(
10283
three arguments: the circuit to append the evolution to, the Pauli operator to
10384
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
10485
into a chain of ``CX`` gates and a single ``RZ`` gate.
105-
Alternatively, the function can also take Pauli operator and evolution time as
106-
inputs and returns the circuit that will be appended to the overall circuit being
107-
built.
10886
wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
10987
effect when ``atomic_evolution is None``.
11088
preserve_order: If ``False``, allows reordering the terms of the operator to
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
upgrade_synthesis:
3+
- |
4+
The ``atomic_evolution`` argument to :class:`.ProductFormula` (and its
5+
subclasses :class:`.QDrift`, :class:`.LieTrotter` and :class:`SuzukiTrotter` )
6+
has a new function signature. Rather than taking some Pauli
7+
operator and time coefficient and returning the evolution circuit, the new
8+
function takes in an existing circuit and should append the evolution of the
9+
provided Pauli and given time to this circuit. This new implementation
10+
benefits from significantly better performance.

test/python/circuit/library/test_evolution_gate.py

+11-15
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ def test_labels_and_name(self):
442442
def test_atomic_evolution(self):
443443
"""Test a custom atomic_evolution."""
444444

445-
def atomic_evolution(pauli, time):
445+
def atomic_evolution(circuit, pauli, time):
446446
if isinstance(pauli, SparsePauliOp):
447447
if len(pauli.paulis) != 1:
448448
raise ValueError("Unsupported input.")
@@ -458,24 +458,20 @@ def atomic_evolution(pauli, time):
458458
target = i
459459
break
460460

461-
definition = QuantumCircuit(pauli.num_qubits)
462-
definition.compose(cliff, inplace=True)
463-
definition.compose(chain, inplace=True)
464-
definition.rz(2 * time, target)
465-
definition.compose(chain.inverse(), inplace=True)
466-
definition.compose(cliff.inverse(), inplace=True)
467-
468-
return definition
461+
circuit.compose(cliff, inplace=True)
462+
circuit.compose(chain, inplace=True)
463+
circuit.rz(2 * time, target)
464+
circuit.compose(chain.inverse(), inplace=True)
465+
circuit.compose(cliff.inverse(), inplace=True)
469466

470467
op = (X ^ X ^ X) + (Y ^ Y ^ Y) + (Z ^ Z ^ Z)
471468
time = 0.123
472469
reps = 4
473-
with self.assertWarns(PendingDeprecationWarning):
474-
evo_gate = PauliEvolutionGate(
475-
op,
476-
time,
477-
synthesis=LieTrotter(reps=reps, atomic_evolution=atomic_evolution),
478-
)
470+
evo_gate = PauliEvolutionGate(
471+
op,
472+
time,
473+
synthesis=LieTrotter(reps=reps, atomic_evolution=atomic_evolution),
474+
)
479475
decomposed = evo_gate.definition.decompose()
480476
self.assertEqual(decomposed.count_ops()["cx"], reps * 3 * 4)
481477

0 commit comments

Comments
 (0)