Skip to content
Merged
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
25ac599
Reflection decomposition
comp-phys-marc Jul 17, 2025
da049f8
Changelog
comp-phys-marc Jul 17, 2025
d019e78
Cleanup imports
comp-phys-marc Jul 17, 2025
51ffeb6
[WIP] TrotterProduct qfunc decomp
comp-phys-marc Jul 18, 2025
a50ba32
[WIP] Test and debug
comp-phys-marc Jul 18, 2025
869adec
[WIP] Most cases passing
comp-phys-marc Jul 18, 2025
cac9019
TrotterProduct qfunc decomposition
comp-phys-marc Jul 18, 2025
fc05a8f
Remove unecessary changes to trotterized_qfunc
comp-phys-marc Jul 18, 2025
c4966e0
Remove trotterized_qfunc resource params
comp-phys-marc Jul 18, 2025
f284608
Changelog
comp-phys-marc Jul 18, 2025
8b17fbc
Remove unecessary resource param
comp-phys-marc Jul 21, 2025
c49792d
MPSPrep qfunc decomp
comp-phys-marc Jul 21, 2025
0da81c6
Changelog
comp-phys-marc Jul 21, 2025
365ac3f
No need for resource_rep
comp-phys-marc Jul 21, 2025
7ef2047
Remove unecessary Counter
comp-phys-marc Jul 21, 2025
ad7bfab
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 21, 2025
5f8c0c2
Revert meth -> math
comp-phys-marc Jul 21, 2025
e1dd92a
Make use of register_condition
comp-phys-marc Jul 21, 2025
f60be30
[WIP] QROM qfunc decomp
comp-phys-marc Jul 22, 2025
2b42424
Remove loop
comp-phys-marc Jul 22, 2025
a740070
Remove loop
comp-phys-marc Jul 22, 2025
65b5620
Combine conditions
comp-phys-marc Jul 22, 2025
9315ecf
Tiny simplification
comp-phys-marc Jul 22, 2025
a6c3670
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 22, 2025
708fa24
Call to class since qml.apply not captured
comp-phys-marc Jul 22, 2025
19c2912
Remove loop
comp-phys-marc Jul 22, 2025
a57bdd7
Use unflatten instead of class dunder
comp-phys-marc Jul 22, 2025
9e999c1
Use unflatten / flatten
comp-phys-marc Jul 22, 2025
7def593
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 22, 2025
5d83a7b
Fix condition
comp-phys-marc Jul 22, 2025
b659e3d
Decomp for Fermionic Double Excitation
comp-phys-marc Jul 22, 2025
456a8b1
Revert "Decomp for Fermionic Double Excitation"
comp-phys-marc Jul 22, 2025
c41fa01
Cleanup imports
comp-phys-marc Jul 23, 2025
b49bbf4
Comment regarding assignment convention
comp-phys-marc Jul 23, 2025
348aec3
Format
comp-phys-marc Jul 23, 2025
5ba3191
Fix condition
comp-phys-marc Jul 23, 2025
ddbfadf
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 23, 2025
6394943
Add additional test cases
comp-phys-marc Jul 23, 2025
66fc9e3
Merge branch 'master' into feature/q1_subroutine_func_decomps
JerryChen97 Jul 23, 2025
a05fecf
pylint
comp-phys-marc Jul 23, 2025
e4d09bd
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 23, 2025
2cf62dc
and should be or
comp-phys-marc Jul 23, 2025
83c0a67
Visual simplificaiton of logical expression
comp-phys-marc Jul 23, 2025
7d35746
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 23, 2025
5d53bd8
pylint
comp-phys-marc Jul 23, 2025
2cd8ff5
Figure out relationships of circuit depth with n, order
comp-phys-marc Jul 23, 2025
d2a788f
Cleanup imports
comp-phys-marc Jul 23, 2025
dd421fb
Make is even check more explicit
comp-phys-marc Jul 23, 2025
e72226b
Changelog indentation
comp-phys-marc Jul 23, 2025
9989219
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 23, 2025
25431ac
Stop recording
comp-phys-marc Jul 24, 2025
2edc098
Simplified linear algebra
comp-phys-marc Jul 24, 2025
3962338
Sanity checks
comp-phys-marc Jul 24, 2025
1686d25
pylint
comp-phys-marc Jul 24, 2025
2543a8b
Typo
comp-phys-marc Jul 24, 2025
8352cb5
Format
comp-phys-marc Jul 24, 2025
be22533
Merge remote-tracking branch 'origin/feature/q1_subroutine_func_decom…
comp-phys-marc Jul 24, 2025
614e1b4
Merge branch 'master' into feature/q1_subroutine_func_decomps
comp-phys-marc Jul 24, 2025
bedce20
We don't support odd num ops
comp-phys-marc Jul 24, 2025
81092ce
Improve coverage
comp-phys-marc Jul 24, 2025
1f3d611
format
comp-phys-marc Jul 24, 2025
4d9c7ee
Write decomp without lists
comp-phys-marc Jul 24, 2025
687ceb6
Merge branch 'master' into feature/q1_subroutine_func_decomps
comp-phys-marc Jul 24, 2025
0d085ff
Add capture test
comp-phys-marc Jul 24, 2025
33edf4d
pylint
comp-phys-marc Jul 24, 2025
4c38154
Merge branch 'feature/q1_subroutine_func_decomps' into feature/p0_sub…
comp-phys-marc Jul 24, 2025
e1891ef
Pylint
comp-phys-marc Jul 24, 2025
a573916
Forgot dict val
comp-phys-marc Jul 24, 2025
efe7d55
PR Feedback
comp-phys-marc Jul 24, 2025
61353f1
Merge remote-tracking branch 'origin/feature/p0_subroutine_func_decom…
comp-phys-marc Jul 24, 2025
64be908
Add more test cases
comp-phys-marc Jul 24, 2025
770d3d6
[WIP] debug
comp-phys-marc Jul 28, 2025
98fd811
Merge branch 'master' into feature/p0_subroutine_func_decomps
comp-phys-marc Jul 28, 2025
32eac7b
Don't queue ops used to make Prod
comp-phys-marc Jul 28, 2025
bd27cbc
TODO
comp-phys-marc Jul 28, 2025
1f8557d
Pylint
comp-phys-marc Jul 28, 2025
d58afa6
Base params for Prod
comp-phys-marc Jul 29, 2025
fb16935
Format
comp-phys-marc Jul 29, 2025
d493b0d
Bug fix
comp-phys-marc Jul 29, 2025
080f13b
Merge branch 'master' into feature/p0_subroutine_func_decomps
comp-phys-marc Jul 30, 2025
ec3a039
Use qml_ops instead of qml
comp-phys-marc Jul 30, 2025
64d39af
Refactor imports for tach
comp-phys-marc Jul 30, 2025
6db2672
Cleanup imports
comp-phys-marc Jul 30, 2025
d60a0ac
Format
comp-phys-marc Jul 30, 2025
884210b
Select resource reps
comp-phys-marc Jul 30, 2025
60b055d
format
comp-phys-marc Jul 30, 2025
f3acd3b
Some tests passing
comp-phys-marc Jul 30, 2025
353dcee
All test passing
comp-phys-marc Jul 30, 2025
0af32e2
Cleanup changelog
comp-phys-marc Jul 30, 2025
044530e
Changelog
comp-phys-marc Jul 30, 2025
9663245
Refactor helpers to be global
comp-phys-marc Jul 30, 2025
95d7fee
pylint
comp-phys-marc Jul 30, 2025
e299896
Format
comp-phys-marc Jul 30, 2025
6332cbe
Bad indent
comp-phys-marc Jul 30, 2025
0d4a42f
Add case for no control wires
comp-phys-marc Jul 30, 2025
2d43e62
Increase test coverage
comp-phys-marc Jul 30, 2025
336aae6
Merge branch 'master' into feature/p0_subroutine_func_decomps
comp-phys-marc Jul 30, 2025
9daca61
Merge branch 'master' into feature/p0_subroutine_func_decomps
comp-phys-marc Aug 1, 2025
6a3285c
pylint
comp-phys-marc Aug 1, 2025
bd1e2e1
Merge branch 'master' into feature/p0_subroutine_func_decomps
comp-phys-marc Aug 1, 2025
b1df3cd
Trigger CI
comp-phys-marc Aug 4, 2025
bc41a6c
Merge branch 'master' into feature/p0_subroutine_func_decomps
comp-phys-marc Aug 4, 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
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
[(#7779)](https://github.com/PennyLaneAI/pennylane/pull/7779)
[(#7908)](https://github.com/PennyLaneAI/pennylane/pull/7908)
[(#7385)](https://github.com/PennyLaneAI/pennylane/pull/7385)
[(#7941)](https://github.com/PennyLaneAI/pennylane/pull/7941)

The included templates are:

Expand Down Expand Up @@ -86,6 +87,8 @@

* :class:`~.TrotterProduct`

* :class:`~.QROM`

* 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.
[(#7951)](https://github.com/PennyLaneAI/pennylane/pull/7951)
Expand Down
213 changes: 213 additions & 0 deletions pennylane/templates/subroutines/qrom.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@
"""

import math
from collections import Counter
from functools import reduce

import numpy as np

from pennylane import ops as qml_ops
from pennylane.decomposition import (
add_decomps,
controlled_resource_rep,
register_resources,
resource_rep,
)
from pennylane.operation import Operation
from pennylane.queuing import QueuingManager, apply
from pennylane.templates.embeddings import BasisEmbedding
Expand Down Expand Up @@ -109,6 +117,14 @@
"""

resource_keys = {
"num_bitstrings",
"num_control_wires",
"num_target_wires",
"num_work_wires",
"clean",
}

def __init__(
self,
bitstrings,
Expand Down Expand Up @@ -157,6 +173,16 @@
metadata = tuple((key, value) for key, value in self.hyperparameters.items())
return tuple(), metadata

@property
def resource_params(self) -> dict:
return {
"num_bitstrings": len(self.hyperparameters["bitstrings"]),
"num_control_wires": len(self.hyperparameters["control_wires"]),
"num_target_wires": len(self.hyperparameters["target_wires"]),
"num_work_wires": len(self.hyperparameters["work_wires"]),
"clean": self.hyperparameters["clean"],
}

@classmethod
def _unflatten(cls, data, metadata):
hyperparams_dict = dict(metadata)
Expand Down Expand Up @@ -310,3 +336,190 @@
def clean(self):
"""Boolean to select the version of QROM."""
return self.hyperparameters["clean"]


def _qrom_decomposition_resources(

Check notice on line 341 in pennylane/templates/subroutines/qrom.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/templates/subroutines/qrom.py#L341

Too many branches (14/12) (too-many-branches)
num_bitstrings, num_control_wires, num_target_wires, num_work_wires, clean
):
if num_control_wires == 0:
return {resource_rep(BasisEmbedding, num_wires=num_target_wires): num_bitstrings}

num_swap_wires = num_target_wires + num_work_wires

# number of operators we store per column (power of 2)
depth = num_swap_wires // num_target_wires
depth = int(2 ** np.floor(np.log2(depth)))
depth = min(depth, num_bitstrings)

ops = [resource_rep(BasisEmbedding, num_wires=num_target_wires) for _ in range(num_bitstrings)]
ops_identity = ops + [qml_ops.I] * int(2**num_control_wires - num_bitstrings)

n_columns = (
num_bitstrings // depth if num_bitstrings % depth == 0 else num_bitstrings // depth + 1
)

# New ops block
new_ops = Counter()
for i in range(n_columns):
column_ops = Counter()
for j in range(depth):
column_ops[ops_identity[i * depth + j]] += 1
if len(column_ops) == 1 and list(column_ops.values())[0] == 1:
new_ops[list(column_ops.keys())[0]] += 1
else:
new_ops[resource_rep(qml_ops.op_math.Prod, resources=dict(column_ops))] += 1

# Select block
num_control_select_wires = int(math.ceil(math.log2(2**num_control_wires / depth)))

new_ops_reps = reduce(
lambda acc, lst: acc + lst, [[key for _ in range(val)] for key, val in new_ops.items()]
)

if num_control_select_wires > 0:
select_ops = {
resource_rep(
Select, num_control_wires=num_control_select_wires, op_reps=tuple(new_ops_reps)
): 1
}
else:
select_ops = new_ops

# Swap block
num_control_swap_wires = num_control_wires - num_control_select_wires
swap_resources = Counter()
for ind in range(num_control_swap_wires):

Check notice on line 391 in pennylane/templates/subroutines/qrom.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/templates/subroutines/qrom.py#L391

Too many positional arguments (6/5) (too-many-positional-arguments)
for j in range(2**ind):
num_swaps = min(
(j + 1) * num_target_wires - (j) * num_target_wires,
(j + 2 ** (ind + 1)) * num_target_wires - (j + 2**ind) * num_target_wires,
)
if num_swaps > 1:
swaps = {qml_ops.SWAP: num_swaps}
swap_resources[
controlled_resource_rep(
base_class=qml_ops.op_math.Prod,
base_params={"resources": swaps},
num_control_wires=1,
)
] += 1
else:
swap_resources[
controlled_resource_rep(
base_class=qml_ops.SWAP,
base_params={},
num_control_wires=1,
)
] += 1

if not clean or depth == 1:
resources = swap_resources
resources.update(select_ops)
return resources

resources = {}

hadamard_ops = {qml_ops.Hadamard: num_target_wires}

for key, val in swap_resources.items():
swap_resources[key] = val * 2

resources.update(hadamard_ops)
resources.update(swap_resources)
resources.update(select_ops)

for key, val in resources.items():
resources[key] = val * 2

return resources


@register_resources(_qrom_decomposition_resources)
def _qrom_decomposition(
wires, bitstrings, control_wires, target_wires, work_wires, clean
): # pylint: disable=unused-argument, too-many-arguments
if len(control_wires) == 0:
for bits in bitstrings:
BasisEmbedding(int(bits, 2), wires=target_wires)

swap_wires = target_wires + work_wires

# number of operators we store per column (power of 2)
depth = len(swap_wires) // len(target_wires)
depth = int(2 ** np.floor(np.log2(depth)))
depth = min(depth, len(bitstrings))

def _new_ops(depth_new, target_wires_new):

with QueuingManager.stop_recording():
ops_new = [BasisEmbedding(int(bits, 2), wires=target_wires) for bits in bitstrings]
ops_identity_new = ops_new + [qml_ops.I(target_wires)] * int(
2 ** len(control_wires) - len(ops_new)
)

n_columns = (
len(bitstrings) // depth_new
if len(bitstrings) % depth_new == 0
else len(bitstrings) // depth_new + 1
)
new_ops = []
for i in range(n_columns):
column_ops = []
for j in range(depth_new):
dic_map = {
ops_identity_new[i * depth_new + j].wires[l]: swap_wires[
j * len(target_wires_new) + l
]
for l in range(len(target_wires))
}
column_ops.append(ops_identity_new[i * depth_new + j].map_wires(dic_map))
new_ops.append(qml_ops.prod(*column_ops))
return new_ops

def _select_ops(control_wires_select, depth_select, target_wires_select):
n_control_select_wires = int(
math.ceil(math.log2(2 ** len(control_wires_select) / depth_select))
)
control_select_wires = control_wires_select[:n_control_select_wires]

if control_select_wires:
Select(
_new_ops(depth_select, target_wires_select),
control=control_select_wires,
)
else:
_new_ops(depth_select, target_wires_select)

def _swap_ops(control_wires_swap, depth_swap, swap_wires_swap, target_wires_swap):
n_control_select_wires = int(
math.ceil(math.log2(2 ** len(control_wires_swap) / depth_swap))
)
control_swap_wires = control_wires_swap[n_control_select_wires:]
for ind in range(len(control_swap_wires)):
for j in range(2**ind):
new_op = qml_ops.prod(_multi_swap)(
swap_wires_swap[
(j) * len(target_wires_swap) : (j + 1) * len(target_wires_swap)
],
swap_wires_swap[
(j + 2**ind)
* len(target_wires_swap) : (j + 2 ** (ind + 1))
* len(target_wires_swap)
],
)
qml_ops.ctrl(new_op, control=control_swap_wires[-ind - 1])

if not clean or depth == 1:
_select_ops(control_wires, depth, target_wires)
_swap_ops(control_wires, depth, swap_wires, target_wires)

else:
for _ in range(2):
for w in target_wires:
qml_ops.Hadamard(wires=w)
_swap_ops(control_wires, depth, swap_wires, target_wires)
_select_ops(control_wires, depth, target_wires)
_swap_ops(control_wires, depth, swap_wires, target_wires)


add_decomps(QROM, _qrom_decomposition)
23 changes: 23 additions & 0 deletions tests/templates/test_subroutines/test_qrom.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import pennylane as qml
from pennylane import numpy as np
from pennylane.ops.functions.assert_valid import _test_decomposition_rule


def test_assert_valid_qrom():
Expand Down Expand Up @@ -192,6 +193,28 @@ def test_decomposition(self):
for op1, op2 in zip(qrom_decomposition, expected_gates):
qml.assert_equal(op1, op2)

@pytest.mark.parametrize(
("bitstrings", "control_wires", "target_wires", "work_wires", "clean"),
[
(["1", "0", "0", "1"], [0, 1], [2], [3], True),
(["10", "00", "00", "01", "01", "00", "00", "01"], [0, 1, 2], [3, 4], [5], False),
(["01", "00", "00", "10", "10", "00", "00", "01"], [0, 1, 2], [3, 4], [5], True),
(["1", "0", "0", "1"], [0, 1], [2], [], False),
(["1", "0", "0", "1"], [0, 1], [2], [3, 4], False),
], # pylint: disable=too-many-arguments
)
def test_decomposition_new(self, bitstrings, control_wires, target_wires, work_wires, clean):
"""Tests the decomposition rule implemented with the new system."""
op = qml.QROM(
bitstrings,
control_wires=control_wires,
target_wires=target_wires,
work_wires=work_wires,
clean=clean,
)
for rule in qml.list_decomps(qml.QROM):
_test_decomposition_rule(op, rule)

def test_zero_control_wires(self):
"""Test that the edge case of zero control wires works"""

Expand Down
Loading