Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
56adebc
Arbitrary state prep qfunc decomp
comp-phys-marc Aug 13, 2025
100e241
cosine window decomp
comp-phys-marc Aug 13, 2025
f0d023a
FlipSign decomp
comp-phys-marc Aug 13, 2025
e30e91c
Amplitude amplifciation no capture
comp-phys-marc Aug 18, 2025
962d3a8
Permute qfunc decomp
comp-phys-marc Aug 18, 2025
91c4d04
AQFT decomp
comp-phys-marc Aug 18, 2025
414c6b1
Fable qfunc decomp
comp-phys-marc Aug 18, 2025
80583e1
[WIP] test FABLE qfunc
comp-phys-marc Aug 18, 2025
ac226fa
Tests passing
comp-phys-marc Aug 19, 2025
f5482dc
Changelog entry
comp-phys-marc Aug 19, 2025
e46563d
Merge branch 'master' into feature/more-p2-qfunc-decomps
comp-phys-marc Aug 19, 2025
8f1cece
FlipSign PL ctrl flow
comp-phys-marc Aug 19, 2025
cbfb041
pylint
comp-phys-marc Aug 19, 2025
0393520
Pylint
comp-phys-marc Aug 19, 2025
d24134d
Debug
comp-phys-marc Aug 19, 2025
931460f
Tweak
comp-phys-marc Aug 19, 2025
893e486
Increase test coverage
comp-phys-marc Aug 19, 2025
7fdfa53
Don't know a way to manually provide abstract test data
comp-phys-marc Aug 19, 2025
960fd7a
Fix num wires
comp-phys-marc Aug 19, 2025
60755f5
Update resource keys for ampl amp
comp-phys-marc Aug 19, 2025
c73e718
Concrete call signature for decomp
comp-phys-marc Aug 20, 2025
85bcd73
PR Feedback
comp-phys-marc Aug 20, 2025
4583fa6
Merge remote-tracking branch 'origin/feature/more-p2-qfunc-decomps' i…
comp-phys-marc Aug 20, 2025
9f3db7b
format
comp-phys-marc Aug 20, 2025
15dabec
neat one liner
comp-phys-marc Aug 20, 2025
de811c6
Format
comp-phys-marc Aug 20, 2025
af73853
Add decompose interpreter test
comp-phys-marc Aug 20, 2025
3c0913a
format
comp-phys-marc Aug 20, 2025
395b128
Merge branch 'master' into feature/more-p2-qfunc-decomps
comp-phys-marc Aug 20, 2025
d75503b
Scope import
comp-phys-marc Aug 20, 2025
5be4ac7
format
comp-phys-marc Aug 20, 2025
7794d39
capture enabled implies we have jax
comp-phys-marc Aug 21, 2025
76d465a
Remove unecessary is_abstract conditioned branch
comp-phys-marc Aug 21, 2025
c5f0c55
Merge remote-tracking branch 'origin/feature/more-p2-qfunc-decomps' i…
comp-phys-marc Aug 21, 2025
5b6c2e9
Cleanup imports
comp-phys-marc Aug 21, 2025
cda27b3
Add custom test for entering cnot loop
comp-phys-marc Aug 21, 2025
2e83442
Remove pragma: no cover
comp-phys-marc Aug 21, 2025
efea1aa
further clarity in test comments
comp-phys-marc Aug 22, 2025
19218cc
pylint disable at top of file
comp-phys-marc Aug 22, 2025
eb4a70e
Merge remote-tracking branch 'origin/feature/more-p2-qfunc-decomps' i…
comp-phys-marc Aug 22, 2025
635a4d5
Allow for DecomposeInterpreter to be used
comp-phys-marc Aug 22, 2025
6498e62
Put import in try / catch
comp-phys-marc Aug 22, 2025
0644411
Try to use qml.math.array with like=jax
comp-phys-marc Aug 22, 2025
513944e
Remove unused imports
comp-phys-marc Aug 22, 2025
ff6d8b5
Merge remote-tracking branch 'origin/feature/more-p2-qfunc-decomps' i…
comp-phys-marc Aug 22, 2025
c4b33c6
Update jnp arrays
comp-phys-marc Aug 22, 2025
77cbe34
Merge branch 'master' into feature/more-p2-qfunc-decomps
comp-phys-marc Aug 25, 2025
36e7c20
Remove unecesary use of Counter
comp-phys-marc Aug 25, 2025
8b60066
pylint
comp-phys-marc Aug 25, 2025
e726e75
Debug unrelated failure
comp-phys-marc Aug 25, 2025
f339f4b
xfail test
albi3ro Aug 25, 2025
877e552
Merge remote-tracking branch 'origin/xfail-lightning-test' into featu…
comp-phys-marc Aug 25, 2025
835bdc7
Update tests/ops/op_math/test_exp.py
albi3ro Aug 25, 2025
e7fb8c7
Merge remote-tracking branch 'origin/xfail-lightning-test' into featu…
comp-phys-marc Aug 25, 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
5 changes: 4 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,16 @@
[(#7385)](https://github.com/PennyLaneAI/pennylane/pull/7385)
[(#7941)](https://github.com/PennyLaneAI/pennylane/pull/7941)
[(#7943)](https://github.com/PennyLaneAI/pennylane/pull/7943)
[(#8075)](https://github.com/PennyLaneAI/pennylane/pull/8075)
[(#8002)](https://github.com/PennyLaneAI/pennylane/pull/8002)

The included templates are: :class:`~.Adder`, :class:`~.ControlledSequence`, :class:`~.ModExp`, :class:`~.MottonenStatePreparation`,
:class:`~.MPSPrep`, :class:`~.Multiplier`, :class:`~.OutAdder`, :class:`~.OutMultiplier`, :class:`~.OutPoly`, :class:`~.PrepSelPrep`,
:class:`~.ops.Prod`, :class:`~.Reflection`, :class:`~.Select`, :class:`~.StatePrep`, :class:`~.TrotterProduct`, :class:`~.QROM`,
:class:`~.GroverOperator`, :class:`~.UCCSD`, :class:`~.StronglyEntanglingLayers`, :class:`~.GQSP`, :class:`~.FermionicSingleExcitation`,
:class:`~.FermionicDoubleExcitation`, :class:`~.QROM`, :class:`~.Qubitization`, and :class:`~.Superposition`
:class:`~.FermionicDoubleExcitation`, :class:`~.QROM`, :class:`~.ArbitraryStatePreparation`, :class:`~.CosineWindow`,
:class:`~.AmplitudeAmplification`, :class:`~.Permute`, :class:`~.AQFT`, :class:`~.FlipSign`, :class:`~.FABLE`,
:class:`~.Qubitization`, and :class:`~.Superposition`

* A new function called :func:`~.math.choi_matrix` is available, which computes the [Choi matrix](https://en.wikipedia.org/wiki/Choi%E2%80%93Jamio%C5%82kowski_isomorphism) of a quantum channel.
This is a useful tool in quantum information science and to check circuit identities involving non-unitary operations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@
"""

import functools
from collections import Counter

import pennylane as qml
from pennylane import register_resources
from pennylane.control_flow import for_loop
from pennylane.decomposition import add_decomps, resource_rep
from pennylane.operation import Operation


Expand Down Expand Up @@ -82,6 +86,8 @@ def vqe(weights):

grad_method = None

resource_keys = {"num_wires"}

def __init__(self, weights, wires, id=None):
shape = qml.math.shape(weights)
if shape != (2 ** (len(wires) + 1) - 2,):
Expand All @@ -91,6 +97,12 @@ def __init__(self, weights, wires, id=None):

super().__init__(weights, wires=wires, id=id)

@property
def resource_params(self) -> dict:
return {
"num_wires": len(self.wires),
}

@property
def num_params(self):
return 1
Expand Down Expand Up @@ -141,3 +153,27 @@ def shape(n_wires):
tuple[int]: shape
"""
return (2 ** (n_wires + 1) - 2,)


def _arbitrary_state_preparation_resources(num_wires):
return dict(
Counter(
resource_rep(qml.PauliRot, pauli_word=pauli_word)
for pauli_word in _state_preparation_pauli_words(num_wires)
)
)


@register_resources(_arbitrary_state_preparation_resources)
def _arbitrary_state_preparation_decomposition(weights, wires):
pauli_words = _state_preparation_pauli_words(len(wires))

@for_loop(len(pauli_words))
def pauli_loop(i):
pauli_word = pauli_words[i]
qml.PauliRot(weights[i], pauli_word, wires=wires)

pauli_loop() # pylint: disable=no-value-for-parameter


add_decomps(ArbitraryStatePreparation, _arbitrary_state_preparation_decomposition)
41 changes: 40 additions & 1 deletion pennylane/templates/state_preparations/cosine_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import numpy as np

import pennylane as qml
from pennylane import math
from pennylane import capture, math, register_resources
from pennylane.control_flow import for_loop
from pennylane.decomposition import add_decomps, adjoint_resource_rep, resource_rep
from pennylane.exceptions import WireError
from pennylane.operation import StatePrepBase
from pennylane.wires import Wires
Expand Down Expand Up @@ -60,6 +62,8 @@ class CosineWindow(StatePrepBase):
[1.87469973e-33 2.50000000e-01 5.00000000e-01 2.50000000e-01]
"""

resource_keys = {"num_wires"}

@staticmethod
def compute_decomposition(wires): # pylint: disable=arguments-differ
r"""Representation of the operator as a product of other operators (static method).
Expand All @@ -83,6 +87,12 @@ def compute_decomposition(wires): # pylint: disable=arguments-differ

return decomp_ops

@property
def resource_params(self) -> dict:
return {
"num_wires": len(self.wires),
}

def label(self, decimals=None, base_label=None, cache=None):
return "CosineWindow"

Expand Down Expand Up @@ -131,3 +141,32 @@ def state_vector(self, wire_order=None):
ket = ket.transpose(desired_order)

return math.convert_like(ket, op_vector)


def _cosine_window_resources(num_wires):
return {
resource_rep(qml.Hadamard): 1,
resource_rep(qml.RZ): 1,
adjoint_resource_rep(qml.QFT, {"num_wires": num_wires}): 1,
resource_rep(qml.PhaseShift): num_wires,
}


@register_resources(_cosine_window_resources)
def _cosine_window_decomposition(wires):
qml.Hadamard(wires=wires[-1])
qml.RZ(np.pi, wires=wires[-1])
qml.adjoint(qml.QFT)(wires=wires)

if capture.enabled():
wires = qml.math.array(wires, like="jax")

@for_loop(len(wires))
def wires_loop(ind):
wire = wires[ind]
qml.PhaseShift(np.pi * 2 ** (-ind - 1), wires=wire)

wires_loop() # pylint: disable=no-value-for-parameter


add_decomps(CosineWindow, _cosine_window_decomposition)
103 changes: 103 additions & 0 deletions pennylane/templates/subroutines/amplitude_amplification.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@

import numpy as np

from pennylane.control_flow import for_loop
from pennylane.decomposition import (
add_decomps,
controlled_resource_rep,
register_resources,
resource_rep,
)
from pennylane.operation import Operation
from pennylane.ops import Hadamard, PhaseShift
from pennylane.ops.op_math import ctrl
Expand Down Expand Up @@ -108,11 +115,23 @@ def circuit():

grad_method = None

resource_keys = {"fixed_point", "O", "iters", "num_reflection_wires", "U"}

def _flatten(self):
data = (self.hyperparameters["U"], self.hyperparameters["O"])
metadata = tuple(item for item in self.hyperparameters.items() if item[0] not in ["O", "U"])
return data, metadata

@property
def resource_params(self) -> dict:
return {
"fixed_point": self.hyperparameters["fixed_point"],
"O": self.hyperparameters["O"],
"iters": self.hyperparameters["iters"],
"num_reflection_wires": len(self.hyperparameters["reflection_wires"]),
"U": self.hyperparameters["U"],
}

@classmethod
def _primitive_bind_call(cls, *args, **kwargs):
return cls._primitive.bind(*args, **kwargs)
Expand Down Expand Up @@ -202,3 +221,87 @@ def queue(self, context=QueuingManager):
context.remove(op)
context.append(self)
return self


def _amplitude_amplification_resources(fixed_point, O, iters, num_reflection_wires, U):
resources = {}

if fixed_point and iters // 2 > 0:

resources[resource_rep(Hadamard)] = 4 * (iters // 2)
resources[
controlled_resource_rep(
O.__class__,
O.resource_params,
num_control_wires=1,
)
] = 2 * (iters // 2)
resources[resource_rep(PhaseShift)] = iters // 2
resources[
resource_rep(
Reflection,
base_class=U.__class__,
base_params=U.resource_params,
num_wires=len(U.wires),
num_reflection_wires=num_reflection_wires,
)
] = (
iters // 2
)
elif not fixed_point and iters > 0:
resources[resource_rep(O.__class__, **O.resource_params)] = iters
resources[
resource_rep(
Reflection,
base_class=U.__class__,
base_params=U.resource_params,
num_wires=len(U.wires),
num_reflection_wires=num_reflection_wires,
)
] = iters

return resources


@register_resources(_amplitude_amplification_resources)
def _amplitude_amplification_decomposition(
*_, U, O, iters, fixed_point, work_wire, p_min, reflection_wires, **__
):

delta = np.sqrt(1 - p_min)
gamma = np.cos(np.arccos(1 / delta, dtype=np.complex128) / iters, dtype=np.complex128) ** -1

if fixed_point:

def alpha(iter):
return np.real(
2 * np.arctan(1 / (np.tan(2 * np.pi * (iter + 1) / iters) * np.sqrt(1 - gamma**2)))
)

def beta(iter):
return -alpha(-(iter + 1))

@for_loop(iters // 2)
def half_iter_loop(iter):
Hadamard(wires=work_wire)
ctrl(O, control=work_wire)
Hadamard(wires=work_wire)
PhaseShift(beta(iter), wires=work_wire)
Hadamard(wires=work_wire)
ctrl(O, control=work_wire)
Hadamard(wires=work_wire)

Reflection(U, -alpha(iter), reflection_wires=reflection_wires)

half_iter_loop() # pylint: disable=no-value-for-parameter
else:

@for_loop(iters)
def iter_loop(_):
apply(O)
Reflection(U, np.pi, reflection_wires=reflection_wires)

iter_loop() # pylint: disable=no-value-for-parameter


add_decomps(AmplitudeAmplification, _amplitude_amplification_decomposition)
79 changes: 78 additions & 1 deletion pennylane/templates/subroutines/aqft.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@

import numpy as np

from pennylane import capture, math
from pennylane.control_flow import for_loop
from pennylane.decomposition import (
add_decomps,
controlled_resource_rep,
register_resources,
resource_rep,
)
from pennylane.operation import Operation
from pennylane.ops import SWAP, ControlledPhaseShift, Hadamard
from pennylane.ops import SWAP, ControlledPhaseShift, Hadamard, PhaseShift, cond


class AQFT(Operation):
Expand Down Expand Up @@ -117,6 +125,8 @@ def circ():

"""

resource_keys = {"num_wires", "order"}

def __init__(self, order, wires=None, id=None):
n_wires = len(wires)

Expand All @@ -139,6 +149,10 @@ def __init__(self, order, wires=None, id=None):
self.hyperparameters["order"] = order
super().__init__(wires=wires, id=id)

@property
def resource_params(self) -> dict:
return {"order": self.hyperparameters["order"], "num_wires": len(self.wires)}

@property
def num_params(self):
return 0
Expand Down Expand Up @@ -188,3 +202,66 @@ def compute_decomposition(wires, order): # pylint: disable=arguments-differ
decomp_ops.append(swap)

return decomp_ops


def _AQFT_resources(num_wires, order):

resources = {}

resources[resource_rep(Hadamard)] = num_wires

resources[
controlled_resource_rep(
PhaseShift,
{},
num_control_wires=1,
)
] = sum(min(num_wires - 1 - i, order) for i in range(num_wires))

resources[resource_rep(SWAP)] = num_wires // 2

return dict(resources)


@register_resources(_AQFT_resources)
def _AQFT_decomposition(wires, order):
n_wires = len(wires)
shifts = [2 * np.pi * 2**-i for i in range(2, n_wires + 1)]

if capture.enabled():
shifts = math.array(shifts, like="jax")
wires = math.array(wires, like="jax")

@for_loop(len(wires))
def wire_loop(i):
wire = wires[i]
Hadamard(wire)

@for_loop(n_wires - 1 - i)
def wires_limited_shift_loop(j):
shift = shifts[j]
control_wire = wires[i + 1 + j]

ControlledPhaseShift(shift, wires=[control_wire, wire])

@for_loop(order)
def order_limited_shift_loop(j):
shift = shifts[j]
control_wire = wires[i + 1 + j]

ControlledPhaseShift(shift, wires=[control_wire, wire])

cond(n_wires - 1 - i < order, wires_limited_shift_loop, order_limited_shift_loop)()

wire_loop() # pylint: disable=no-value-for-parameter

@for_loop(len(wires) // 2)
def half_wire_loop(k):
wire1 = wires[k]
wire2 = wires[-k - 1]
SWAP(wires=[wire1, wire2])

half_wire_loop() # pylint: disable=no-value-for-parameter


add_decomps(AQFT, _AQFT_decomposition)
Loading