Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 12 additions & 8 deletions doc/releases/changelog-0.43.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -530,15 +530,15 @@
of equivalencies to compile certain patterns to efficient Clifford+T equivalents.
[(#7748)](https://github.com/PennyLaneAI/pennylane/pull/7748)

* New intermediate representations (IRs) :func:`~transforms.parity_matrix` and
:func:`~transforms.phase_polynomial` are available in PennyLane. These IRs are
used in compilation passes to optimize CNOT and phase polynomial circuits, respectively.
* New intermediate representations (IRs) :func:`~transforms.parity_matrix` and
:func:`~transforms.phase_polynomial` are available in PennyLane. These IRs are
used in compilation passes to optimize CNOT and phase polynomial circuits, respectively.
Also added :func:`~transforms.rowcol`, which uses the parity matrix as its IR for CNOT
routing under constraint connectivity.
[(#8171)](https://github.com/PennyLaneAI/pennylane/pull/8171)

* A new transform :func:`~.transforms.rz_phase_gradient` lets you realize arbitrary angle :class:`~.RZ` rotations
with a phase gradient resource state and semi-in-place addition (:class:`~.SemiAdder`). This can be a crucial
with a phase gradient resource state and semi-in-place addition (:class:`~.SemiAdder`). This can be a crucial
subroutine in FTQC when sufficient auxiliary wires are available, as it saves on T gates compared to other
discretization schemes.
[(#8213)](https://github.com/PennyLaneAI/pennylane/pull/8213)
Expand Down Expand Up @@ -643,7 +643,7 @@

* :func:`pennylane.snapshots` can now be used with `mcm_method="one-shot"` and `mcm_method="tree-traversal"`.
[(#8140)](https://github.com/PennyLaneAI/pennylane/pull/8140)

* An error is no longer raised when non-integer wire labels are used in QNodes using `mcm_method="deferred"`.
[(#7934)](https://github.com/PennyLaneAI/pennylane/pull/7934)

Expand Down Expand Up @@ -1183,8 +1183,8 @@
* Removes excess copies in `QuantumScript.copy`, and some other performance improvements to `resolve_dynamic_wires`.
[(#8406)](https://github.com/PennyLaneAI/pennylane/pull/8406)

* GitHub actions and workflows (`interface-unit-tests.yml`, `tests-labs.yml`, `unit-test.yml`, `upload-nightly-release.yml` and `upload.yml`) have been updated to
use `ubuntu-24.04` runners.
* GitHub actions and workflows (`interface-unit-tests.yml`, `tests-labs.yml`, `unit-test.yml`, `upload-nightly-release.yml` and `upload.yml`) have been updated to
use `ubuntu-24.04` runners.
[(#8371)](https://github.com/PennyLaneAI/pennylane/pull/8371)

* Issues with imports and circular dependencies in files were addressed in the ``ops/``and ``templates/`` directories.
Expand All @@ -1196,7 +1196,7 @@
* Enforce various modules to follow modular architecture via `tach`.
[(#7847)](https://github.com/PennyLaneAI/pennylane/pull/7847)

* Add CI workflow to test documentation using `sybil`.
* Add CI workflow to test documentation using `sybil`.
[(#8324)](https://github.com/PennyLaneAI/pennylane/pull/8324)
[(#8328)](https://github.com/PennyLaneAI/pennylane/pull/8328)
[(#8329)](https://github.com/PennyLaneAI/pennylane/pull/8329)
Expand Down Expand Up @@ -1464,6 +1464,10 @@

<h3>Bug fixes 🐛</h3>

* `qml.compiler.python_compiler.transforms.MergeRotationsPass` now takes the `adjoint` property of
merged operations correctly into account.
[(#8429)](https://github.com/PennyLaneAI/pennylane/pull/8429)

* Stops promoting numpy data to autograd in `qml.qchem.molecular_hamiltonian`.
[(#8410)](https://github.com/PennyLaneAI/pennylane/pull/8410)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,16 @@
"ControlledPhaseShift",
"IsingXX",
"IsingYY",
"IsingXY",
"IsingZZ",
"MultiRZ",
"SingleExcitation",
"DoubleExcitation",
"SingleExcitationMinus",
"SingleExcitationPlus",
"DoubleExcitationMinus",
"DoubleExcitationPlus",
"OrbitalRotation",
]


Expand Down Expand Up @@ -74,33 +83,42 @@ def match_and_rewrite(self, funcOp: func.FuncOp, rewriter: pattern_rewriter.Patt

param = op.operands[0]
while True:
next_user = None
for use in op.results[0].uses:
user = use.operation
if _can_merge(op, user):
next_user = user
break

if next_user is None:
else:
# No next_user was set because no user could be merged. Go to next op
break

for q1, q2 in zip(op.in_qubits, op.out_qubits, strict=True):
rewriter.replace_all_uses_with(q2, q1)
for cq1, cq2 in zip(op.in_ctrl_qubits, op.out_ctrl_qubits, strict=True):
rewriter.replace_all_uses_with(cq2, cq1)

# Whether op is adjoint will determine adjoint of new_op, and how to combine angles
is_adjoint = getattr(op, "adjoint", False)
rewriter.erase_op(op)

next_param = next_user.operands[0]
addOp = arith.AddfOp(param, next_param)
rewriter.insert_op(addOp, InsertPoint.before(next_user))
param = addOp.result
next_is_adjoint = getattr(next_user, "adjoint", False)
if is_adjoint == next_is_adjoint:
# If both op and next_user are adjoint, or neither is, we add angles
combOp = arith.AddfOp(param, next_param)
else:
# If one of op and next_user is adjoint, we subtract angles
combOp = arith.SubfOp(param, next_param)
rewriter.insert_op(combOp, InsertPoint.before(next_user))
param = combOp.result

new_op = CustomOp(
in_qubits=next_user.in_qubits,
gate_name=next_user.gate_name,
params=(param,),
in_ctrl_qubits=next_user.in_ctrl_qubits,
in_ctrl_values=next_user.in_ctrl_values,
adjoint=getattr(next_user, "adjoint", False),
adjoint=is_adjoint,
)
rewriter.replace_op(next_user, new_op)
op = new_op
Expand Down
8 changes: 8 additions & 0 deletions pennylane/ops/qubit/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,16 @@ def __contains__(self, obj):
"ControlledPhaseShift",
"IsingXX",
"IsingYY",
"IsingXY",
"IsingZZ",
"Rot",
"SingleExcitation",
"DoubleExcitation",
"SingleExcitationMinus",
"SingleExcitationPlus",
"DoubleExcitationMinus",
"DoubleExcitationPlus",
"OrbitalRotation",
]
)
"""Attribute: Operations for which composing multiple copies of the operation results in an
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
class TestMergeRotationsPass:
"""Unit tests for MergeRotationsPass."""

pipeline = (MergeRotationsPass(),)

def test_no_composable_ops(self, run_filecheck):
"""Test that nothing changes when there are no composable gates."""
program = """
Expand All @@ -43,8 +45,7 @@ def test_no_composable_ops(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_composable_ops(self, run_filecheck):
"""Test that composable gates are merged."""
Expand All @@ -61,8 +62,7 @@ def test_composable_ops(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_many_composable_ops(self, run_filecheck):
"""Test that more than 2 composable ops are merged correctly."""
Expand All @@ -83,8 +83,7 @@ def test_many_composable_ops(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_non_consecutive_composable_ops(self, run_filecheck):
"""Test that non-consecutive composable gates are not merged."""
Expand All @@ -102,8 +101,7 @@ def test_non_consecutive_composable_ops(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_composable_ops_different_qubits(self, run_filecheck):
"""Test that composable gates on different qubits are not merged."""
Expand All @@ -121,8 +119,7 @@ def test_composable_ops_different_qubits(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_controlled_composable_ops(self, run_filecheck):
"""Test that controlled composable ops can be merged."""
Expand All @@ -144,8 +141,7 @@ def test_controlled_composable_ops(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_controlled_composable_ops_same_control_values(self, run_filecheck):
"""Test that controlled composable ops with the same control values
Expand All @@ -169,8 +165,7 @@ def test_controlled_composable_ops_same_control_values(self, run_filecheck):
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

def test_controlled_composable_ops_different_control_values(self, run_filecheck):
"""Test that controlled composable ops with different control values
Expand All @@ -193,8 +188,36 @@ def test_controlled_composable_ops_different_control_values(self, run_filecheck)
}
"""

pipeline = (MergeRotationsPass(),)
run_filecheck(program, pipeline)
run_filecheck(program, self.pipeline)

@pytest.mark.parametrize(
"first_adj, second_adj, sign",
[
(False, False, "+"),
(False, True, "-"),
(True, False, "-"),
(True, True, "+"),
],
)
def test_adjoint_property(self, first_adj, second_adj, sign, run_filecheck):
"""Test that composable ops with and without adjoint property are merged correctly."""
adj_string_0 = "adj" if first_adj else ""
adj_string_1 = "adj" if second_adj else ""
arith_op_str = "arith.addf" if sign == "+" else "arith.subf"
program = f"""
func.func @test_func(%arg0: f64, %arg1: f64) {{
// CHECK: [[q0:%.+]] = "test.op"() : () -> !quantum.bit
%0 = "test.op"() : () -> !quantum.bit
// CHECK: [[new_angle:%.+]] = {arith_op_str} %arg0, %arg1 : f64
// CHECK: [[q1:%.+]] = quantum.custom "RX"([[new_angle]]) [[q0]] {adj_string_0} : !quantum.bit
// CHECK-NOT: "quantum.custom"
%1 = quantum.custom "RX"(%arg0) %0 {adj_string_0}: !quantum.bit
%2 = quantum.custom "RX"(%arg1) %1 {adj_string_1}: !quantum.bit
return
}}
"""

run_filecheck(program, self.pipeline)


# pylint: disable=too-few-public-methods
Expand All @@ -204,7 +227,7 @@ class TestMergeRotationsIntegration:

def test_qjit(self, run_filecheck_qjit):
"""Test that the MergeRotationsPass works correctly with qjit."""
dev = qml.device("lightning.qubit", wires=2)
dev = qml.device("lightning.qubit", wires=1)

@qml.qjit(target="mlir", pass_plugins=[getXDSLPluginAbsolutePath()])
@merge_rotations_pass
Expand Down