Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
57dce3b
[WIP] IQP Template
comp-phys-marc Dec 4, 2025
a57e485
Resource class
comp-phys-marc Dec 5, 2025
39807df
Test
comp-phys-marc Dec 5, 2025
2f71359
[WIP] expval
comp-phys-marc Dec 8, 2025
b2eeabb
Remove expval WIP
comp-phys-marc Dec 9, 2025
05b4153
changelog
comp-phys-marc Dec 9, 2025
0c321c5
Merge branch 'master' into feature/IQPCircuit
comp-phys-marc Dec 9, 2025
e12c175
pylint
comp-phys-marc Dec 9, 2025
c308da9
pylint
comp-phys-marc Dec 9, 2025
23cd757
Test primitive bind call
comp-phys-marc Dec 9, 2025
1c1a7fb
Cleanup imports
comp-phys-marc Dec 9, 2025
137ca02
IQP circuit contents test
comp-phys-marc Dec 9, 2025
cf3b4ab
pylint
comp-phys-marc Dec 9, 2025
e9bf373
Debug docs
comp-phys-marc Dec 9, 2025
090df05
Merge branch 'master' into feature/IQPCircuit
comp-phys-marc Dec 9, 2025
b82ab6f
Rename init_coeffs -> init_params
comp-phys-marc Dec 9, 2025
d9dd3ba
Deug docs
comp-phys-marc Dec 9, 2025
7dd3842
Debug docs
comp-phys-marc Dec 9, 2025
f72148e
Simplify
comp-phys-marc Dec 10, 2025
39f8c74
Remove unoptimized init layer
comp-phys-marc Dec 10, 2025
d64562f
Rename params
comp-phys-marc Dec 10, 2025
1480f86
Format
comp-phys-marc Dec 10, 2025
2f745fb
Reorder params
comp-phys-marc Dec 10, 2025
e0fc340
correct typo
josephbowles Dec 11, 2025
4eb9c20
Reword for natural tone
comp-phys-marc Dec 11, 2025
c1633c9
Replace gates -> pattern in docs
comp-phys-marc Dec 11, 2025
4ff9b9a
PR Feedback: docs wording
comp-phys-marc Dec 11, 2025
3bf0123
Merge remote-tracking branch 'origin/feature/IQPCircuit' into feature…
comp-phys-marc Dec 11, 2025
3a72eb9
Merge branch 'master' into feature/IQPCircuit
comp-phys-marc Dec 11, 2025
c9a55a0
pattern example
comp-phys-marc Dec 11, 2025
0824b52
update docstring
josephbowles Dec 12, 2025
c6530bf
update docstring
josephbowles Dec 12, 2025
eb00e4c
changelog
comp-phys-marc Dec 12, 2025
e1fe2bb
Merge remote-tracking branch 'origin/feature/IQPCircuit' into feature…
comp-phys-marc Dec 12, 2025
f551a06
Combine docstrings
comp-phys-marc Dec 12, 2025
a468d9d
Example in docs
comp-phys-marc Dec 12, 2025
b483a71
seealso section
comp-phys-marc Dec 12, 2025
7e28e4c
Debug doctest
comp-phys-marc Dec 12, 2025
ec2eb7f
name args
comp-phys-marc Dec 15, 2025
4c3fea0
[WIP] matrix
comp-phys-marc Dec 15, 2025
8f63b1f
[WIP] matrix
comp-phys-marc Dec 15, 2025
8ee8deb
matrix
comp-phys-marc Dec 15, 2025
d2025c0
Resource estimation example
comp-phys-marc Dec 15, 2025
26d2dd5
pylint
comp-phys-marc Dec 15, 2025
35f41f7
SKIP doctests due to stochasticity
comp-phys-marc Dec 15, 2025
9a19e8d
Use mathematical definition for matrix
comp-phys-marc Dec 15, 2025
d1fe60a
Cleanup imports
comp-phys-marc Dec 15, 2025
88d9113
backticks
comp-phys-marc Dec 16, 2025
c7796eb
backticks
comp-phys-marc Dec 16, 2025
5969c91
backticks
comp-phys-marc Dec 16, 2025
07d8d99
backticks
comp-phys-marc Dec 16, 2025
490179a
keywords
comp-phys-marc Dec 16, 2025
27fb14c
template docs and thumbnail
comp-phys-marc Dec 16, 2025
5fb089d
Merge branch 'master' into feature/IQPCircuit
comp-phys-marc Dec 16, 2025
59bf282
Resource docs
comp-phys-marc Dec 16, 2025
58f47a1
Merge branch 'master' into feature/IQPCircuit
comp-phys-marc Dec 16, 2025
edd665a
Merge branch 'master' into feature/IQPCircuit
comp-phys-marc Dec 16, 2025
2a2507e
Reorder changelog
comp-phys-marc Dec 16, 2025
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
Binary file added doc/_static/templates/subroutines/iqp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions doc/introduction/templates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ Other subroutines

Other useful templates which do not belong to the previous categories can be found here.

.. gallery-item::
:description: :doc:`Instantaneous Quantum Polynomial Circuit <../code/api/pennylane.IQP>`
:figure: _static/templates/subroutines/iqp.png

.. gallery-item::
:description: :doc:`Grover Diffusion Operator <../code/api/pennylane.GroverOperator>`
:figure: _static/templates/subroutines/grover.svg
Expand Down
9 changes: 9 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
Hadamard gradient differentiation methods per [the paper](https://arxiv.org/pdf/2408.05406).
[(#8640)](https://github.com/PennyLaneAI/pennylane/pull/8640)

<h4>Instantaneous Quantum Polynomial Circuits </h4>

* A new template for building an Instantaneous Quantum Polynomial (`~.IQP`) circuit has been added along with a
lightweight version (based on the :class:`~.estimator.resource_operator.ResourceOperator` class) to rapidly
estimate its resources. This unlocks easily estimating the resources of the IQP circuit introduced in the
`Train on classical, deploy on quantum <https://arxiv.org/abs/2503.02934>`_ work for generative quantum machine
learning.
[(#8748)](https://github.com/PennyLaneAI/pennylane/pull/8748)

<h4>Pauli-based computation </h4>

* Users can now perform rapid Clifford+T decomposition with :func:`pennylane.qjit` using the new
Expand Down
2 changes: 2 additions & 0 deletions pennylane/labs/resource_estimation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
~ResourceSemiAdder
~ResourceQPE
~ResourceIterativeQPE
~ResourceIQP
~ResourceControlledSequence
~ResourceQFT
~ResourceAQFT
Expand Down Expand Up @@ -227,6 +228,7 @@
ResourcePhaseGradient,
ResourceOutMultiplier,
ResourceSemiAdder,
ResourceIQP,
ResourceQFT,
ResourceAQFT,
ResourceBasisRotation,
Expand Down
1 change: 1 addition & 0 deletions pennylane/labs/resource_estimation/templates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ResourcePhaseGradient,
ResourceOutMultiplier,
ResourceSemiAdder,
ResourceIQP,
ResourceQFT,
ResourceAQFT,
ResourceBasisRotation,
Expand Down
142 changes: 142 additions & 0 deletions pennylane/labs/resource_estimation/templates/subroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,148 @@ def resource_decomp(cls, base_cmpr_op, num_iter, **kwargs):
return gate_counts


class ResourceIQP(ResourceOperator):
r"""Resource class for IQP.

Args:
num_wires (int): the number of qubits the operation acts upon
pattern (list[list[list[int]]]): Specification of the trainable gates. Each element of gates corresponds to a
unique trainable parameter. Each sublist specifies the generators to which that parameter applies.
Generators are specified by listing the qubits on which an X operator acts.
spin_sym (bool, optional): If True, the circuit is equivalent to one where the initial state
:math:`\frac{1}{\sqrt(2)}(|00\dots0> + |11\dots1>)` is used in place of :math:`|00\dots0>`.
wires (Sequence[int], optional): the wires the operation acts on

**Example:**

We can inspect the resources used by an IQP subroutine by using `qml.specs`.

.. code-block:: python

import pennylane as qml

@qml.qnode(qml.device('lightning.qubit'))
def circuit():
qml.IQP(
weights=math.random.uniform(0, 2 * np.pi, 4),
pattern=[[[0]], [[1]], [[2]], [[3]]],
spin_sym=True,
num_wires=4,
)
return qml.state()

>>> print(qml.specs(circuit, level="device")())
Device: lightning.qubit
Device wires: None
Shots: Shots(total=None)
Level: device
Resource specifications:
Total qubit allocations: 4
Total gates: 13
Circuit depth: 4
Gate types:
PauliRot: 1
Hadamard: 8
MultiRZ: 4
Measurements:
state(all wires): 1

.. seealso:: :class:`~.IQP`

"""

resource_keys = {"spin_sym", "pattern", "num_wires"}

def __init__(self, num_wires, pattern, spin_sym, wires=None) -> None:
self.num_wires = num_wires
self.spin_sym = spin_sym
self.pattern = pattern
super().__init__(wires=wires)

@property
def resource_params(self) -> dict:
r"""Returns a dictionary containing the minimal information needed to compute the resources.

Returns:
dict: A dictionary containing the resource parameters:
* num_wires (int): the number of qubits the operation acts upon
"""
return {
"spin_sym": self.spin_sym,
"pattern": self.pattern,
"num_wires": self.num_wires,
}

@classmethod
def resource_rep(cls, num_wires, pattern, spin_sym) -> CompressedResourceOp:
r"""Returns a compressed representation containing only the parameters of
the Operator that are needed to compute the resources.

Args:
num_wires (int): the number of qubits the operation acts upon
pattern (list[list[list[int]]]): Specification of the trainable gates. Each element of gates corresponds to a
unique trainable parameter. Each sublist specifies the generators to which that parameter applies.
Generators are specified by listing the qubits on which an X operator acts.
spin_sym (bool, optional): If True, the circuit is equivalent to one where the initial state
:math:`\frac{1}{\sqrt(2)}(|00\dots0> + |11\dots1>)` is used in place of :math:`|00\dots0>`.

Returns:
CompressedResourceOp: the operator in a compressed representation
"""
return CompressedResourceOp(
cls,
num_wires,
{
"spin_sym": spin_sym,
"pattern": pattern,
"num_wires": num_wires,
},
)

@classmethod
def resource_decomp(cls, num_wires, pattern, spin_sym) -> list[GateCount]:
r"""Returns a list representing the resources of the operator. Each object in the list
represents a gate and the number of times it occurs in the circuit.

Args:
num_wires (int): the number of qubits the operation acts upon
pattern (list[list[list[int]]]): Specification of the trainable gates. Each element of gates corresponds to a
unique trainable parameter. Each sublist specifies the generators to which that parameter applies.
Generators are specified by listing the qubits on which an X operator acts.
spin_sym (bool, optional): If True, the circuit is equivalent to one where the initial state
:math:`\frac{1}{\sqrt(2)}(|00\dots0> + |11\dots1>)` is used in place of :math:`|00\dots0>`.

Returns:
list[~.pennylane.labs.resource_estimation.GateCount]: A list of GateCount objects, where each object
represents a specific quantum gate and the number of times it appears
in the decomposition.
"""
hadamard = resource_rep(re.ResourceHadamard)
pauli_rot = resource_rep(re.ResourcePauliRot, {"pauli_string": "Y" + "X" * (num_wires - 1)})

hadamard_counts = 2 * num_wires
multi_rz_counts = defaultdict(int)

for gate in pattern:
for gen in gate:
multi_rz_counts[len(gen)] += 1

ret = [GateCount(hadamard, hadamard_counts)]

if spin_sym:
ret.append(GateCount(pauli_rot, 1))

return ret + [
GateCount(resource_rep(re.ResourceMultiRZ, {"num_wires": wires}), counts)
for (wires, counts) in multi_rz_counts.items()
]

@staticmethod
def tracking_name(num_wires, pattern, spin_sym) -> str:
r"""Returns the tracking name built with the operator's parameters."""
return f"IQP({num_wires}, {pattern}, {spin_sym})"


class ResourceQFT(ResourceOperator):
r"""Resource class for QFT.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,85 @@ def test_resources(self, base_op, num_iter, expected_res):
assert op.resource_decomp(**op.resource_params) == expected_res


class TestResourceIQP:
"""Test the ResourceIQP class."""

@pytest.mark.parametrize(
("num_wires", "pattern", "spin_sym"),
[
(4, [[[0]], [[1]], [[2]], [[3]]], False),
(
6,
[[[0]], [[4]], [[3]], [[2]], [[1]], [[5]]],
True,
),
],
)
def test_resource_params(self, num_wires, pattern, spin_sym):
"""Test that the resource params are correct."""
op = plre.ResourceIQP(num_wires, pattern, spin_sym)
assert op.resource_params == {
"spin_sym": spin_sym,
"pattern": pattern,
"num_wires": num_wires,
}

@pytest.mark.parametrize(
("num_wires", "pattern", "spin_sym"),
[
(4, [[[0]], [[1]], [[2]], [[3]]], False),
(
6,
[[[0]], [[1]], [[2]], [[3]], [[4]], [[5]]],
True,
),
],
)
def test_resource_rep(self, num_wires, pattern, spin_sym):
"""Test that the compressed representation is correct."""
expected = plre.CompressedResourceOp(
plre.ResourceIQP,
num_wires,
{
"num_wires": num_wires,
"pattern": pattern,
"spin_sym": spin_sym,
},
)
assert (
plre.ResourceIQP.resource_rep(num_wires=num_wires, pattern=pattern, spin_sym=spin_sym)
== expected
)

@pytest.mark.parametrize(
("num_wires", "pattern", "spin_sym", "expected_res"),
[
(
4,
[[[0]], [[1]], [[2]], [[3]]],
False,
[
GateCount(resource_rep(plre.ResourceHadamard), 8),
GateCount(resource_rep(plre.ResourceMultiRZ, {"num_wires": 1}), 4),
],
),
(
6,
[[[0]], [[1]], [[2]], [[3]], [[4]], [[5]]],
True,
[
GateCount(resource_rep(plre.ResourceHadamard), 12),
GateCount(resource_rep(plre.ResourcePauliRot, {"pauli_string": "YXXXXX"}), 1),
GateCount(resource_rep(plre.ResourceMultiRZ, {"num_wires": 1}), 6),
],
),
],
)
def test_resources(self, num_wires, pattern, spin_sym, expected_res):
"""Test that the resources are correct."""
assert plre.ResourceIQP.resource_decomp(num_wires, pattern, spin_sym) == expected_res


class TestResourceQFT:
"""Test the ResourceQFT class."""

Expand Down
2 changes: 2 additions & 0 deletions pennylane/templates/subroutines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from .aqft import AQFT
from .amplitude_amplification import AmplitudeAmplification
from .qram import BBQRAM
from .iqp import IQP
from .qrom import QROM
from .gqsp import GQSP
from .select_pauli_rot import SelectPauliRot
Expand Down Expand Up @@ -78,6 +79,7 @@
"TrotterProduct",
"trotterize",
"Interferometer",
"IQP",
"Permute",
"QFT",
"QuantumPhaseEstimation",
Expand Down
Loading