From 97b75dbe4e94b5cc1a8697acfda953fc054282fe Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 18 Oct 2024 11:50:18 -0400 Subject: [PATCH 001/335] Feature branch for experimental resource estimation in labs --- pennylane/labs/resource_estimation/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pennylane/labs/resource_estimation/__init__.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py new file mode 100644 index 00000000000..30ae22fc947 --- /dev/null +++ b/pennylane/labs/resource_estimation/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""This module contains experimental resource estimation functionality. """ From e3e4910837d35911a6178e6521b8045050529f01 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 22 Oct 2024 08:36:04 -0400 Subject: [PATCH 002/335] adding Resources and OpTypeWithParams to labs --- pennylane/labs/__init__.py | 2 + .../labs/resource_estimation/__init__.py | 2 + .../resource_estimation/resource_contianer.py | 111 +++++++++++++++ .../test_resource_contianer.py | 129 ++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 pennylane/labs/resource_estimation/resource_contianer.py create mode 100644 pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 92eb83a2c99..5dd984bbb75 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -33,4 +33,6 @@ """ +# from .resource_estimation import * + __all__ = [] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 30ae22fc947..f56e8b6cd33 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -12,3 +12,5 @@ # See the License for the specific language governing permissions and # limitations under the License. r"""This module contains experimental resource estimation functionality. """ + +from .resource_contianer import Resources, OpTypeWithParams \ No newline at end of file diff --git a/pennylane/labs/resource_estimation/resource_contianer.py b/pennylane/labs/resource_estimation/resource_contianer.py new file mode 100644 index 00000000000..1b4bdaf29d1 --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_contianer.py @@ -0,0 +1,111 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Base classes for the resource objects and resource estimation.""" +from collections import defaultdict +from dataclasses import dataclass, field + + +class OpTypeWithParams: + + def __init__(self, op_type: str, params_tuple: tuple) -> None: + r"""Instantiate the light weight class corressponding to the + Operator type and parameters. + + Args: + op_type (str): the name of the operation + params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal + pairs of parameter names and values required to compute the resources for the given operator! + + .. details:: + + This representation is the minimal amount of information required to estimate resources for the operator. + + **Example** + + >>> op_tp = OpTypeWithParams("Hadamard", (("num_wires", 1),)) + >>> print(op_tp) + Hadamard(num_wires=1) + + >>> op_tp = OpTypeWithParams( + "QSVT", + ( + ("num_wires", 5), + ("num_angles", 100), + ), + ) + >>> print(op_tp) + QSVT(num_wires=5, num_angles=100) + """ + self.op_type = op_type + self.params = params_tuple + + def __repr__(self) -> str: + op_type_str = self.op_type + "(" + params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" + + return op_type_str + params_str + + +@dataclass +class Resources: + r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. + + Args: + num_wires (int): number of qubits + num_gates (int): number of gates + gate_types (dict): dictionary storing operations (~.OpTypeWithParams) + as keys and the number of times they are used in the circuit (int) as values + + .. details:: + + The resources being tracked can be accessed as class attributes. + Additionally, the :code:`Resources` instance can be nicely displayed in the console. + + **Example** + + >>> r = Resources( + ... num_wires=2, + ... num_gates=2, + ... gate_types={ + ... OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, + ... OpTypeWithParams("CNOT", (("num_wires", 2),)):1, + ... } + ... ) + >>> print(r) + wires: 2 + gates: 2 + gate_types: + {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1} + """ + num_wires: int = 0 + num_gates: int = 0 + gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) + + def __str__(self): + keys = ["wires", "gates"] + vals = [self.num_wires, self.num_gates] + items = "\n".join([str(i) for i in zip(keys, vals)]) + items = items.replace("('", "") + items = items.replace("',", ":") + items = items.replace(")", "") + + gate_type_str = ", ".join( + [f"'{str(gate_name)}': {count}" for gate_name, count in self.gate_types.items()] + ) + items += "\ngate_types:\n{" + gate_type_str + "}" + return items + + def _ipython_display_(self): + """Displays __str__ in ipython instead of __repr__""" + print(str(self)) diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py new file mode 100644 index 00000000000..85379368d23 --- /dev/null +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py @@ -0,0 +1,129 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Test base Resource class and its associated methods +""" + +import pytest +from collections import defaultdict + +from pennylane.labs.resource_estimation import Resources, OpTypeWithParams + + +class TestResources: + """Test the methods and attributes of the Resource class""" + + resource_quantities = ( + Resources(), + Resources(5, 0, defaultdict(int)), + Resources( + 1, 3, defaultdict( + int, + {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("PauliZ", (("num_wires", 1),)): 2}, + ) + ), + Resources( + 4, 2, defaultdict( + int, + {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1}, + ) + ), + ) + + resource_parameters = ( + (0, 0, defaultdict(int)), + (5, 0, defaultdict(int)), + (1, 3, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("PauliZ", (("num_wires", 1),)): 2})), + (4, 2, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})), + ) + + @pytest.mark.parametrize("r, attribute_tup", zip(resource_quantities, resource_parameters)) + def test_init(self, r, attribute_tup): + """Test that the Resource class is instantiated as expected.""" + num_wires, num_gates, gate_types = attribute_tup + + assert r.num_wires == num_wires + assert r.num_gates == num_gates + + for key, value in gate_types.items(): + assert r.gate_types[key] == value + # assert r.gate_types == gate_types + + test_str_data = ( + ( + "wires: 0\n" + + "gates: 0\n" + + "gate_types:\n" + + "{}" + ), + ( + "wires: 5\n" + + "gates: 0\n" + + "gate_types:\n" + + "{}" + ), + ( + "wires: 1\n" + + "gates: 3\n" + + "gate_types:\n" + + "{'Hadamard(num_wires=1)': 1, 'PauliZ(num_wires=1)': 2}" + ), + ( + "wires: 4\n" + + "gates: 2\n" + + "gate_types:\n" + + "{'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1}" + ), + ) + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) + def test_str(self, r, rep): + """Test the string representation of a Resources instance.""" + assert str(r) == rep + + test_rep_data = ( + "Resources(num_wires=0, num_gates=0, gate_types=defaultdict(, {}))", + "Resources(num_wires=5, num_gates=0, gate_types=defaultdict(, {}))", + "Resources(num_wires=1, num_gates=3, gate_types=defaultdict(, {'Hadamard(num_wires=1)': 1, 'PauliZ(num_wires=1)': 2}))", + "Resources(num_wires=4, num_gates=2, gate_types=defaultdict(, {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1}))", + ) + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_rep_data)) + def test_repr(self, r, rep): + """Test the repr method of a Resources instance looks as expected.""" + assert repr(r) == rep + + def test_eq(self): + """Test that the equality dunder method is correct for Resources.""" + r1 = Resources(4, 2, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) + r2 = Resources(4, 2, defaultdict(int, {OpTypeWithParams("CNOT", (("num_wires", 2),)): 1, OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1})) # all equal + + r3 = Resources(1, 2, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) # diff wires + r4 = Resources(4, 1, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) # diff num_gates + r5 = Resources(4, 2, defaultdict(int, {OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) # diff gate_types + + + assert r1.__eq__(r1) + assert r1.__eq__(r2) + + assert not r1.__eq__(r3) + assert not r1.__eq__(r4) + assert not r1.__eq__(r5) + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) + def test_ipython_display(self, r, rep, capsys): + """Test that the ipython display prints the string representation of a Resources instance.""" + r._ipython_display_() # pylint: disable=protected-access + captured = capsys.readouterr() + assert rep in captured.out \ No newline at end of file From 9a30b224338cba3ef49f30d48987a1543ea36a6a Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 22 Oct 2024 13:22:58 -0400 Subject: [PATCH 003/335] fixing typo in file name --- .../resource_estimation/resource_container.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 pennylane/labs/resource_estimation/resource_container.py diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py new file mode 100644 index 00000000000..1b4bdaf29d1 --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -0,0 +1,111 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Base classes for the resource objects and resource estimation.""" +from collections import defaultdict +from dataclasses import dataclass, field + + +class OpTypeWithParams: + + def __init__(self, op_type: str, params_tuple: tuple) -> None: + r"""Instantiate the light weight class corressponding to the + Operator type and parameters. + + Args: + op_type (str): the name of the operation + params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal + pairs of parameter names and values required to compute the resources for the given operator! + + .. details:: + + This representation is the minimal amount of information required to estimate resources for the operator. + + **Example** + + >>> op_tp = OpTypeWithParams("Hadamard", (("num_wires", 1),)) + >>> print(op_tp) + Hadamard(num_wires=1) + + >>> op_tp = OpTypeWithParams( + "QSVT", + ( + ("num_wires", 5), + ("num_angles", 100), + ), + ) + >>> print(op_tp) + QSVT(num_wires=5, num_angles=100) + """ + self.op_type = op_type + self.params = params_tuple + + def __repr__(self) -> str: + op_type_str = self.op_type + "(" + params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" + + return op_type_str + params_str + + +@dataclass +class Resources: + r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. + + Args: + num_wires (int): number of qubits + num_gates (int): number of gates + gate_types (dict): dictionary storing operations (~.OpTypeWithParams) + as keys and the number of times they are used in the circuit (int) as values + + .. details:: + + The resources being tracked can be accessed as class attributes. + Additionally, the :code:`Resources` instance can be nicely displayed in the console. + + **Example** + + >>> r = Resources( + ... num_wires=2, + ... num_gates=2, + ... gate_types={ + ... OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, + ... OpTypeWithParams("CNOT", (("num_wires", 2),)):1, + ... } + ... ) + >>> print(r) + wires: 2 + gates: 2 + gate_types: + {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1} + """ + num_wires: int = 0 + num_gates: int = 0 + gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) + + def __str__(self): + keys = ["wires", "gates"] + vals = [self.num_wires, self.num_gates] + items = "\n".join([str(i) for i in zip(keys, vals)]) + items = items.replace("('", "") + items = items.replace("',", ":") + items = items.replace(")", "") + + gate_type_str = ", ".join( + [f"'{str(gate_name)}': {count}" for gate_name, count in self.gate_types.items()] + ) + items += "\ngate_types:\n{" + gate_type_str + "}" + return items + + def _ipython_display_(self): + """Displays __str__ in ipython instead of __repr__""" + print(str(self)) From d252672e4918da98c16c7e3d1b82d535b4a98c0e Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 22 Oct 2024 13:24:38 -0400 Subject: [PATCH 004/335] removing old file --- .../resource_estimation/resource_contianer.py | 111 ------------------ 1 file changed, 111 deletions(-) delete mode 100644 pennylane/labs/resource_estimation/resource_contianer.py diff --git a/pennylane/labs/resource_estimation/resource_contianer.py b/pennylane/labs/resource_estimation/resource_contianer.py deleted file mode 100644 index 1b4bdaf29d1..00000000000 --- a/pennylane/labs/resource_estimation/resource_contianer.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -r"""Base classes for the resource objects and resource estimation.""" -from collections import defaultdict -from dataclasses import dataclass, field - - -class OpTypeWithParams: - - def __init__(self, op_type: str, params_tuple: tuple) -> None: - r"""Instantiate the light weight class corressponding to the - Operator type and parameters. - - Args: - op_type (str): the name of the operation - params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal - pairs of parameter names and values required to compute the resources for the given operator! - - .. details:: - - This representation is the minimal amount of information required to estimate resources for the operator. - - **Example** - - >>> op_tp = OpTypeWithParams("Hadamard", (("num_wires", 1),)) - >>> print(op_tp) - Hadamard(num_wires=1) - - >>> op_tp = OpTypeWithParams( - "QSVT", - ( - ("num_wires", 5), - ("num_angles", 100), - ), - ) - >>> print(op_tp) - QSVT(num_wires=5, num_angles=100) - """ - self.op_type = op_type - self.params = params_tuple - - def __repr__(self) -> str: - op_type_str = self.op_type + "(" - params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" - - return op_type_str + params_str - - -@dataclass -class Resources: - r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. - - Args: - num_wires (int): number of qubits - num_gates (int): number of gates - gate_types (dict): dictionary storing operations (~.OpTypeWithParams) - as keys and the number of times they are used in the circuit (int) as values - - .. details:: - - The resources being tracked can be accessed as class attributes. - Additionally, the :code:`Resources` instance can be nicely displayed in the console. - - **Example** - - >>> r = Resources( - ... num_wires=2, - ... num_gates=2, - ... gate_types={ - ... OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, - ... OpTypeWithParams("CNOT", (("num_wires", 2),)):1, - ... } - ... ) - >>> print(r) - wires: 2 - gates: 2 - gate_types: - {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1} - """ - num_wires: int = 0 - num_gates: int = 0 - gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) - - def __str__(self): - keys = ["wires", "gates"] - vals = [self.num_wires, self.num_gates] - items = "\n".join([str(i) for i in zip(keys, vals)]) - items = items.replace("('", "") - items = items.replace("',", ":") - items = items.replace(")", "") - - gate_type_str = ", ".join( - [f"'{str(gate_name)}': {count}" for gate_name, count in self.gate_types.items()] - ) - items += "\ngate_types:\n{" + gate_type_str + "}" - return items - - def _ipython_display_(self): - """Displays __str__ in ipython instead of __repr__""" - print(str(self)) From cb05ed5d492922b1b2c0641f379b68ebdc527fb7 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 22 Oct 2024 13:44:09 -0400 Subject: [PATCH 005/335] fixing typo --- pennylane/labs/resource_estimation/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index f56e8b6cd33..b5194d7b23b 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,4 +13,4 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .resource_contianer import Resources, OpTypeWithParams \ No newline at end of file +from .resource_container import Resources, OpTypeWithParams From 8ba8e26548e673ea2267dd18f0b02b4cf365991c Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 22 Oct 2024 16:38:37 -0400 Subject: [PATCH 006/335] rework OpTypeWithParams and Resources object --- .../resource_estimation/resource_container.py | 79 +++---------- .../resource_estimation/resources_base.py | 70 +++++++++++ .../test_resource_contianer.py | 110 +----------------- 3 files changed, 85 insertions(+), 174 deletions(-) create mode 100644 pennylane/labs/resource_estimation/resources_base.py diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 1b4bdaf29d1..8dae1e7c546 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -12,18 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. r"""Base classes for the resource objects and resource estimation.""" -from collections import defaultdict -from dataclasses import dataclass, field -class OpTypeWithParams: +class CompressedResourceOp: - def __init__(self, op_type: str, params_tuple: tuple) -> None: - r"""Instantiate the light weight class corressponding to the - Operator type and parameters. + def __init__(self, op_type: type, params_tuple: tuple) -> None: + r"""Instantiate the light weight class corressponding to the operator type and parameters. Args: - op_type (str): the name of the operation + op_type (Type): the PennyLane type of the operation params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal pairs of parameter names and values required to compute the resources for the given operator! @@ -33,12 +30,12 @@ def __init__(self, op_type: str, params_tuple: tuple) -> None: **Example** - >>> op_tp = OpTypeWithParams("Hadamard", (("num_wires", 1),)) + >>> op_tp = CompressedResourceOp(qml.Hadamard, (("num_wires", 1),)) >>> print(op_tp) Hadamard(num_wires=1) - >>> op_tp = OpTypeWithParams( - "QSVT", + >>> op_tp = CompressedResourceOp( + qml.QSVT, ( ("num_wires", 5), ("num_angles", 100), @@ -50,62 +47,14 @@ def __init__(self, op_type: str, params_tuple: tuple) -> None: self.op_type = op_type self.params = params_tuple + def __hash__(self) -> int: + return hash((self.op_type.__name__, self.params)) + + def __eq__(self, other: object) -> bool: + return (self.op_type == other.op_type) and (dict(self.params) == dict(other.params)) + def __repr__(self) -> str: - op_type_str = self.op_type + "(" + op_type_str = self.op_type.__name__ + "(" params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" return op_type_str + params_str - - -@dataclass -class Resources: - r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. - - Args: - num_wires (int): number of qubits - num_gates (int): number of gates - gate_types (dict): dictionary storing operations (~.OpTypeWithParams) - as keys and the number of times they are used in the circuit (int) as values - - .. details:: - - The resources being tracked can be accessed as class attributes. - Additionally, the :code:`Resources` instance can be nicely displayed in the console. - - **Example** - - >>> r = Resources( - ... num_wires=2, - ... num_gates=2, - ... gate_types={ - ... OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, - ... OpTypeWithParams("CNOT", (("num_wires", 2),)):1, - ... } - ... ) - >>> print(r) - wires: 2 - gates: 2 - gate_types: - {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1} - """ - num_wires: int = 0 - num_gates: int = 0 - gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) - - def __str__(self): - keys = ["wires", "gates"] - vals = [self.num_wires, self.num_gates] - items = "\n".join([str(i) for i in zip(keys, vals)]) - items = items.replace("('", "") - items = items.replace("',", ":") - items = items.replace(")", "") - - gate_type_str = ", ".join( - [f"'{str(gate_name)}': {count}" for gate_name, count in self.gate_types.items()] - ) - items += "\ngate_types:\n{" + gate_type_str + "}" - return items - - def _ipython_display_(self): - """Displays __str__ in ipython instead of __repr__""" - print(str(self)) diff --git a/pennylane/labs/resource_estimation/resources_base.py b/pennylane/labs/resource_estimation/resources_base.py new file mode 100644 index 00000000000..c6db63e446b --- /dev/null +++ b/pennylane/labs/resource_estimation/resources_base.py @@ -0,0 +1,70 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Base classes for the resources object.""" +from collections import defaultdict +from dataclasses import dataclass, field + + +@dataclass +class Resources: + r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. + + Args: + num_wires (int): number of qubits + num_gates (int): number of gates + gate_types (dict): dictionary storing operations (~.CompressedResourceOp) + as keys and the number of times they are used in the circuit (int) as values + + .. details:: + + The resources being tracked can be accessed as class attributes. + Additionally, the :code:`Resources` instance can be nicely displayed in the console. + + **Example** + + >>> r = Resources( + ... num_wires=2, + ... num_gates=2, + ... gate_types={ + ... OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, + ... OpTypeWithParams("CNOT", (("num_wires", 2),)):1, + ... } + ... ) + >>> print(r) + wires: 2 + gates: 2 + gate_types: + {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1} + """ + num_wires: int = 0 + num_gates: int = 0 + gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) + + def __str__(self): + keys = ["wires", "gates"] + vals = [self.num_wires, self.num_gates] + items = "\n".join([str(i) for i in zip(keys, vals)]) + items = items.replace("('", "") + items = items.replace("',", ":") + items = items.replace(")", "") + + gate_type_str = ", ".join( + [f"'{str(gate_name)}': {count}" for gate_name, count in self.gate_types.items()] + ) + items += "\ngate_types:\n{" + gate_type_str + "}" + return items + + def _ipython_display_(self): + """Displays __str__ in ipython instead of __repr__""" + print(str(self)) \ No newline at end of file diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py index 85379368d23..12b3cdb1c86 100644 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py @@ -18,112 +18,4 @@ import pytest from collections import defaultdict -from pennylane.labs.resource_estimation import Resources, OpTypeWithParams - - -class TestResources: - """Test the methods and attributes of the Resource class""" - - resource_quantities = ( - Resources(), - Resources(5, 0, defaultdict(int)), - Resources( - 1, 3, defaultdict( - int, - {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("PauliZ", (("num_wires", 1),)): 2}, - ) - ), - Resources( - 4, 2, defaultdict( - int, - {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1}, - ) - ), - ) - - resource_parameters = ( - (0, 0, defaultdict(int)), - (5, 0, defaultdict(int)), - (1, 3, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("PauliZ", (("num_wires", 1),)): 2})), - (4, 2, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})), - ) - - @pytest.mark.parametrize("r, attribute_tup", zip(resource_quantities, resource_parameters)) - def test_init(self, r, attribute_tup): - """Test that the Resource class is instantiated as expected.""" - num_wires, num_gates, gate_types = attribute_tup - - assert r.num_wires == num_wires - assert r.num_gates == num_gates - - for key, value in gate_types.items(): - assert r.gate_types[key] == value - # assert r.gate_types == gate_types - - test_str_data = ( - ( - "wires: 0\n" - + "gates: 0\n" - + "gate_types:\n" - + "{}" - ), - ( - "wires: 5\n" - + "gates: 0\n" - + "gate_types:\n" - + "{}" - ), - ( - "wires: 1\n" - + "gates: 3\n" - + "gate_types:\n" - + "{'Hadamard(num_wires=1)': 1, 'PauliZ(num_wires=1)': 2}" - ), - ( - "wires: 4\n" - + "gates: 2\n" - + "gate_types:\n" - + "{'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1}" - ), - ) - - @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) - def test_str(self, r, rep): - """Test the string representation of a Resources instance.""" - assert str(r) == rep - - test_rep_data = ( - "Resources(num_wires=0, num_gates=0, gate_types=defaultdict(, {}))", - "Resources(num_wires=5, num_gates=0, gate_types=defaultdict(, {}))", - "Resources(num_wires=1, num_gates=3, gate_types=defaultdict(, {'Hadamard(num_wires=1)': 1, 'PauliZ(num_wires=1)': 2}))", - "Resources(num_wires=4, num_gates=2, gate_types=defaultdict(, {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1}))", - ) - - @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_rep_data)) - def test_repr(self, r, rep): - """Test the repr method of a Resources instance looks as expected.""" - assert repr(r) == rep - - def test_eq(self): - """Test that the equality dunder method is correct for Resources.""" - r1 = Resources(4, 2, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) - r2 = Resources(4, 2, defaultdict(int, {OpTypeWithParams("CNOT", (("num_wires", 2),)): 1, OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1})) # all equal - - r3 = Resources(1, 2, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) # diff wires - r4 = Resources(4, 1, defaultdict(int, {OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) # diff num_gates - r5 = Resources(4, 2, defaultdict(int, {OpTypeWithParams("CNOT", (("num_wires", 2),)): 1})) # diff gate_types - - - assert r1.__eq__(r1) - assert r1.__eq__(r2) - - assert not r1.__eq__(r3) - assert not r1.__eq__(r4) - assert not r1.__eq__(r5) - - @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) - def test_ipython_display(self, r, rep, capsys): - """Test that the ipython display prints the string representation of a Resources instance.""" - r._ipython_display_() # pylint: disable=protected-access - captured = capsys.readouterr() - assert rep in captured.out \ No newline at end of file +from pennylane.labs.resource_estimation import CompressedResourceOp From 938c2e525c540ba696e7e21958b7c107deec1f81 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 23 Oct 2024 09:55:48 -0400 Subject: [PATCH 007/335] updating init file --- pennylane/labs/resource_estimation/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index b5194d7b23b..91ab79beda2 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,4 +13,9 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .resource_container import Resources, OpTypeWithParams +from .resource_constructor import ResourceConstructor +from .resource_container import CompressedResourceOp +from .resources_base import Resources + +# Resource Operators +from .resource_qft import ResourceQFT From 2701fe756719c594b82057d322d29d9504f1d55a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 23 Oct 2024 13:30:15 -0400 Subject: [PATCH 008/335] qft --- .../resource_constructor.py | 25 ++++++++++ .../labs/resource_estimation/resource_qft.py | 50 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 pennylane/labs/resource_estimation/resource_constructor.py create mode 100644 pennylane/labs/resource_estimation/resource_qft.py diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py new file mode 100644 index 00000000000..5c76847affa --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -0,0 +1,25 @@ +from abc import ABC, abstractmethod +from typing import Callable + +import pennylane.labs.resource_estimation.resources_base as resources_base +import pennylane.labs.resource_estimation.resource_container as resource_container + +CompressedResourceOp = resource_container.CompressedResourceOp +Resources = resources_base.Resources + +class ResourceConstructor(ABC): + + @staticmethod + @abstractmethod + def compute_resources(*args, **kwargs) -> Resources: + """Returns the Resource object associated with the Operator.""" + + @classmethod + def set_compute_resources(cls, new_func: Callable) -> None: + """Override the compute_resources method.""" + cls.compute_resources = new_func + + @abstractmethod + def resource_rep(self) -> CompressedResourceOp: + """Returns a compressed representation containing only the parameters of + the Operator that are needed to compute a resource estimation.""" diff --git a/pennylane/labs/resource_estimation/resource_qft.py b/pennylane/labs/resource_estimation/resource_qft.py new file mode 100644 index 00000000000..7edda246959 --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_qft.py @@ -0,0 +1,50 @@ +import pennylane as qml +from pennylane.labs.resource_estimation import ResourceConstructor, CompressedResourceOp + +#pylint: disable=too-many-ancestors,arguments-differ + +class ResourceQFT(qml.QFT, ResourceConstructor): + """Resource class for QFT""" + + @staticmethod + def compute_resources(num_wires) -> dict: + gate_types = {} + + hadamard = CompressedResourceOp(qml.Hadamard, ()) + swap = CompressedResourceOp(qml.SWAP, ()) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, ()) + + gate_types[hadamard] = num_wires + gate_types[swap] = num_wires // 2 + gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 + + return gate_types + + def resource_rep(self) -> CompressedResourceOp: + params = (('num_wires', len(self.wires)),) + return CompressedResourceOp(qml.QFT, params) + + +class ResourceControlledPhaseShift(qml.ControlledPhaseShift, ResourceConstructor): + """Resource class for ControlledPhaseShift""" + + @staticmethod + def compute_resources() -> dict: + gate_types = {} + + cnot = CompressedResourceOp(qml.CNOT, ()) + rz = CompressedResourceOp(qml.RZ, ()) + + gate_types[cnot] = 2 + gate_types[rz] = 3 + + return gate_types + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.ControlledPhaseShift, ()) + +class ResourceCNOT(qml.CNOT, ResourceConstructor): + """Resource class for CNOT""" + +class ResourceRZ(qml.RZ, ResourceConstructor): + """Resource class for RZ""" From 7f2f46a34eb1763035099fef531f53369ccd8333 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 23 Oct 2024 14:56:50 -0400 Subject: [PATCH 009/335] Adding tests --- .../labs/resource_estimation/__init__.py | 4 +- .../resource_estimation/resource_container.py | 5 ++- .../test_resource_contianer.py | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 91ab79beda2..f3705416e14 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,9 +13,9 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .resource_constructor import ResourceConstructor +# from .resource_constructor import ResourceConstructor from .resource_container import CompressedResourceOp from .resources_base import Resources # Resource Operators -from .resource_qft import ResourceQFT +# from .resource_qft import ResourceQFT diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 8dae1e7c546..5ba46b26f23 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -44,17 +44,18 @@ def __init__(self, op_type: type, params_tuple: tuple) -> None: >>> print(op_tp) QSVT(num_wires=5, num_angles=100) """ + self._name = op_type.__name__ self.op_type = op_type self.params = params_tuple def __hash__(self) -> int: - return hash((self.op_type.__name__, self.params)) + return hash((self._name, self.params)) def __eq__(self, other: object) -> bool: return (self.op_type == other.op_type) and (dict(self.params) == dict(other.params)) def __repr__(self) -> str: - op_type_str = self.op_type.__name__ + "(" + op_type_str = self._name + "(" params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" return op_type_str + params_str diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py index 12b3cdb1c86..772fca26179 100644 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py @@ -18,4 +18,44 @@ import pytest from collections import defaultdict +import pennylane as qml from pennylane.labs.resource_estimation import CompressedResourceOp + + +class TestCompressedResourceOp: + + hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0)@qml.Z(1)]) + compressed_op_args_lst = ( + ("X", qml.X, tuple({"num_wires": 1})), + ("QFT", qml.QFT, tuple({"num_wires": 5})), + ("QSVT", qml.QSVT, tuple({"num_wires": 3, "num_angles":5})), + (qml.TrotterProduct, tuple({"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2})) + ) + + @pytest.mark.parametrize("name, op_type, parameters", compressed_op_args_lst) + def test_init(self, name, op_type, parameters): + """Test that we can correctly instantiate CompressedResourceOp""" + cr_op = CompressedResourceOp(op_type, parameters) + + assert cr_op._name == name + assert cr_op.op_type is op_type + assert cr_op.params == parameters + + def test_hash(self): + """Test that the hash method behaves as expected""" + CmprssedQSVT1 = CompressedResourceOp(qml.QSVT, tuple({"num_wires": 3, "num_angles":5})) + CmprssedQSVT2 = CompressedResourceOp(qml.QSVT, tuple({"num_wires": 3, "num_angles":5})) + Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3})) + + assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) + assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) + assert hash(CmprssedQSVT1) != hash(Other) + + def test_equality(self): + """Test that the equality methods behaves as expected""" + + assert True + + def test_repr(self): + """Test that the repr method behaves as expected.""" + assert True \ No newline at end of file From 6b13419811e4717b6774dd39d641d49d34a6931a Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 24 Oct 2024 08:48:23 -0400 Subject: [PATCH 010/335] add tests and clean up --- .../labs/resource_estimation/__init__.py | 6 +- .../resource_estimation/resource_container.py | 104 +++++++++++-- .../resource_estimation/resources_base.py | 70 --------- .../test_resource_contianer.py | 144 ++++++++++++++++++ .../test_resource_contianer.py | 117 +++++++++++--- 5 files changed, 338 insertions(+), 103 deletions(-) delete mode 100644 pennylane/labs/resource_estimation/resources_base.py create mode 100644 pennylane/labs/tests/resource_estimation/test_resource_contianer.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index f3705416e14..c73465a6bcb 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -14,8 +14,4 @@ r"""This module contains experimental resource estimation functionality. """ # from .resource_constructor import ResourceConstructor -from .resource_container import CompressedResourceOp -from .resources_base import Resources - -# Resource Operators -# from .resource_qft import ResourceQFT +from .resource_container import CompressedResourceOp, Resources diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 5ba46b26f23..f070a8c1766 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -11,19 +11,48 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""Base classes for the resource objects and resource estimation.""" +r"""Base classes for resource estimation.""" +from collections import defaultdict +from dataclasses import dataclass, field class CompressedResourceOp: + r"""Instantiate the light weight class corressponding to the operator type and parameters. + + Args: + op_type (Type): the PennyLane type of the operation + params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal + pairs of parameter names and values required to compute the resources for the given operator! + + .. details:: + + This representation is the minimal amount of information required to estimate resources for the operator. + + **Example** + + >>> op_tp = CompressedResourceOp(qml.Hadamard, (("num_wires", 1),)) + >>> print(op_tp) + Hadamard(num_wires=1) + + >>> op_tp = CompressedResourceOp( + qml.QSVT, + ( + ("num_wires", 5), + ("num_angles", 100), + ), + ) + >>> print(op_tp) + QSVT(num_wires=5, num_angles=100) + """ def __init__(self, op_type: type, params_tuple: tuple) -> None: - r"""Instantiate the light weight class corressponding to the operator type and parameters. + r"""Instantiate the light weight class corressponding to the operator type and parameters. Args: - op_type (Type): the PennyLane type of the operation - params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal + op_type (Type): the PennyLane type of the operation + params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal pairs of parameter names and values required to compute the resources for the given operator! - + .. details:: This representation is the minimal amount of information required to estimate resources for the operator. @@ -33,11 +62,11 @@ def __init__(self, op_type: type, params_tuple: tuple) -> None: >>> op_tp = CompressedResourceOp(qml.Hadamard, (("num_wires", 1),)) >>> print(op_tp) Hadamard(num_wires=1) - + >>> op_tp = CompressedResourceOp( - qml.QSVT, + qml.QSVT, ( - ("num_wires", 5), + ("num_wires", 5), ("num_angles", 100), ), ) @@ -47,15 +76,68 @@ def __init__(self, op_type: type, params_tuple: tuple) -> None: self._name = op_type.__name__ self.op_type = op_type self.params = params_tuple - + def __hash__(self) -> int: return hash((self._name, self.params)) - + def __eq__(self, other: object) -> bool: return (self.op_type == other.op_type) and (dict(self.params) == dict(other.params)) - + def __repr__(self) -> str: op_type_str = self._name + "(" params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" return op_type_str + params_str + + +@dataclass +class Resources: + r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. + + Args: + num_wires (int): number of qubits + num_gates (int): number of gates + gate_types (dict): dictionary storing operation names (str) as keys + and the number of times they are used in the circuit (int) as values + + .. details:: + + The resources being tracked can be accessed as class attributes. + Additionally, the :code:`Resources` instance can be nicely displayed in the console. + + **Example** + + >>> r = Resources( + ... num_wires=2, + ... num_gates=2, + ... gate_types={"Hadamard": 1, "CNOT": 1} + ... ) + >>> print(r) + wires: 2 + gates: 2 + gate_types: + {'Hadamard': 1, 'CNOT': 1} + """ + + num_wires: int = 0 + num_gates: int = 0 + gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) + + def __str__(self): + """String representation of the Resources object.""" + keys = ["wires", "gates"] + vals = [self.num_wires, self.num_gates] + items = "\n".join([str(i) for i in zip(keys, vals)]) + items = items.replace("('", "") + items = items.replace("',", ":") + items = items.replace(")", "") + + gate_type_str = ", ".join( + [f"'{gate_name}': {count}" for gate_name, count in self.gate_types.items()] + ) + items += "\ngate_types:\n{" + gate_type_str + "}" + return items + + def _ipython_display_(self): + """Displays __str__ in ipython instead of __repr__""" + print(str(self)) diff --git a/pennylane/labs/resource_estimation/resources_base.py b/pennylane/labs/resource_estimation/resources_base.py deleted file mode 100644 index c6db63e446b..00000000000 --- a/pennylane/labs/resource_estimation/resources_base.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -r"""Base classes for the resources object.""" -from collections import defaultdict -from dataclasses import dataclass, field - - -@dataclass -class Resources: - r"""Contains attributes which store key resources such as number of gates, number of wires, and gate types. - - Args: - num_wires (int): number of qubits - num_gates (int): number of gates - gate_types (dict): dictionary storing operations (~.CompressedResourceOp) - as keys and the number of times they are used in the circuit (int) as values - - .. details:: - - The resources being tracked can be accessed as class attributes. - Additionally, the :code:`Resources` instance can be nicely displayed in the console. - - **Example** - - >>> r = Resources( - ... num_wires=2, - ... num_gates=2, - ... gate_types={ - ... OpTypeWithParams("Hadamard", (("num_wires", 1),)): 1, - ... OpTypeWithParams("CNOT", (("num_wires", 2),)):1, - ... } - ... ) - >>> print(r) - wires: 2 - gates: 2 - gate_types: - {'Hadamard(num_wires=1)': 1, 'CNOT(num_wires=2)': 1} - """ - num_wires: int = 0 - num_gates: int = 0 - gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) - - def __str__(self): - keys = ["wires", "gates"] - vals = [self.num_wires, self.num_gates] - items = "\n".join([str(i) for i in zip(keys, vals)]) - items = items.replace("('", "") - items = items.replace("',", ":") - items = items.replace(")", "") - - gate_type_str = ", ".join( - [f"'{str(gate_name)}': {count}" for gate_name, count in self.gate_types.items()] - ) - items += "\ngate_types:\n{" + gate_type_str + "}" - return items - - def _ipython_display_(self): - """Displays __str__ in ipython instead of __repr__""" - print(str(self)) \ No newline at end of file diff --git a/pennylane/labs/tests/resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/resource_estimation/test_resource_contianer.py new file mode 100644 index 00000000000..20b5f5514df --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/test_resource_contianer.py @@ -0,0 +1,144 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Test base Resource class and its associated methods +""" + +from collections import defaultdict + +import pytest + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, Resources + + +class TestCompressedResourceOp: + + hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) + compressed_op_args_lst = ( + ("PauliX", qml.X, tuple({"num_wires": 1}.items())), + ("QFT", qml.QFT, tuple({"num_wires": 5}.items())), + ("QSVT", qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items())), + ( + "TrotterProduct", + qml.TrotterProduct, + tuple({"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2}.items()), + ), + ) + + compressed_op_reprs = ( + "PauliX(num_wires=1)", + "QFT(num_wires=5)", + "QSVT(num_wires=3, num_angles=5)", + "TrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", + ) + + @pytest.mark.parametrize("name, op_type, parameters", compressed_op_args_lst) + def test_init(self, name, op_type, parameters): + """Test that we can correctly instantiate CompressedResourceOp""" + cr_op = CompressedResourceOp(op_type, parameters) + + assert cr_op._name == name + assert cr_op.op_type is op_type + assert cr_op.params == parameters + + def test_hash(self): + """Test that the hash method behaves as expected""" + CmprssedQSVT1 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + CmprssedQSVT2 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) + + assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) # compare same object + assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) # compare identical instance + assert hash(CmprssedQSVT1) != hash(Other) + + def test_equality(self): + """Test that the equality methods behaves as expected""" + CmprssedQSVT1 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + CmprssedQSVT2 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + CmprssedQSVT3 = CompressedResourceOp( + qml.QSVT, tuple({"num_angles": 5, "num_wires": 3}.items()) + ) + Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) + + assert CmprssedQSVT1 == CmprssedQSVT1 # compare same object + assert CmprssedQSVT1 == CmprssedQSVT2 # compare identical instance + assert CmprssedQSVT1 == CmprssedQSVT3 # compare swapped parameters + assert CmprssedQSVT1 != Other + + @pytest.mark.parametrize("args, repr", zip(compressed_op_args_lst, compressed_op_reprs)) + def test_repr(self, args, repr): + """Test that the repr method behaves as expected.""" + _, op_type, parameters = args + cr_op = CompressedResourceOp(op_type, parameters) + + assert str(cr_op) == repr + + +class TestResources: + """Test the methods and attributes of the Resource class""" + + resource_quantities = ( + Resources(), + Resources(5, 0, {}), + Resources( + 1, + 3, + defaultdict(int, {"Hadamard": 1, "PauliZ": 2}), + ), + Resources(4, 2, {"Hadamard": 1, "CNOT": 1}), + ) + + resource_parameters = ( + (0, 0, {}), + (5, 0, {}), + (1, 3, defaultdict(int, {"Hadamard": 1, "PauliZ": 2})), + (4, 2, defaultdict(int, {"Hadamard": 1, "CNOT": 1})), + ) + + @pytest.mark.parametrize("r, attribute_tup", zip(resource_quantities, resource_parameters)) + def test_init(self, r, attribute_tup): + """Test that the Resource class is instantiated as expected.""" + num_wires, num_gates, gate_types = attribute_tup + + assert r.num_wires == num_wires + assert r.num_gates == num_gates + assert r.gate_types == gate_types + + test_str_data = ( + ("wires: 0\n" + "gates: 0\n" + "gate_types:\n" + "{}"), + ("wires: 5\n" + "gates: 0\n" + "gate_types:\n" + "{}"), + ("wires: 1\n" + "gates: 3\n" + "gate_types:\n" + "{'Hadamard': 1, 'PauliZ': 2}"), + ("wires: 4\n" + "gates: 2\n" + "gate_types:\n" + "{'Hadamard': 1, 'CNOT': 1}"), + ) + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) + def test_str(self, r, rep): + """Test the string representation of a Resources instance.""" + assert str(r) == rep + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) + def test_ipython_display(self, r, rep, capsys): + """Test that the ipython display prints the string representation of a Resources instance.""" + r._ipython_display_() # pylint: disable=protected-access + captured = capsys.readouterr() + assert rep in captured.out diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py index 772fca26179..20b5f5514df 100644 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py @@ -15,21 +15,33 @@ Test base Resource class and its associated methods """ -import pytest from collections import defaultdict +import pytest + import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp +from pennylane.labs.resource_estimation import CompressedResourceOp, Resources class TestCompressedResourceOp: - hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0)@qml.Z(1)]) + hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) compressed_op_args_lst = ( - ("X", qml.X, tuple({"num_wires": 1})), - ("QFT", qml.QFT, tuple({"num_wires": 5})), - ("QSVT", qml.QSVT, tuple({"num_wires": 3, "num_angles":5})), - (qml.TrotterProduct, tuple({"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2})) + ("PauliX", qml.X, tuple({"num_wires": 1}.items())), + ("QFT", qml.QFT, tuple({"num_wires": 5}.items())), + ("QSVT", qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items())), + ( + "TrotterProduct", + qml.TrotterProduct, + tuple({"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2}.items()), + ), + ) + + compressed_op_reprs = ( + "PauliX(num_wires=1)", + "QFT(num_wires=5)", + "QSVT(num_wires=3, num_angles=5)", + "TrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", ) @pytest.mark.parametrize("name, op_type, parameters", compressed_op_args_lst) @@ -43,19 +55,90 @@ def test_init(self, name, op_type, parameters): def test_hash(self): """Test that the hash method behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp(qml.QSVT, tuple({"num_wires": 3, "num_angles":5})) - CmprssedQSVT2 = CompressedResourceOp(qml.QSVT, tuple({"num_wires": 3, "num_angles":5})) - Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3})) + CmprssedQSVT1 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + CmprssedQSVT2 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) - assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) - assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) + assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) # compare same object + assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) # compare identical instance assert hash(CmprssedQSVT1) != hash(Other) - + def test_equality(self): """Test that the equality methods behaves as expected""" - - assert True + CmprssedQSVT1 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + CmprssedQSVT2 = CompressedResourceOp( + qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) + ) + CmprssedQSVT3 = CompressedResourceOp( + qml.QSVT, tuple({"num_angles": 5, "num_wires": 3}.items()) + ) + Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) - def test_repr(self): + assert CmprssedQSVT1 == CmprssedQSVT1 # compare same object + assert CmprssedQSVT1 == CmprssedQSVT2 # compare identical instance + assert CmprssedQSVT1 == CmprssedQSVT3 # compare swapped parameters + assert CmprssedQSVT1 != Other + + @pytest.mark.parametrize("args, repr", zip(compressed_op_args_lst, compressed_op_reprs)) + def test_repr(self, args, repr): """Test that the repr method behaves as expected.""" - assert True \ No newline at end of file + _, op_type, parameters = args + cr_op = CompressedResourceOp(op_type, parameters) + + assert str(cr_op) == repr + + +class TestResources: + """Test the methods and attributes of the Resource class""" + + resource_quantities = ( + Resources(), + Resources(5, 0, {}), + Resources( + 1, + 3, + defaultdict(int, {"Hadamard": 1, "PauliZ": 2}), + ), + Resources(4, 2, {"Hadamard": 1, "CNOT": 1}), + ) + + resource_parameters = ( + (0, 0, {}), + (5, 0, {}), + (1, 3, defaultdict(int, {"Hadamard": 1, "PauliZ": 2})), + (4, 2, defaultdict(int, {"Hadamard": 1, "CNOT": 1})), + ) + + @pytest.mark.parametrize("r, attribute_tup", zip(resource_quantities, resource_parameters)) + def test_init(self, r, attribute_tup): + """Test that the Resource class is instantiated as expected.""" + num_wires, num_gates, gate_types = attribute_tup + + assert r.num_wires == num_wires + assert r.num_gates == num_gates + assert r.gate_types == gate_types + + test_str_data = ( + ("wires: 0\n" + "gates: 0\n" + "gate_types:\n" + "{}"), + ("wires: 5\n" + "gates: 0\n" + "gate_types:\n" + "{}"), + ("wires: 1\n" + "gates: 3\n" + "gate_types:\n" + "{'Hadamard': 1, 'PauliZ': 2}"), + ("wires: 4\n" + "gates: 2\n" + "gate_types:\n" + "{'Hadamard': 1, 'CNOT': 1}"), + ) + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) + def test_str(self, r, rep): + """Test the string representation of a Resources instance.""" + assert str(r) == rep + + @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) + def test_ipython_display(self, r, rep, capsys): + """Test that the ipython display prints the string representation of a Resources instance.""" + r._ipython_display_() # pylint: disable=protected-access + captured = capsys.readouterr() + assert rep in captured.out From bacb16b15eade8818b21addc0fe82a26b25410c6 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 24 Oct 2024 08:49:41 -0400 Subject: [PATCH 011/335] renamed folder --- .../test_resource_contianer.py | 144 ------------------ 1 file changed, 144 deletions(-) delete mode 100644 pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py deleted file mode 100644 index 20b5f5514df..00000000000 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_contianer.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Test base Resource class and its associated methods -""" - -from collections import defaultdict - -import pytest - -import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, Resources - - -class TestCompressedResourceOp: - - hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) - compressed_op_args_lst = ( - ("PauliX", qml.X, tuple({"num_wires": 1}.items())), - ("QFT", qml.QFT, tuple({"num_wires": 5}.items())), - ("QSVT", qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items())), - ( - "TrotterProduct", - qml.TrotterProduct, - tuple({"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2}.items()), - ), - ) - - compressed_op_reprs = ( - "PauliX(num_wires=1)", - "QFT(num_wires=5)", - "QSVT(num_wires=3, num_angles=5)", - "TrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", - ) - - @pytest.mark.parametrize("name, op_type, parameters", compressed_op_args_lst) - def test_init(self, name, op_type, parameters): - """Test that we can correctly instantiate CompressedResourceOp""" - cr_op = CompressedResourceOp(op_type, parameters) - - assert cr_op._name == name - assert cr_op.op_type is op_type - assert cr_op.params == parameters - - def test_hash(self): - """Test that the hash method behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - CmprssedQSVT2 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) - - assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) # compare same object - assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) # compare identical instance - assert hash(CmprssedQSVT1) != hash(Other) - - def test_equality(self): - """Test that the equality methods behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - CmprssedQSVT2 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - CmprssedQSVT3 = CompressedResourceOp( - qml.QSVT, tuple({"num_angles": 5, "num_wires": 3}.items()) - ) - Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) - - assert CmprssedQSVT1 == CmprssedQSVT1 # compare same object - assert CmprssedQSVT1 == CmprssedQSVT2 # compare identical instance - assert CmprssedQSVT1 == CmprssedQSVT3 # compare swapped parameters - assert CmprssedQSVT1 != Other - - @pytest.mark.parametrize("args, repr", zip(compressed_op_args_lst, compressed_op_reprs)) - def test_repr(self, args, repr): - """Test that the repr method behaves as expected.""" - _, op_type, parameters = args - cr_op = CompressedResourceOp(op_type, parameters) - - assert str(cr_op) == repr - - -class TestResources: - """Test the methods and attributes of the Resource class""" - - resource_quantities = ( - Resources(), - Resources(5, 0, {}), - Resources( - 1, - 3, - defaultdict(int, {"Hadamard": 1, "PauliZ": 2}), - ), - Resources(4, 2, {"Hadamard": 1, "CNOT": 1}), - ) - - resource_parameters = ( - (0, 0, {}), - (5, 0, {}), - (1, 3, defaultdict(int, {"Hadamard": 1, "PauliZ": 2})), - (4, 2, defaultdict(int, {"Hadamard": 1, "CNOT": 1})), - ) - - @pytest.mark.parametrize("r, attribute_tup", zip(resource_quantities, resource_parameters)) - def test_init(self, r, attribute_tup): - """Test that the Resource class is instantiated as expected.""" - num_wires, num_gates, gate_types = attribute_tup - - assert r.num_wires == num_wires - assert r.num_gates == num_gates - assert r.gate_types == gate_types - - test_str_data = ( - ("wires: 0\n" + "gates: 0\n" + "gate_types:\n" + "{}"), - ("wires: 5\n" + "gates: 0\n" + "gate_types:\n" + "{}"), - ("wires: 1\n" + "gates: 3\n" + "gate_types:\n" + "{'Hadamard': 1, 'PauliZ': 2}"), - ("wires: 4\n" + "gates: 2\n" + "gate_types:\n" + "{'Hadamard': 1, 'CNOT': 1}"), - ) - - @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) - def test_str(self, r, rep): - """Test the string representation of a Resources instance.""" - assert str(r) == rep - - @pytest.mark.parametrize("r, rep", zip(resource_quantities, test_str_data)) - def test_ipython_display(self, r, rep, capsys): - """Test that the ipython display prints the string representation of a Resources instance.""" - r._ipython_display_() # pylint: disable=protected-access - captured = capsys.readouterr() - assert rep in captured.out From cc1436d7079f926fab4f8cdca47638505a67486c Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 09:49:10 -0400 Subject: [PATCH 012/335] first draft of resource ops --- .../labs/resource_estimation/__init__.py | 4 +- .../resource_constructor.py | 4 +- .../{resource_qft.py => resource_ops.py} | 40 +++++++++- .../test_resource_ops.py | 77 +++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) rename pennylane/labs/resource_estimation/{resource_qft.py => resource_ops.py} (58%) create mode 100644 pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 91ab79beda2..77391510547 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,9 +13,9 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .resource_constructor import ResourceConstructor +from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp from .resources_base import Resources # Resource Operators -from .resource_qft import ResourceQFT +from .resource_ops import * diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 5c76847affa..ca0926b710f 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -8,7 +8,6 @@ Resources = resources_base.Resources class ResourceConstructor(ABC): - @staticmethod @abstractmethod def compute_resources(*args, **kwargs) -> Resources: @@ -23,3 +22,6 @@ def set_compute_resources(cls, new_func: Callable) -> None: def resource_rep(self) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" + +class ResourcesNotDefined(Exception): + """Exception to be raised when a ResourceConstructor does not implement compute_resources""" diff --git a/pennylane/labs/resource_estimation/resource_qft.py b/pennylane/labs/resource_estimation/resource_ops.py similarity index 58% rename from pennylane/labs/resource_estimation/resource_qft.py rename to pennylane/labs/resource_estimation/resource_ops.py index 7edda246959..c108424be77 100644 --- a/pennylane/labs/resource_estimation/resource_qft.py +++ b/pennylane/labs/resource_estimation/resource_ops.py @@ -1,5 +1,6 @@ +import numpy as np import pennylane as qml -from pennylane.labs.resource_estimation import ResourceConstructor, CompressedResourceOp +from pennylane.labs.resource_estimation import ResourceConstructor, CompressedResourceOp, ResourcesNotDefined #pylint: disable=too-many-ancestors,arguments-differ @@ -8,6 +9,12 @@ class ResourceQFT(qml.QFT, ResourceConstructor): @staticmethod def compute_resources(num_wires) -> dict: + if not isinstance(num_wires, int): + raise TypeError("num_wires must be an int.") + + if num_wires < 1: + raise ValueError("num_wires must be greater than 0.") + gate_types = {} hadamard = CompressedResourceOp(qml.Hadamard, ()) @@ -46,5 +53,36 @@ def resource_rep(self) -> CompressedResourceOp: class ResourceCNOT(qml.CNOT, ResourceConstructor): """Resource class for CNOT""" + @staticmethod + def compute_resources() -> dict: + raise ResourcesNotDefined + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.CNOT, ()) + class ResourceRZ(qml.RZ, ResourceConstructor): """Resource class for RZ""" + + @staticmethod + def compute_resources(epsilon=10e-3) -> dict: + gate_types = {} + + num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) + t = CompressedResourceOp(qml.T, ()) + gate_types[t] = num_gates + + return gate_types + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.RZ, ()) + + +class ResourceT(qml.T, ResourceConstructor): + """Resource class for T""" + + @staticmethod + def compute_resources() -> dict: + raise ResourcesNotDefined + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.T, ()) diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py new file mode 100644 index 00000000000..260d6e3722a --- /dev/null +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py @@ -0,0 +1,77 @@ +import pytest + +import pennylane as qml +import pennylane.labs.resource_estimation.resource_ops as ops +from pennylane.labs.resource_estimation import CompressedResourceOp + +class TestQFT: + """Test the ResourceQFT class""" + + @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", + [ + (1, 1, 0, 0), + (2, 2, 1, 1), + (3, 3, 1, 3), + (4, 4, 2, 6), + ] + ) + def test_compute_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): + """Test the compute_resources method returns the correct dictionary""" + hadamard = CompressedResourceOp(qml.Hadamard, ()) + swap = CompressedResourceOp(qml.SWAP, ()) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, ()) + + expected = { + hadamard: num_hadamard, + swap: num_swap, + ctrl_phase_shift: num_ctrl_phase_shift + } + + assert ops.ResourceQFT.compute_resources(num_wires) == expected + + @pytest.mark.parametrize("num_wires", [1, 2, 3, 4]) + def test_resource_rep(self, num_wires): + """Test the resource_rep returns the correct CompressedResourceOp""" + + expected = CompressedResourceOp(qml.QFT, (("num_wires", num_wires),)) + op = ops.ResourceQFT(wires=range(num_wires)) + + assert op.resource_rep() == expected + + @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", + [ + (1, 1, 0, 0), + (2, 2, 1, 1), + (3, 3, 1, 1), + (4, 4, 2, 2), + ] + ) + def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): + """Test that computing the resources from a compressed representation works""" + + hadamard = CompressedResourceOp(qml.Hadamard, ()) + swap = CompressedResourceOp(qml.SWAP, ()) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, ()) + + expected = { + hadamard: num_hadamard, + swap: num_swap, + ctrl_phase_shift: num_ctrl_phase_shift + } + + rep = ops.ResourceQFT(wires=range(num_wires)).resource_rep() + actual = ops.ResourceQFT.compute_resources(**rep.params) + + assert actual == expected + + @pytest.mark.parametrize("num_wires", [2.5, -0.5]) + def test_type_error(self, num_wires): + """Test that compute_resources correctly raises a TypeError""" + with pytest.raises(TypeError, match="num_wires must be an int."): + ops.ResourceQFT.compute_resources(num_wires) + + @pytest.mark.parametrize("num_wires", [0, -1]) + def test_value_error(self, num_wires): + """Test that compute_resources correctly raises a ValueError""" + with pytest.raises(ValueError, match="num_wires must be greater than 0."): + ops.ResourceQFT.compute_resources(num_wires) From 2d8d92a21d5eb80e1bf4b0a491a625d2daf6064d Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 24 Oct 2024 09:50:11 -0400 Subject: [PATCH 013/335] tuple --> dict for params --- .../labs/resource_estimation/resource_container.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index f070a8c1766..b1b919ec56e 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -45,7 +45,7 @@ class CompressedResourceOp: QSVT(num_wires=5, num_angles=100) """ - def __init__(self, op_type: type, params_tuple: tuple) -> None: + def __init__(self, op_type: type, params: dict) -> None: r"""Instantiate the light weight class corressponding to the operator type and parameters. Args: @@ -75,13 +75,17 @@ def __init__(self, op_type: type, params_tuple: tuple) -> None: """ self._name = op_type.__name__ self.op_type = op_type - self.params = params_tuple + self.params = params + + sorted_keys = sorted(list(params)) + self._hashable_params = tuple((key, params[key]) for key in sorted_keys) # tuple of sorted params + def __hash__(self) -> int: - return hash((self._name, self.params)) + return hash((self._name, self._hashable_params)) def __eq__(self, other: object) -> bool: - return (self.op_type == other.op_type) and (dict(self.params) == dict(other.params)) + return (self.op_type == other.op_type) and (self.params == other.params) def __repr__(self) -> str: op_type_str = self._name + "(" From 0edae376c51eb05ac9604a9805f128a8d5b9e092 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 24 Oct 2024 10:50:39 -0400 Subject: [PATCH 014/335] Fixed tests and lint --- .../resource_estimation/resource_container.py | 7 ++-- .../test_resource_contianer.py | 35 ++++++++----------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index b1b919ec56e..2f3cac5b0da 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -78,8 +78,9 @@ def __init__(self, op_type: type, params: dict) -> None: self.params = params sorted_keys = sorted(list(params)) - self._hashable_params = tuple((key, params[key]) for key in sorted_keys) # tuple of sorted params - + self._hashable_params = tuple( + (key, params[key]) for key in sorted_keys + ) # tuple of sorted params def __hash__(self) -> int: return hash((self._name, self._hashable_params)) @@ -89,7 +90,7 @@ def __eq__(self, other: object) -> bool: def __repr__(self) -> str: op_type_str = self._name + "(" - params_str = ", ".join([f"{param[0]}={param[1]}" for param in self.params]) + ")" + params_str = ", ".join([f"{key}={self.params[key]}" for key in self.params]) + ")" return op_type_str + params_str diff --git a/pennylane/labs/tests/resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/resource_estimation/test_resource_contianer.py index 20b5f5514df..fb6c50bf2c3 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_contianer.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_contianer.py @@ -27,13 +27,13 @@ class TestCompressedResourceOp: hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) compressed_op_args_lst = ( - ("PauliX", qml.X, tuple({"num_wires": 1}.items())), - ("QFT", qml.QFT, tuple({"num_wires": 5}.items())), - ("QSVT", qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items())), + ("PauliX", qml.X, {"num_wires": 1}), + ("QFT", qml.QFT, {"num_wires": 5}), + ("QSVT", qml.QSVT, {"num_wires": 3, "num_angles": 5}), ( "TrotterProduct", qml.TrotterProduct, - tuple({"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2}.items()), + {"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2}, ), ) @@ -53,15 +53,14 @@ def test_init(self, name, op_type, parameters): assert cr_op.op_type is op_type assert cr_op.params == parameters + hashable_parameters = tuple((key, parameters[key]) for key in sorted(parameters)) + assert cr_op._hashable_params == hashable_parameters + def test_hash(self): """Test that the hash method behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - CmprssedQSVT2 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) + CmprssedQSVT1 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) + CmprssedQSVT2 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) + Other = CompressedResourceOp(qml.QFT, {"num_wires": 3}) assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) # compare same object assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) # compare identical instance @@ -69,16 +68,10 @@ def test_hash(self): def test_equality(self): """Test that the equality methods behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - CmprssedQSVT2 = CompressedResourceOp( - qml.QSVT, tuple({"num_wires": 3, "num_angles": 5}.items()) - ) - CmprssedQSVT3 = CompressedResourceOp( - qml.QSVT, tuple({"num_angles": 5, "num_wires": 3}.items()) - ) - Other = CompressedResourceOp(qml.QFT, tuple({"num_wires": 3}.items())) + CmprssedQSVT1 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) + CmprssedQSVT2 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) + CmprssedQSVT3 = CompressedResourceOp(qml.QSVT, {"num_angles": 5, "num_wires": 3}) + Other = CompressedResourceOp(qml.QFT, {"num_wires": 3}) assert CmprssedQSVT1 == CmprssedQSVT1 # compare same object assert CmprssedQSVT1 == CmprssedQSVT2 # compare identical instance From 3d0f0ef126201357f122a78b2d8115883b40db97 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 11:27:13 -0400 Subject: [PATCH 015/335] resource ops --- .../labs/resource_estimation/__init__.py | 5 +--- .../{resource_ops.py => ops/resource_qft.py} | 25 ++++++++++--------- .../resource_constructor.py | 5 +--- .../test_resource_ops.py | 20 +++++++-------- 4 files changed, 25 insertions(+), 30 deletions(-) rename pennylane/labs/resource_estimation/{resource_ops.py => ops/resource_qft.py} (75%) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 86fb459b149..eb28d5b321b 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -14,7 +14,4 @@ r"""This module contains experimental resource estimation functionality. """ from .resource_constructor import ResourceConstructor, ResourcesNotDefined -from .resource_container import CompressedResourceOp -from .resources_base import Resources - -from .resource_ops import * +from .resource_container import CompressedResourceOp, Resources diff --git a/pennylane/labs/resource_estimation/resource_ops.py b/pennylane/labs/resource_estimation/ops/resource_qft.py similarity index 75% rename from pennylane/labs/resource_estimation/resource_ops.py rename to pennylane/labs/resource_estimation/ops/resource_qft.py index c108424be77..dec9ccf5aae 100644 --- a/pennylane/labs/resource_estimation/resource_ops.py +++ b/pennylane/labs/resource_estimation/ops/resource_qft.py @@ -1,6 +1,7 @@ import numpy as np import pennylane as qml -from pennylane.labs.resource_estimation import ResourceConstructor, CompressedResourceOp, ResourcesNotDefined + +from .. import ResourceConstructor, CompressedResourceOp, ResourcesNotDefined #pylint: disable=too-many-ancestors,arguments-differ @@ -17,9 +18,9 @@ def compute_resources(num_wires) -> dict: gate_types = {} - hadamard = CompressedResourceOp(qml.Hadamard, ()) - swap = CompressedResourceOp(qml.SWAP, ()) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, ()) + hadamard = CompressedResourceOp(qml.Hadamard, {}) + swap = CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) gate_types[hadamard] = num_wires gate_types[swap] = num_wires // 2 @@ -28,7 +29,7 @@ def compute_resources(num_wires) -> dict: return gate_types def resource_rep(self) -> CompressedResourceOp: - params = (('num_wires', len(self.wires)),) + params = {"num_wires": len(self.wires)} return CompressedResourceOp(qml.QFT, params) @@ -39,8 +40,8 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, ResourceConstructor def compute_resources() -> dict: gate_types = {} - cnot = CompressedResourceOp(qml.CNOT, ()) - rz = CompressedResourceOp(qml.RZ, ()) + cnot = CompressedResourceOp(qml.CNOT, {}) + rz = CompressedResourceOp(qml.RZ, {}) gate_types[cnot] = 2 gate_types[rz] = 3 @@ -48,7 +49,7 @@ def compute_resources() -> dict: return gate_types def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.ControlledPhaseShift, ()) + return CompressedResourceOp(qml.ControlledPhaseShift, {}) class ResourceCNOT(qml.CNOT, ResourceConstructor): """Resource class for CNOT""" @@ -58,7 +59,7 @@ def compute_resources() -> dict: raise ResourcesNotDefined def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.CNOT, ()) + return CompressedResourceOp(qml.CNOT, {}) class ResourceRZ(qml.RZ, ResourceConstructor): """Resource class for RZ""" @@ -68,13 +69,13 @@ def compute_resources(epsilon=10e-3) -> dict: gate_types = {} num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) - t = CompressedResourceOp(qml.T, ()) + t = CompressedResourceOp(qml.T, {}) gate_types[t] = num_gates return gate_types def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.RZ, ()) + return CompressedResourceOp(qml.RZ, {}) class ResourceT(qml.T, ResourceConstructor): @@ -85,4 +86,4 @@ def compute_resources() -> dict: raise ResourcesNotDefined def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.T, ()) + return CompressedResourceOp(qml.T, {}) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index ca0926b710f..7f244d004e3 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -1,11 +1,8 @@ from abc import ABC, abstractmethod from typing import Callable -import pennylane.labs.resource_estimation.resources_base as resources_base -import pennylane.labs.resource_estimation.resource_container as resource_container +from .resource_container import CompressedResourceOp, Resources -CompressedResourceOp = resource_container.CompressedResourceOp -Resources = resources_base.Resources class ResourceConstructor(ABC): @staticmethod diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py index 260d6e3722a..ffa3a95f000 100644 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py @@ -1,7 +1,7 @@ import pytest import pennylane as qml -import pennylane.labs.resource_estimation.resource_ops as ops +import pennylane.labs.resource_estimation.ops as ops from pennylane.labs.resource_estimation import CompressedResourceOp class TestQFT: @@ -17,9 +17,9 @@ class TestQFT: ) def test_compute_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test the compute_resources method returns the correct dictionary""" - hadamard = CompressedResourceOp(qml.Hadamard, ()) - swap = CompressedResourceOp(qml.SWAP, ()) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, ()) + hadamard = CompressedResourceOp(qml.Hadamard, {}) + swap = CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) expected = { hadamard: num_hadamard, @@ -33,7 +33,7 @@ def test_compute_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_pha def test_resource_rep(self, num_wires): """Test the resource_rep returns the correct CompressedResourceOp""" - expected = CompressedResourceOp(qml.QFT, (("num_wires", num_wires),)) + expected = CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) op = ops.ResourceQFT(wires=range(num_wires)) assert op.resource_rep() == expected @@ -42,16 +42,16 @@ def test_resource_rep(self, num_wires): [ (1, 1, 0, 0), (2, 2, 1, 1), - (3, 3, 1, 1), - (4, 4, 2, 2), + (3, 3, 1, 3), + (4, 4, 2, 6), ] ) def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test that computing the resources from a compressed representation works""" - hadamard = CompressedResourceOp(qml.Hadamard, ()) - swap = CompressedResourceOp(qml.SWAP, ()) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, ()) + hadamard = CompressedResourceOp(qml.Hadamard, {}) + swap = CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) expected = { hadamard: num_hadamard, From 2fde4f228f80e509c56d43a27fa1acfef9baf2da Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 11:28:57 -0400 Subject: [PATCH 016/335] create constructor file --- pennylane/labs/resource_estimation/resource_constructor.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 pennylane/labs/resource_estimation/resource_constructor.py diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py new file mode 100644 index 00000000000..9c595a6fb76 --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -0,0 +1 @@ +temp From f7bf31f24f881846ee974d86b9ee661748b286cd Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 11:29:37 -0400 Subject: [PATCH 017/335] populate resource constructor --- .../resource_constructor.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 9c595a6fb76..7f244d004e3 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -1 +1,24 @@ -temp +from abc import ABC, abstractmethod +from typing import Callable + +from .resource_container import CompressedResourceOp, Resources + + +class ResourceConstructor(ABC): + @staticmethod + @abstractmethod + def compute_resources(*args, **kwargs) -> Resources: + """Returns the Resource object associated with the Operator.""" + + @classmethod + def set_compute_resources(cls, new_func: Callable) -> None: + """Override the compute_resources method.""" + cls.compute_resources = new_func + + @abstractmethod + def resource_rep(self) -> CompressedResourceOp: + """Returns a compressed representation containing only the parameters of + the Operator that are needed to compute a resource estimation.""" + +class ResourcesNotDefined(Exception): + """Exception to be raised when a ResourceConstructor does not implement compute_resources""" From c08ac344fd2d3b2c66823513f88ca8546451303c Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 11:30:31 -0400 Subject: [PATCH 018/335] add resource constructor to init --- pennylane/labs/resource_estimation/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index c73465a6bcb..1a600cdb0d8 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,5 +13,5 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -# from .resource_constructor import ResourceConstructor +from .resource_constructor import ResourceConstructor from .resource_container import CompressedResourceOp, Resources From 8d4e0357c402d3b64b6033c0adc9d40236871c53 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 11:32:35 -0400 Subject: [PATCH 019/335] linting --- pennylane/labs/resource_estimation/resource_constructor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 7f244d004e3..bf60c354b3b 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -20,5 +20,6 @@ def resource_rep(self) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" + class ResourcesNotDefined(Exception): """Exception to be raised when a ResourceConstructor does not implement compute_resources""" From c361ac7f7c6e28e525bfb2796ed43f31b3a72878 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 24 Oct 2024 11:43:43 -0400 Subject: [PATCH 020/335] Removed sorting of hashable params, added arithemtic ops --- .../resource_estimation/resource_container.py | 161 +++++++++++++++--- .../test_resource_contianer.py | 4 +- 2 files changed, 143 insertions(+), 22 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 2f3cac5b0da..78b88c991b8 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. r"""Base classes for resource estimation.""" +import copy from collections import defaultdict from dataclasses import dataclass, field @@ -21,8 +22,8 @@ class CompressedResourceOp: Args: op_type (Type): the PennyLane type of the operation - params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal - pairs of parameter names and values required to compute the resources for the given operator! + params (dict): a dictionary containing the minimal pairs of parameter names and values + required to compute the resources for the given operator! .. details:: @@ -30,16 +31,16 @@ class CompressedResourceOp: **Example** - >>> op_tp = CompressedResourceOp(qml.Hadamard, (("num_wires", 1),)) + >>> op_tp = CompressedResourceOp(qml.Hadamard, {"num_wires":1}) >>> print(op_tp) Hadamard(num_wires=1) >>> op_tp = CompressedResourceOp( qml.QSVT, - ( - ("num_wires", 5), - ("num_angles", 100), - ), + { + "num_wires": 5, + "num_angles": 100, + }, ) >>> print(op_tp) QSVT(num_wires=5, num_angles=100) @@ -50,8 +51,8 @@ def __init__(self, op_type: type, params: dict) -> None: Args: op_type (Type): the PennyLane type of the operation - params_tuple (tuple): [tuple(tuple(parameter_name: str, parameter_value))] contains the minimal - pairs of parameter names and values required to compute the resources for the given operator! + params (dict): a dictionary containing the minimal pairs of parameter names and values + required to compute the resources for the given operator! .. details:: @@ -59,16 +60,16 @@ def __init__(self, op_type: type, params: dict) -> None: **Example** - >>> op_tp = CompressedResourceOp(qml.Hadamard, (("num_wires", 1),)) + >>> op_tp = CompressedResourceOp(qml.Hadamard, {"num_wires":1}) >>> print(op_tp) Hadamard(num_wires=1) >>> op_tp = CompressedResourceOp( qml.QSVT, - ( - ("num_wires", 5), - ("num_angles", 100), - ), + { + "num_wires": 5, + "num_angles": 100, + }, ) >>> print(op_tp) QSVT(num_wires=5, num_angles=100) @@ -76,11 +77,7 @@ def __init__(self, op_type: type, params: dict) -> None: self._name = op_type.__name__ self.op_type = op_type self.params = params - - sorted_keys = sorted(list(params)) - self._hashable_params = tuple( - (key, params[key]) for key in sorted_keys - ) # tuple of sorted params + self._hashable_params = tuple(params.items()) def __hash__(self) -> int: return hash((self._name, self._hashable_params)) @@ -128,6 +125,24 @@ class Resources: num_gates: int = 0 gate_types: defaultdict = field(default_factory=lambda: defaultdict(int)) + def __add__(self, other: "Resources") -> "Resources": + """Add two resources objects in series""" + return add_in_series(self, other) + + def __mul__(self, scaler: int) -> "Resources": + """Scale a resources object in series""" + return mul_in_series(self, scaler) + + __rmul__ = __mul__ # same implementation + + def __iadd__(self, other: "Resources") -> None: + """Add two resources objects in series""" + add_in_series(self, other, in_place=True) + + def __imull__(self, scaler: int) -> None: + """Scale a resources object in series""" + mul_in_series(self, scaler, in_place=True) + def __str__(self): """String representation of the Resources object.""" keys = ["wires", "gates"] @@ -146,3 +161,111 @@ def __str__(self): def _ipython_display_(self): """Displays __str__ in ipython instead of __repr__""" print(str(self)) + + +def add_in_series(first: Resources, other: Resources, in_place=False) -> Resources: + """Add two resources assuming the circuits are executed in series. + + Args: + first (Resources): first resource object to combine + other (Resources): other resource object to combine with + in_place (bool): determines if the first Resources are modified in place (default False) + + Returns: + Resources: combined resources + """ + new_wires = max(first.num_wires, other.num_wires) + new_gates = first.num_gates + other.num_gates + new_gate_types = _combine_dict(first.gate_types, other.gate_types, in_place=in_place) + + if in_place: + first.num_wires = new_wires + first.num_gates = new_gates + return first + + return Resources(new_wires, new_gates, new_gate_types) + + +def add_in_parallel(first: Resources, other: Resources, in_place=False) -> Resources: + """Add two resources assuming the circuits are executed in parallel. + + Args: + first (Resources): first resource object to combine + other (Resources): other resource object to combine with + in_place (bool): determines if the first Resources are modified in place (default False) + + Returns: + Resources: combined resources + """ + new_wires = first.num_wires + other.num_wires + new_gates = first.num_gates + other.num_gates + new_gate_types = _combine_dict(first.gate_types, other.gate_types, in_place=in_place) + + if in_place: + first.num_wires = new_wires + first.num_gates = new_gates + return first + + return Resources(new_wires, new_gates, new_gate_types) + + +def mul_in_series(first: Resources, scaler: int, in_place=False) -> Resources: + """Multiply two resources assuming the circuits are executed in series. + + Args: + first (Resources): first resource object to combine + scaler (int): integer value to scale the resources by + in_place (bool): determines if the first Resources are modified in place (default False) + + Returns: + Resources: combined resources + """ + new_gates = scaler * first.num_gates + new_gate_types = _scale_dict(first.gate_types, scaler, in_place=in_place) + + if in_place: + first.num_gates = new_gates + return first + + return Resources(first.num_wires, new_gates, new_gate_types) + + +def mul_in_parllel(first: Resources, scaler: int, in_place=False) -> Resources: + """Multiply two resources assuming the circuits are executed in parallel. + + Args: + first (Resources): first resource object to combine + scaler (int): integer value to scale the resources by + in_place (bool): determines if the first Resources are modified in place (default False) + + Returns: + Resources: combined resources + """ + new_wires = scaler * first.num_wires + new_gates = scaler * first.num_gates + new_gate_types = _scale_dict(first.gate_types, scaler, in_place=in_place) + + if in_place: + first.num_wires = new_wires + first.num_gates = new_gates + return first + + return Resources(new_wires, new_gates, new_gate_types) + + +def _combine_dict(dict1: defaultdict, dict2: defaultdict, in_place=False): + combined_dict = dict1 if in_place else copy.copy(dict1) + + for k, v in dict2.items(): + combined_dict[k] += v + + return combined_dict + + +def _scale_dict(dict1: defaultdict, scaler: int, in_place=False): + combined_dict = dict1 if in_place else copy.copy(dict1) + + for k in combined_dict: + combined_dict[k] *= scaler + + return combined_dict diff --git a/pennylane/labs/tests/resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/resource_estimation/test_resource_contianer.py index fb6c50bf2c3..920b1a5f800 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_contianer.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_contianer.py @@ -52,9 +52,7 @@ def test_init(self, name, op_type, parameters): assert cr_op._name == name assert cr_op.op_type is op_type assert cr_op.params == parameters - - hashable_parameters = tuple((key, parameters[key]) for key in sorted(parameters)) - assert cr_op._hashable_params == hashable_parameters + assert cr_op._hashable_params == tuple(parameters.items()) def test_hash(self): """Test that the hash method behaves as expected""" From 8fa721b5760bd50f8c2e787a6f7ab2f4ed0d39e1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 14:34:00 -0400 Subject: [PATCH 021/335] changing resource method --- .../resource_constructor.py | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index bf60c354b3b..dd158a15530 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -1,19 +1,41 @@ from abc import ABC, abstractmethod -from typing import Callable +from typing import Callable, Dict -from .resource_container import CompressedResourceOp, Resources +from .resource_container import CompressedResourceOp class ResourceConstructor(ABC): + r""" This is an abstract class that defines the methods a PennyLane Operator + must implement in order to be used for resource estimation. + + .. details:: + + **Example** + import pennylane as qml + + class ResourceQFT(qml.QFT, ResourceConstructor): + def compute_resources(num_wires): + return + + def resource_rep(self): + return + """ + @staticmethod @abstractmethod - def compute_resources(*args, **kwargs) -> Resources: - """Returns the Resource object associated with the Operator.""" + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + """Returns the Resource object. This method is only to be used inside + the methods of classes inheriting from ResourceConstructor.""" + + @classmethod + def resources(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: + """Returns the Resource object""" + return cls._resource_decomp(*args, **kwargs) @classmethod - def set_compute_resources(cls, new_func: Callable) -> None: + def set_resources(cls, new_func: Callable) -> None: """Override the compute_resources method.""" - cls.compute_resources = new_func + cls.resources = new_func @abstractmethod def resource_rep(self) -> CompressedResourceOp: From af6935c8ad354487dd327b49cebfcb88f7a14252 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 15:13:09 -0400 Subject: [PATCH 022/335] renaming --- .../labs/resource_estimation/ops/resource_qft.py | 10 +++++----- .../test_resource_ops.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/resource_qft.py b/pennylane/labs/resource_estimation/ops/resource_qft.py index dec9ccf5aae..ea72a7e5365 100644 --- a/pennylane/labs/resource_estimation/ops/resource_qft.py +++ b/pennylane/labs/resource_estimation/ops/resource_qft.py @@ -9,7 +9,7 @@ class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @staticmethod - def compute_resources(num_wires) -> dict: + def _resource_decomp(num_wires) -> dict: if not isinstance(num_wires, int): raise TypeError("num_wires must be an int.") @@ -37,7 +37,7 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, ResourceConstructor """Resource class for ControlledPhaseShift""" @staticmethod - def compute_resources() -> dict: + def _resource_decomp() -> dict: gate_types = {} cnot = CompressedResourceOp(qml.CNOT, {}) @@ -55,7 +55,7 @@ class ResourceCNOT(qml.CNOT, ResourceConstructor): """Resource class for CNOT""" @staticmethod - def compute_resources() -> dict: + def _resource_decomp() -> dict: raise ResourcesNotDefined def resource_rep(self) -> CompressedResourceOp: @@ -65,7 +65,7 @@ class ResourceRZ(qml.RZ, ResourceConstructor): """Resource class for RZ""" @staticmethod - def compute_resources(epsilon=10e-3) -> dict: + def _resource_decomp(epsilon=10e-3) -> dict: gate_types = {} num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) @@ -82,7 +82,7 @@ class ResourceT(qml.T, ResourceConstructor): """Resource class for T""" @staticmethod - def compute_resources() -> dict: + def _resource_decomp() -> dict: raise ResourcesNotDefined def resource_rep(self) -> CompressedResourceOp: diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py b/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py index ffa3a95f000..179194da8f6 100644 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py +++ b/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py @@ -15,8 +15,8 @@ class TestQFT: (4, 4, 2, 6), ] ) - def test_compute_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): - """Test the compute_resources method returns the correct dictionary""" + def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): + """Test the resources method returns the correct dictionary""" hadamard = CompressedResourceOp(qml.Hadamard, {}) swap = CompressedResourceOp(qml.SWAP, {}) ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) @@ -27,7 +27,7 @@ def test_compute_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_pha ctrl_phase_shift: num_ctrl_phase_shift } - assert ops.ResourceQFT.compute_resources(num_wires) == expected + assert ops.ResourceQFT.resources(num_wires) == expected @pytest.mark.parametrize("num_wires", [1, 2, 3, 4]) def test_resource_rep(self, num_wires): @@ -60,18 +60,18 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph } rep = ops.ResourceQFT(wires=range(num_wires)).resource_rep() - actual = ops.ResourceQFT.compute_resources(**rep.params) + actual = ops.ResourceQFT.resources(**rep.params) assert actual == expected @pytest.mark.parametrize("num_wires", [2.5, -0.5]) def test_type_error(self, num_wires): - """Test that compute_resources correctly raises a TypeError""" + """Test that resources correctly raises a TypeError""" with pytest.raises(TypeError, match="num_wires must be an int."): - ops.ResourceQFT.compute_resources(num_wires) + ops.ResourceQFT.resources(num_wires) @pytest.mark.parametrize("num_wires", [0, -1]) def test_value_error(self, num_wires): - """Test that compute_resources correctly raises a ValueError""" + """Test that resources correctly raises a ValueError""" with pytest.raises(ValueError, match="num_wires must be greater than 0."): - ops.ResourceQFT.compute_resources(num_wires) + ops.ResourceQFT.resources(num_wires) From d21ea90515897a4132e1b4019be2b69ca95bdae0 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 15:13:42 -0400 Subject: [PATCH 023/335] update init --- pennylane/labs/resource_estimation/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index d1f9d2ae744..eb28d5b321b 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,9 +13,5 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -<<<<<<< HEAD from .resource_constructor import ResourceConstructor, ResourcesNotDefined -======= -from .resource_constructor import ResourceConstructor ->>>>>>> 8d4e0357c402d3b64b6033c0adc9d40236871c53 from .resource_container import CompressedResourceOp, Resources From 7182f7c3bbedac8ac7a15101d053501637e7e7d1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 15:14:34 -0400 Subject: [PATCH 024/335] making resources a variable --- pennylane/labs/resource_estimation/resource_constructor.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index dd158a15530..213f6c36e36 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -27,10 +27,7 @@ def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns the Resource object. This method is only to be used inside the methods of classes inheriting from ResourceConstructor.""" - @classmethod - def resources(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: - """Returns the Resource object""" - return cls._resource_decomp(*args, **kwargs) + resources = _resource_decomp @classmethod def set_resources(cls, new_func: Callable) -> None: From 7369c8d292955ac87865017d46a6ca20443178d3 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 15:28:35 -0400 Subject: [PATCH 025/335] changing resources to class method --- pennylane/labs/resource_estimation/resource_constructor.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 213f6c36e36..ef1d2973eca 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -27,7 +27,11 @@ def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns the Resource object. This method is only to be used inside the methods of classes inheriting from ResourceConstructor.""" - resources = _resource_decomp + @classmethod + def resources(cls, *args, **kwargs): + """Returns the Resource object. This method is intended to be user facing + and overridable.""" + return cls._resource_decomp(*args, **kwargs) @classmethod def set_resources(cls, new_func: Callable) -> None: From 7aa9d0a3925b4bdb55cfafbfc7f5e42ce88aba14 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 16:22:11 -0400 Subject: [PATCH 026/335] adding class method wrapper --- pennylane/labs/resource_estimation/resource_constructor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index ef1d2973eca..bb69bc58b2f 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -36,7 +36,7 @@ def resources(cls, *args, **kwargs): @classmethod def set_resources(cls, new_func: Callable) -> None: """Override the compute_resources method.""" - cls.resources = new_func + cls.resources = classmethod(new_func) @abstractmethod def resource_rep(self) -> CompressedResourceOp: From 8240d8e27518995a78019b877f5232f62e536aae Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 16:41:52 -0400 Subject: [PATCH 027/335] removing classmethod --- pennylane/labs/resource_estimation/resource_constructor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index bb69bc58b2f..ef1d2973eca 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -36,7 +36,7 @@ def resources(cls, *args, **kwargs): @classmethod def set_resources(cls, new_func: Callable) -> None: """Override the compute_resources method.""" - cls.resources = classmethod(new_func) + cls.resources = new_func @abstractmethod def resource_rep(self) -> CompressedResourceOp: From deddc1204516bfae4915d8377cbf83be42229fbd Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 24 Oct 2024 16:45:05 -0400 Subject: [PATCH 028/335] adding tests for resource constructor --- .../test_resource_constructor.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 pennylane/labs/tests/resource_estimation/test_resource_constructor.py diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py new file mode 100644 index 00000000000..71b594f98a2 --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -0,0 +1,31 @@ +import pytest + +from pennylane.labs.resource_estimation import ResourceConstructor + +def test_abstract_resource_decomp(): + class DummyClass(ResourceConstructor): + def resource_rep(self): + return + + with pytest.raises(TypeError, match="Can't instantiate abstract class DummyClass with abstract method _resource_decomp"): + DummyClass() + +def test_abstract_resource_rep(): + class DummyClass(ResourceConstructor): + def _resource_decomp(): + return + + with pytest.raises(TypeError, match="Can't instantiate abstract class DummyClass with abstract method resource_rep"): + DummyClass() + +def test_set_resources(): + class DummyClass(ResourceConstructor): + def resource_rep(self): + return + + def _resource_decomp(): + return + + dummy = DummyClass() + DummyClass.set_resources(lambda _: 5) + assert DummyClass.resources(10) == 5 From 110134e455f3588ae9031e97f099a7cf04fd9d9f Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 25 Oct 2024 08:14:11 -0400 Subject: [PATCH 029/335] fixed typo in file name --- .../{test_resource_contianer.py => test_resource_container.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pennylane/labs/tests/resource_estimation/{test_resource_contianer.py => test_resource_container.py} (100%) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_contianer.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py similarity index 100% rename from pennylane/labs/tests/resource_estimation/test_resource_contianer.py rename to pennylane/labs/tests/resource_estimation/test_resource_container.py From 9b1758b1f7e432fe2b80d9ec9de266b685156c3a Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 25 Oct 2024 10:58:34 -0400 Subject: [PATCH 030/335] changing file structure and adding tests --- .../labs/resource_estimation/__init__.py | 12 +++ .../labs/resource_estimation/ops/__init__.py | 10 +++ .../ops/op_math/__init__.py | 1 + .../ops/op_math/controlled_ops.py | 34 +++++++ .../resource_estimation/ops/qubit/__init__.py | 8 ++ .../ops/qubit/non_parametric_ops.py | 24 +++++ .../ops/qubit/parametric_ops_single_qubit.py | 25 ++++++ .../resource_estimation/ops/resource_qft.py | 89 ------------------- .../resource_estimation/templates/__init__.py | 1 + .../templates/resource_qft.py | 30 +++++++ .../ops/op_math/test_controlled_ops.py | 67 ++++++++++++++ .../ops/qubit/test_non_parametric_ops.py | 34 +++++++ .../qubit/test_parametric_ops_single_qubit.py | 41 +++++++++ .../templates/test_resource_qft.py} | 15 ++-- 14 files changed, 294 insertions(+), 97 deletions(-) create mode 100644 pennylane/labs/resource_estimation/ops/__init__.py create mode 100644 pennylane/labs/resource_estimation/ops/op_math/__init__.py create mode 100644 pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py create mode 100644 pennylane/labs/resource_estimation/ops/qubit/__init__.py create mode 100644 pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py create mode 100644 pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py delete mode 100644 pennylane/labs/resource_estimation/ops/resource_qft.py create mode 100644 pennylane/labs/resource_estimation/templates/__init__.py create mode 100644 pennylane/labs/resource_estimation/templates/resource_qft.py create mode 100644 pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py create mode 100644 pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py create mode 100644 pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py rename pennylane/labs/tests/{tests_resource_estimation/test_resource_ops.py => resource_estimation/templates/test_resource_qft.py} (85%) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index eb28d5b321b..70e8a96e46e 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -15,3 +15,15 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources + +from .ops import( + ResourceCNOT, + ResourceControlledPhaseShift, + ResourceHadamard, + ResourceRZ, + ResourceT, + ) + +from .templates import( + ResourceQFT, + ) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py new file mode 100644 index 00000000000..924e1f88d64 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -0,0 +1,10 @@ +from .qubit import( + ResourceHadamard, + ResourceRZ, + ResourceT, +) + +from .op_math import( + ResourceCNOT, + ResourceControlledPhaseShift, +) diff --git a/pennylane/labs/resource_estimation/ops/op_math/__init__.py b/pennylane/labs/resource_estimation/ops/op_math/__init__.py new file mode 100644 index 00000000000..e8e76804b47 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/op_math/__init__.py @@ -0,0 +1 @@ +from .controlled_ops import * diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py new file mode 100644 index 00000000000..1fbb66cb02a --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -0,0 +1,34 @@ +from typing import Dict + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourcesNotDefined + +#pylint: disable=too-many-ancestors + +class ResourceControlledPhaseShift(qml.ControlledPhaseShift, ResourceConstructor): + """Resource class for ControlledPhaseShift""" + + @staticmethod + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + gate_types = {} + + cnot = CompressedResourceOp(qml.CNOT, {}) + rz = CompressedResourceOp(qml.RZ, {}) + + gate_types[cnot] = 2 + gate_types[rz] = 3 + + return gate_types + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.ControlledPhaseShift, {}) + +class ResourceCNOT(qml.CNOT, ResourceConstructor): + """Resource class for CNOT""" + + @staticmethod + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + raise ResourcesNotDefined + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.CNOT, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py new file mode 100644 index 00000000000..ff95ef9b423 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -0,0 +1,8 @@ +from .non_parametric_ops import( + ResourceHadamard, + ResourceT, + ) + +from .parametric_ops_single_qubit import( + ResourceRZ, + ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py new file mode 100644 index 00000000000..be09a373c32 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -0,0 +1,24 @@ +from typing import Dict + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourcesNotDefined + +class ResourceHadamard(qml.Hadamard, ResourceConstructor): + """Resource class for Hadamard""" + + @staticmethod + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + raise ResourcesNotDefined + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.Hadamard, {}) + +class ResourceT(qml.T, ResourceConstructor): + """Resource class for T""" + + @staticmethod + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + raise ResourcesNotDefined + + def resource_rep(self) -> CompressedResourceOp: + return CompressedResourceOp(qml.T, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py new file mode 100644 index 00000000000..b6896a53bf2 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -0,0 +1,25 @@ +import numpy as np +from typing import Dict + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor + +def _rotation_resources(epsilon=10e-3): + gate_types = {} + + num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) + t = CompressedResourceOp(qml.T, {}) + gate_types[t] = num_gates + + return gate_types + +class ResourceRZ(qml.RZ, ResourceConstructor): + """Resource class for RZ""" + + @staticmethod + def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: + print(epsilon) + return _rotation_resources(epsilon=epsilon) + + def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + return CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) diff --git a/pennylane/labs/resource_estimation/ops/resource_qft.py b/pennylane/labs/resource_estimation/ops/resource_qft.py deleted file mode 100644 index ea72a7e5365..00000000000 --- a/pennylane/labs/resource_estimation/ops/resource_qft.py +++ /dev/null @@ -1,89 +0,0 @@ -import numpy as np -import pennylane as qml - -from .. import ResourceConstructor, CompressedResourceOp, ResourcesNotDefined - -#pylint: disable=too-many-ancestors,arguments-differ - -class ResourceQFT(qml.QFT, ResourceConstructor): - """Resource class for QFT""" - - @staticmethod - def _resource_decomp(num_wires) -> dict: - if not isinstance(num_wires, int): - raise TypeError("num_wires must be an int.") - - if num_wires < 1: - raise ValueError("num_wires must be greater than 0.") - - gate_types = {} - - hadamard = CompressedResourceOp(qml.Hadamard, {}) - swap = CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) - - gate_types[hadamard] = num_wires - gate_types[swap] = num_wires // 2 - gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 - - return gate_types - - def resource_rep(self) -> CompressedResourceOp: - params = {"num_wires": len(self.wires)} - return CompressedResourceOp(qml.QFT, params) - - -class ResourceControlledPhaseShift(qml.ControlledPhaseShift, ResourceConstructor): - """Resource class for ControlledPhaseShift""" - - @staticmethod - def _resource_decomp() -> dict: - gate_types = {} - - cnot = CompressedResourceOp(qml.CNOT, {}) - rz = CompressedResourceOp(qml.RZ, {}) - - gate_types[cnot] = 2 - gate_types[rz] = 3 - - return gate_types - - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.ControlledPhaseShift, {}) - -class ResourceCNOT(qml.CNOT, ResourceConstructor): - """Resource class for CNOT""" - - @staticmethod - def _resource_decomp() -> dict: - raise ResourcesNotDefined - - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.CNOT, {}) - -class ResourceRZ(qml.RZ, ResourceConstructor): - """Resource class for RZ""" - - @staticmethod - def _resource_decomp(epsilon=10e-3) -> dict: - gate_types = {} - - num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) - t = CompressedResourceOp(qml.T, {}) - gate_types[t] = num_gates - - return gate_types - - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.RZ, {}) - - -class ResourceT(qml.T, ResourceConstructor): - """Resource class for T""" - - @staticmethod - def _resource_decomp() -> dict: - raise ResourcesNotDefined - - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.T, {}) diff --git a/pennylane/labs/resource_estimation/templates/__init__.py b/pennylane/labs/resource_estimation/templates/__init__.py new file mode 100644 index 00000000000..cead6cd577a --- /dev/null +++ b/pennylane/labs/resource_estimation/templates/__init__.py @@ -0,0 +1 @@ +from .resource_qft import ResourceQFT diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py new file mode 100644 index 00000000000..f4f4626f492 --- /dev/null +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -0,0 +1,30 @@ +import pennylane as qml + +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor + +class ResourceQFT(qml.QFT, ResourceConstructor): + """Resource class for QFT""" + + @staticmethod + def _resource_decomp(num_wires) -> dict: + if not isinstance(num_wires, int): + raise TypeError("num_wires must be an int.") + + if num_wires < 1: + raise ValueError("num_wires must be greater than 0.") + + gate_types = {} + + hadamard = CompressedResourceOp(qml.Hadamard, {}) + swap = CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) + + gate_types[hadamard] = num_wires + gate_types[swap] = num_wires // 2 + gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 + + return gate_types + + def resource_rep(self) -> CompressedResourceOp: + params = {"num_wires": len(self.wires)} + return CompressedResourceOp(qml.QFT, params) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py new file mode 100644 index 00000000000..b34c62a6325 --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -0,0 +1,67 @@ +import pytest + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceCNOT, ResourceControlledPhaseShift, ResourcesNotDefined + +class TestControlledPhaseShift: + """Test ResourceControlledPhaseShift""" + + @pytest.mark.parametrize("phi, wires", + [ + (1.2, [0, 1]), + (2.4, [2, 3]), + ]) + def test_resources(self, phi, wires): + """Test the resources method""" + op = ResourceControlledPhaseShift(phi, wires) + + expected = { + CompressedResourceOp(qml.CNOT, {}): 2, + CompressedResourceOp(qml.RZ, {}): 3, + } + + assert op.resources() == expected + + @pytest.mark.parametrize("phi, wires", + [ + (1.2, [0, 1]), + (2.4, [2, 3]), + ]) + def test_resource_rep(self, phi, wires): + """Test the compressed representation""" + op = ResourceControlledPhaseShift(phi, wires) + expected = CompressedResourceOp(qml.ControlledPhaseShift, {}) + + assert op.resource_rep() == expected + + + @pytest.mark.parametrize("phi, wires", + [ + (1.2, [0, 1]), + (2.4, [2, 3]), + ]) + def test_resources_from_rep(self, phi, wires): + """Compute the resources from the compressed representation""" + op = ResourceControlledPhaseShift(phi, wires) + + expected = { + CompressedResourceOp(qml.CNOT, {}): 2, + CompressedResourceOp(qml.RZ, {}): 3, + } + + assert op.resources(*op.resource_rep().params) == expected + +class TestCNOT: + """Test ResourceCNOT""" + + def test_resources(self): + """Test that the resources method is not implemented""" + op = ResourceCNOT([0, 1]) + with pytest.raises(ResourcesNotDefined): + op.resources() + + def test_resource_rep(self): + """Test the compressed representation""" + op = ResourceCNOT([0, 1]) + expected = CompressedResourceOp(qml.CNOT, {}) + assert op.resource_rep() == expected diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py new file mode 100644 index 00000000000..0c5aed6ad6e --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -0,0 +1,34 @@ +import pytest + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceHadamard, ResourceT, ResourcesNotDefined + +class TestHadamard(): + """Tests for ResourceHadamard""" + + def test_resources(self): + """Test that ResourceHadamard does not implement a decomposition""" + op = ResourceHadamard(0) + with pytest.raises(ResourcesNotDefined): + op.resources() + + def test_resource_rep(self): + """Test that the compact representation is correct""" + op = ResourceHadamard(0) + expected = CompressedResourceOp(qml.Hadamard, {}) + assert op.resource_rep() == expected + +class TestT(): + """Tests for ResourceT""" + + def test_resources(self): + """Test that ResourceT does not implement a decomposition""" + op = ResourceT(0) + with pytest.raises(ResourcesNotDefined): + op.resources() + + def test_resource_rep(self): + """Test that the compact representation is correct""" + op = ResourceT(0) + expected = CompressedResourceOp(qml.T, {}) + assert op.resource_rep() == expected diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py new file mode 100644 index 00000000000..abd98b94185 --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -0,0 +1,41 @@ +import numpy as np +import pytest + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourcesNotDefined, ResourceRZ +from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import _rotation_resources + +@pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) +def test_rotation_resources(epsilon): + """Test the hardcoded resources used for RX, RY, RZ""" + gate_types = {} + + num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) + t = CompressedResourceOp(qml.T, {}) + gate_types[t] = num_gates + assert gate_types == _rotation_resources(epsilon=epsilon) + +class TestRZ: + """Test ResourceRZ""" + + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) + def test_resources(self, epsilon): + """Test the resources method""" + op = ResourceRZ(1.24, wires=0) + assert op.resources(epsilon=epsilon) == _rotation_resources(epsilon=epsilon) + + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) + def test_resource_rep(self, epsilon): + """Test the compact representation""" + op = ResourceRZ(1.24, wires=0) + expected = CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + + assert op.resource_rep(epsilon=epsilon) == expected + + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) + def test_resources_from_rep(self, epsilon): + """Test the resources can be obtained from the compact representation""" + op = ResourceRZ(1.24, wires=0) + expected = _rotation_resources(epsilon=epsilon) + + assert ResourceRZ.resources(**op.resource_rep(epsilon=epsilon).params) == expected diff --git a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py similarity index 85% rename from pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py rename to pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 179194da8f6..024195a1c69 100644 --- a/pennylane/labs/tests/tests_resource_estimation/test_resource_ops.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -1,8 +1,7 @@ import pytest import pennylane as qml -import pennylane.labs.resource_estimation.ops as ops -from pennylane.labs.resource_estimation import CompressedResourceOp +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceQFT class TestQFT: """Test the ResourceQFT class""" @@ -27,14 +26,14 @@ def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift ctrl_phase_shift: num_ctrl_phase_shift } - assert ops.ResourceQFT.resources(num_wires) == expected + assert ResourceQFT.resources(num_wires) == expected @pytest.mark.parametrize("num_wires", [1, 2, 3, 4]) def test_resource_rep(self, num_wires): """Test the resource_rep returns the correct CompressedResourceOp""" expected = CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) - op = ops.ResourceQFT(wires=range(num_wires)) + op = ResourceQFT(wires=range(num_wires)) assert op.resource_rep() == expected @@ -59,8 +58,8 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph ctrl_phase_shift: num_ctrl_phase_shift } - rep = ops.ResourceQFT(wires=range(num_wires)).resource_rep() - actual = ops.ResourceQFT.resources(**rep.params) + rep = ResourceQFT(wires=range(num_wires)).resource_rep() + actual = ResourceQFT.resources(**rep.params) assert actual == expected @@ -68,10 +67,10 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph def test_type_error(self, num_wires): """Test that resources correctly raises a TypeError""" with pytest.raises(TypeError, match="num_wires must be an int."): - ops.ResourceQFT.resources(num_wires) + ResourceQFT.resources(num_wires) @pytest.mark.parametrize("num_wires", [0, -1]) def test_value_error(self, num_wires): """Test that resources correctly raises a ValueError""" with pytest.raises(ValueError, match="num_wires must be greater than 0."): - ops.ResourceQFT.resources(num_wires) + ResourceQFT.resources(num_wires) From dc2182f65fc79981bcabe15b505026065e5922b7 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 25 Oct 2024 11:14:32 -0400 Subject: [PATCH 031/335] Added more tests and formating --- pennylane/labs/__init__.py | 15 ++-- .../test_resource_container.py | 69 ++++++++++++++++++- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 5dd984bbb75..d51d8e8c53a 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -22,17 +22,22 @@ This module is experimental. Frequent changes will occur, with no guarantees of stability or backwards compatibility. -.. currentmodule:: pennylane.labs +.. currentmodule:: pennylane.labs.resource_estimation -Modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Resource Estimation +~~~~~~~~~~~~~~~~~~~ .. autosummary:: :toctree: api + ~Resources + ~CompressedResourceOp """ -# from .resource_estimation import * +from .resource_estimation import ( + Resources, + CompressedResourceOp, +) -__all__ = [] +__all__ = ["Resources", "CompressedResourceOp"] diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index 920b1a5f800..a94704e8a90 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -20,7 +20,12 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, Resources +from pennylane.labs.resource_estimation.resource_container import ( + CompressedResourceOp, + Resources, + _combine_dict, + _scale_dict, +) class TestCompressedResourceOp: @@ -115,6 +120,34 @@ def test_init(self, r, attribute_tup): assert r.num_gates == num_gates assert r.gate_types == gate_types + # @pytest.mark.parametrize("in_place", (False, True)) + # @pytest.mark.parametrize("resource_obj", resource_quantities) + # def test_add_in_series(self, resource_obj, in_place): + # """Test the add_in_series function works with Resoruces""" + # other_obj = Resources( + # num_wires=2, + # num_gates=6, + # gate_types=defaultdict(int, {"RZ":2, "CNOT":1, "RY":2, "Hadamard":1}) + # ) + + # resultant_obj = resource_obj.add_in_series(other_obj, in_place=in_place) + # expected_res_obj = Resources( + # num_wires = other_obj.num_wires + # ) + # assert True + + def test_add_in_parallel(self): + """Test the add_in_parallel function works with Resoruces""" + assert True + + def test_mul_in_series(self): + """Test the mul_in_series function works with Resoruces""" + assert True + + def test_mul_in_parallel(self): + """Test the mul_in_parallel function works with Resoruces""" + assert True + test_str_data = ( ("wires: 0\n" + "gates: 0\n" + "gate_types:\n" + "{}"), ("wires: 5\n" + "gates: 0\n" + "gate_types:\n" + "{}"), @@ -133,3 +166,37 @@ def test_ipython_display(self, r, rep, capsys): r._ipython_display_() # pylint: disable=protected-access captured = capsys.readouterr() assert rep in captured.out + + +@pytest.mark.parametrize("in_place", [False, True]) +def test_combine_dict(in_place): + """Test that we can combine dictionaries as expected.""" + d1 = defaultdict(int, {"a": 2, "b": 4, "c": 6}) + d2 = defaultdict(int, {"a": 1, "b": 2, "d": 3}) + + result = _combine_dict(d1, d2, in_place=in_place) + expected = defaultdict(int, {"a": 3, "b": 6, "c": 6, "d": 3}) + + assert result == expected + + if in_place: + assert result is d1 + else: + assert result is not d1 + + +@pytest.mark.parametrize("scaler", (1, 2, 3)) +@pytest.mark.parametrize("in_place", (False, True)) +def test_scale_dict(scaler, in_place): + """Test that we can scale the values of a dictionary as expected.""" + d1 = defaultdict(int, {"a": 2, "b": 4, "c": 6}) + + expected = defaultdict(int, {k: scaler * v for k, v in d1.items()}) + result = _scale_dict(d1, scaler, in_place=in_place) + + assert result == expected + + if in_place: + assert result is d1 + else: + assert result is not d1 From cf60d2e963a645574b99f687c03efebfbab3168f Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 25 Oct 2024 12:52:46 -0400 Subject: [PATCH 032/335] removing print statement --- .../resource_estimation/ops/qubit/parametric_ops_single_qubit.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index b6896a53bf2..fc907bf6c9b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -18,7 +18,6 @@ class ResourceRZ(qml.RZ, ResourceConstructor): @staticmethod def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: - print(epsilon) return _rotation_resources(epsilon=epsilon) def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: From 84e5ade92a369bc3a7ffb989849e90851b981723 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 25 Oct 2024 13:47:26 -0400 Subject: [PATCH 033/335] docs and linting --- pennylane/labs/__init__.py | 10 +++++ .../labs/resource_estimation/__init__.py | 2 +- .../resource_constructor.py | 44 +++++++++++++++---- .../test_resource_constructor.py | 13 +++++- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index d51d8e8c53a..997946105ea 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -32,12 +32,22 @@ ~Resources ~CompressedResourceOp + ~ResourceConstructor +Errors +~~~~~~ + +.. autosummary:: + :toctree: api + + ~ResourcesNotDefined """ from .resource_estimation import ( Resources, CompressedResourceOp, + ResourceConstructor, + ResourcesNotDefined, ) __all__ = ["Resources", "CompressedResourceOp"] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 1a600cdb0d8..eb28d5b321b 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -13,5 +13,5 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .resource_constructor import ResourceConstructor +from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index ef1d2973eca..c88e85ae69e 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -5,20 +5,46 @@ class ResourceConstructor(ABC): - r""" This is an abstract class that defines the methods a PennyLane Operator + r"""This is an abstract class that defines the methods a PennyLane Operator must implement in order to be used for resource estimation. .. details:: **Example** - import pennylane as qml - class ResourceQFT(qml.QFT, ResourceConstructor): - def compute_resources(num_wires): - return + A PennyLane Operator can be extended for resource estimation by creating a new class that inherits from both the Operator and Resource Constructor. + Here is an example showing how to extend ``qml.QFT`` for resource estimation. - def resource_rep(self): - return + .. code-block:: python + + import pennylane as qml + from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor + + class ResourceQFT(qml.QFT, ResourceConstructor): + + @staticmethod + def _resource_decomp(num_wires) -> dict: + if not isinstance(num_wires, int): + raise TypeError("num_wires must be an int.") + + if num_wires < 1: + raise ValueError("num_wires must be greater than 0.") + + gate_types = {} + + hadamard = CompressedResourceOp(qml.Hadamard, {}) + swap = CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) + + gate_types[hadamard] = num_wires + gate_types[swap] = num_wires // 2 + gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 + + return gate_types + + def resource_rep(self) -> CompressedResourceOp: + params = {"num_wires": len(self.wires)} + return CompressedResourceOp(qml.QFT, params) """ @staticmethod @@ -35,7 +61,7 @@ def resources(cls, *args, **kwargs): @classmethod def set_resources(cls, new_func: Callable) -> None: - """Override the compute_resources method.""" + """Set a custom resource method.""" cls.resources = new_func @abstractmethod @@ -45,4 +71,4 @@ def resource_rep(self) -> CompressedResourceOp: class ResourcesNotDefined(Exception): - """Exception to be raised when a ResourceConstructor does not implement compute_resources""" + """Exception to be raised when a ``ResourceConstructor`` does not implement _resource_decomp""" diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 71b594f98a2..350a0fbb45e 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -2,22 +2,31 @@ from pennylane.labs.resource_estimation import ResourceConstructor + def test_abstract_resource_decomp(): class DummyClass(ResourceConstructor): def resource_rep(self): return - with pytest.raises(TypeError, match="Can't instantiate abstract class DummyClass with abstract method _resource_decomp"): + with pytest.raises( + TypeError, + match="Can't instantiate abstract class DummyClass with abstract method _resource_decomp", + ): DummyClass() + def test_abstract_resource_rep(): class DummyClass(ResourceConstructor): def _resource_decomp(): return - with pytest.raises(TypeError, match="Can't instantiate abstract class DummyClass with abstract method resource_rep"): + with pytest.raises( + TypeError, + match="Can't instantiate abstract class DummyClass with abstract method resource_rep", + ): DummyClass() + def test_set_resources(): class DummyClass(ResourceConstructor): def resource_rep(self): From c41fee690ed42f3b577e776cdedf48adf2865b08 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 25 Oct 2024 16:46:53 -0400 Subject: [PATCH 034/335] finalized tests --- .../resource_estimation/resource_container.py | 2 +- .../test_resource_container.py | 179 ++++++++++++++---- 2 files changed, 147 insertions(+), 34 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 78b88c991b8..8c0a51581bd 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -230,7 +230,7 @@ def mul_in_series(first: Resources, scaler: int, in_place=False) -> Resources: return Resources(first.num_wires, new_gates, new_gate_types) -def mul_in_parllel(first: Resources, scaler: int, in_place=False) -> Resources: +def mul_in_parallel(first: Resources, scaler: int, in_place=False) -> Resources: """Multiply two resources assuming the circuits are executed in parallel. Args: diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index a94704e8a90..f1ae5dda674 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -15,6 +15,7 @@ Test base Resource class and its associated methods """ +import copy from collections import defaultdict import pytest @@ -25,6 +26,10 @@ Resources, _combine_dict, _scale_dict, + add_in_parallel, + add_in_series, + mul_in_parallel, + mul_in_series, ) @@ -95,18 +100,14 @@ class TestResources: resource_quantities = ( Resources(), - Resources(5, 0, {}), - Resources( - 1, - 3, - defaultdict(int, {"Hadamard": 1, "PauliZ": 2}), - ), - Resources(4, 2, {"Hadamard": 1, "CNOT": 1}), + Resources(5, 0, defaultdict(int, {})), + Resources(1, 3, defaultdict(int, {"Hadamard": 1, "PauliZ": 2})), + Resources(4, 2, defaultdict(int, {"Hadamard": 1, "CNOT": 1})), ) resource_parameters = ( - (0, 0, {}), - (5, 0, {}), + (0, 0, defaultdict(int, {})), + (5, 0, defaultdict(int, {})), (1, 3, defaultdict(int, {"Hadamard": 1, "PauliZ": 2})), (4, 2, defaultdict(int, {"Hadamard": 1, "CNOT": 1})), ) @@ -120,32 +121,144 @@ def test_init(self, r, attribute_tup): assert r.num_gates == num_gates assert r.gate_types == gate_types - # @pytest.mark.parametrize("in_place", (False, True)) - # @pytest.mark.parametrize("resource_obj", resource_quantities) - # def test_add_in_series(self, resource_obj, in_place): - # """Test the add_in_series function works with Resoruces""" - # other_obj = Resources( - # num_wires=2, - # num_gates=6, - # gate_types=defaultdict(int, {"RZ":2, "CNOT":1, "RY":2, "Hadamard":1}) - # ) - - # resultant_obj = resource_obj.add_in_series(other_obj, in_place=in_place) - # expected_res_obj = Resources( - # num_wires = other_obj.num_wires - # ) - # assert True - - def test_add_in_parallel(self): + expected_results_add_series = ( + Resources(2, 6, defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1})), + Resources(5, 6, defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1})), + Resources( + 2, 9, defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 2, "PauliZ": 2}) + ), + Resources(4, 8, defaultdict(int, {"RZ": 2, "CNOT": 2, "RY": 2, "Hadamard": 2})), + ) + + @pytest.mark.parametrize("in_place", (False, True)) + @pytest.mark.parametrize( + "resource_obj, expected_res_obj", zip(resource_quantities, expected_results_add_series) + ) + def test_add_in_series(self, resource_obj, expected_res_obj, in_place): + """Test the add_in_series function works with Resoruces""" + resource_obj = copy.deepcopy(resource_obj) + other_obj = Resources( + num_wires=2, + num_gates=6, + gate_types=defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1}), + ) + + resultant_obj = add_in_series(resource_obj, other_obj, in_place=in_place) + assert resultant_obj == expected_res_obj + + if in_place: + assert resultant_obj is resource_obj + + expected_results_add_parallel = ( + Resources(2, 6, defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1})), + Resources(7, 6, defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1})), + Resources( + 3, 9, defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 2, "PauliZ": 2}) + ), + Resources(6, 8, defaultdict(int, {"RZ": 2, "CNOT": 2, "RY": 2, "Hadamard": 2})), + ) + + @pytest.mark.parametrize("in_place", (False, True)) + @pytest.mark.parametrize( + "resource_obj, expected_res_obj", zip(resource_quantities, expected_results_add_parallel) + ) + def test_add_in_parallel(self, resource_obj, expected_res_obj, in_place): """Test the add_in_parallel function works with Resoruces""" - assert True + resource_obj = copy.deepcopy(resource_obj) + other_obj = Resources( + num_wires=2, + num_gates=6, + gate_types=defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1}), + ) + + resultant_obj = add_in_parallel(resource_obj, other_obj, in_place=in_place) + assert resultant_obj == expected_res_obj - def test_mul_in_series(self): + if in_place: + assert resultant_obj is resource_obj + + expected_results_mul_series = ( + Resources( + num_wires=2, + num_gates=6, + gate_types=defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1}), + ), + Resources( + num_wires=2, + num_gates=12, + gate_types=defaultdict(int, {"RZ": 4, "CNOT": 2, "RY": 4, "Hadamard": 2}), + ), + Resources( + num_wires=2, + num_gates=18, + gate_types=defaultdict(int, {"RZ": 6, "CNOT": 3, "RY": 6, "Hadamard": 3}), + ), + Resources( + num_wires=2, + num_gates=30, + gate_types=defaultdict(int, {"RZ": 10, "CNOT": 5, "RY": 10, "Hadamard": 5}), + ), + ) + + @pytest.mark.parametrize("in_place", (False, True)) + @pytest.mark.parametrize( + "scalar, expected_res_obj", zip((1, 2, 3, 5), expected_results_mul_series) + ) + def test_mul_in_series(self, scalar, expected_res_obj, in_place): """Test the mul_in_series function works with Resoruces""" + resource_obj = Resources( + num_wires=2, + num_gates=6, + gate_types=defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1}), + ) + + resultant_obj = mul_in_series(resource_obj, scalar, in_place=in_place) + assert resultant_obj == expected_res_obj + + if in_place: + assert resultant_obj is resource_obj assert True - def test_mul_in_parallel(self): + expected_results_mul_parallel = ( + Resources( + num_wires=2, + num_gates=6, + gate_types=defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1}), + ), + Resources( + num_wires=4, + num_gates=12, + gate_types=defaultdict(int, {"RZ": 4, "CNOT": 2, "RY": 4, "Hadamard": 2}), + ), + Resources( + num_wires=6, + num_gates=18, + gate_types=defaultdict(int, {"RZ": 6, "CNOT": 3, "RY": 6, "Hadamard": 3}), + ), + Resources( + num_wires=10, + num_gates=30, + gate_types=defaultdict(int, {"RZ": 10, "CNOT": 5, "RY": 10, "Hadamard": 5}), + ), + ) + + @pytest.mark.parametrize("in_place", (False, True)) + @pytest.mark.parametrize( + "scalar, expected_res_obj", zip((1, 2, 3, 5), expected_results_mul_parallel) + ) + def test_mul_in_parallel(self, scalar, expected_res_obj, in_place): """Test the mul_in_parallel function works with Resoruces""" + resource_obj = Resources( + num_wires=2, + num_gates=6, + gate_types=defaultdict(int, {"RZ": 2, "CNOT": 1, "RY": 2, "Hadamard": 1}), + ) + + resultant_obj = mul_in_parallel(resource_obj, scalar, in_place=in_place) + assert resultant_obj == expected_res_obj + + if in_place: + assert resultant_obj is resource_obj assert True test_str_data = ( @@ -185,14 +298,14 @@ def test_combine_dict(in_place): assert result is not d1 -@pytest.mark.parametrize("scaler", (1, 2, 3)) +@pytest.mark.parametrize("scalar", (1, 2, 3)) @pytest.mark.parametrize("in_place", (False, True)) -def test_scale_dict(scaler, in_place): +def test_scale_dict(scalar, in_place): """Test that we can scale the values of a dictionary as expected.""" d1 = defaultdict(int, {"a": 2, "b": 4, "c": 6}) - expected = defaultdict(int, {k: scaler * v for k, v in d1.items()}) - result = _scale_dict(d1, scaler, in_place=in_place) + expected = defaultdict(int, {k: scalar * v for k, v in d1.items()}) + result = _scale_dict(d1, scalar, in_place=in_place) assert result == expected From 533f05ff4320e9749a54fb60c7c2d0ae37d23292 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 25 Oct 2024 17:05:24 -0400 Subject: [PATCH 035/335] lint + format --- .../labs/tests/resource_estimation/test_resource_container.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index f1ae5dda674..d2c7a6e5710 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -14,7 +14,7 @@ """ Test base Resource class and its associated methods """ - +# pylint:disable=protected-access, no-self-use import copy from collections import defaultdict @@ -34,6 +34,7 @@ class TestCompressedResourceOp: + """Testing the methods and attributes of the CompressedResourceOp class""" hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) compressed_op_args_lst = ( @@ -81,7 +82,6 @@ def test_equality(self): CmprssedQSVT3 = CompressedResourceOp(qml.QSVT, {"num_angles": 5, "num_wires": 3}) Other = CompressedResourceOp(qml.QFT, {"num_wires": 3}) - assert CmprssedQSVT1 == CmprssedQSVT1 # compare same object assert CmprssedQSVT1 == CmprssedQSVT2 # compare identical instance assert CmprssedQSVT1 == CmprssedQSVT3 # compare swapped parameters assert CmprssedQSVT1 != Other From 225f85e4490090aa52396a4754880047fcb3c872 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 25 Oct 2024 17:19:09 -0400 Subject: [PATCH 036/335] fix formatting --- .../labs/resource_estimation/resource_container.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 8c0a51581bd..86809b87199 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -164,7 +164,7 @@ def _ipython_display_(self): def add_in_series(first: Resources, other: Resources, in_place=False) -> Resources: - """Add two resources assuming the circuits are executed in series. + r"""Add two resources assuming the circuits are executed in series. Args: first (Resources): first resource object to combine @@ -187,7 +187,7 @@ def add_in_series(first: Resources, other: Resources, in_place=False) -> Resourc def add_in_parallel(first: Resources, other: Resources, in_place=False) -> Resources: - """Add two resources assuming the circuits are executed in parallel. + r"""Add two resources assuming the circuits are executed in parallel. Args: first (Resources): first resource object to combine @@ -210,7 +210,7 @@ def add_in_parallel(first: Resources, other: Resources, in_place=False) -> Resou def mul_in_series(first: Resources, scaler: int, in_place=False) -> Resources: - """Multiply two resources assuming the circuits are executed in series. + r"""Multiply two resources assuming the circuits are executed in series. Args: first (Resources): first resource object to combine @@ -231,7 +231,7 @@ def mul_in_series(first: Resources, scaler: int, in_place=False) -> Resources: def mul_in_parallel(first: Resources, scaler: int, in_place=False) -> Resources: - """Multiply two resources assuming the circuits are executed in parallel. + r"""Multiply two resources assuming the circuits are executed in parallel. Args: first (Resources): first resource object to combine @@ -254,6 +254,7 @@ def mul_in_parallel(first: Resources, scaler: int, in_place=False) -> Resources: def _combine_dict(dict1: defaultdict, dict2: defaultdict, in_place=False): + r"""Private function which combines two dictionaries together.""" combined_dict = dict1 if in_place else copy.copy(dict1) for k, v in dict2.items(): @@ -263,6 +264,8 @@ def _combine_dict(dict1: defaultdict, dict2: defaultdict, in_place=False): def _scale_dict(dict1: defaultdict, scaler: int, in_place=False): + r"""Private function which scales the values in a dictionary.""" + combined_dict = dict1 if in_place else copy.copy(dict1) for k in combined_dict: From cf62dd3e676a2ea3d089b79a77fab709ab5d783d Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 28 Oct 2024 09:16:35 -0400 Subject: [PATCH 037/335] run ci From aa26f665f92e7bd71dfc5a42eff1a7c7b7bb9fe1 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 09:15:17 -0400 Subject: [PATCH 038/335] RX and RY --- .../labs/resource_estimation/__init__.py | 2 ++ .../labs/resource_estimation/ops/__init__.py | 2 ++ .../resource_estimation/ops/qubit/__init__.py | 2 ++ .../ops/qubit/parametric_ops_single_qubit.py | 20 +++++++++++ .../qubit/test_parametric_ops_single_qubit.py | 35 +++++++++++-------- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 70e8a96e46e..4ae041c2f59 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -20,7 +20,9 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceHadamard, + ResourceRX, ResourceRZ, + ResourceRY, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 924e1f88d64..895829d5c0b 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,5 +1,7 @@ from .qubit import( ResourceHadamard, + ResourceRX, + ResourceRY, ResourceRZ, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index ff95ef9b423..23761116bdc 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -4,5 +4,7 @@ ) from .parametric_ops_single_qubit import( + ResourceRX, + ResourceRY, ResourceRZ, ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index fc907bf6c9b..db01089c5db 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -13,6 +13,26 @@ def _rotation_resources(epsilon=10e-3): return gate_types +class ResourceRX(qml.RX, ResourceConstructor): + """Resource class for RX""" + + @staticmethod + def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: + return _rotation_resources(epsilon=epsilon) + + def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + return CompressedResourceOp(qml.RX, {"epsilon": epsilon}) + +class ResourceRY(qml.RY, ResourceConstructor): + """Resource class for RY""" + + @staticmethod + def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: + return _rotation_resources(epsilon=epsilon) + + def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + return CompressedResourceOp(qml.RY, {"epsilon": epsilon}) + class ResourceRZ(qml.RZ, ResourceConstructor): """Resource class for RZ""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index abd98b94185..d9600176c24 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -2,7 +2,8 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourcesNotDefined, ResourceRZ +from pennylane import RX, RY, RZ #pylint: disable=unused-import +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceRX, ResourceRY, ResourceRZ from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import _rotation_resources @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) @@ -15,27 +16,33 @@ def test_rotation_resources(epsilon): gate_types[t] = num_gates assert gate_types == _rotation_resources(epsilon=epsilon) -class TestRZ: - """Test ResourceRZ""" +class TestPauliRotation: + """Test ResourceRX, ResourceRY, and ResourceRZ""" - @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) - def test_resources(self, epsilon): + params = list(zip([ResourceRX, ResourceRY, ResourceRZ], [10e-3, 10e-4, 10e-5])) + + @pytest.mark.parametrize("resource_class, epsilon", params) + def test_resources(self, epsilon, resource_class): """Test the resources method""" - op = ResourceRZ(1.24, wires=0) + + op = resource_class(1.24, wires=0) assert op.resources(epsilon=epsilon) == _rotation_resources(epsilon=epsilon) - @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) - def test_resource_rep(self, epsilon): + @pytest.mark.parametrize("resource_class, epsilon", params) + def test_resource_rep(self, epsilon, resource_class): """Test the compact representation""" - op = ResourceRZ(1.24, wires=0) - expected = CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + + op = resource_class(1.24, wires=0) + pl_class = globals()[resource_class.__name__[8:]] + expected = CompressedResourceOp(pl_class, {"epsilon": epsilon}) assert op.resource_rep(epsilon=epsilon) == expected - @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) - def test_resources_from_rep(self, epsilon): + @pytest.mark.parametrize("resource_class, epsilon", params) + def test_resources_from_rep(self, epsilon, resource_class): """Test the resources can be obtained from the compact representation""" - op = ResourceRZ(1.24, wires=0) + + op = resource_class(1.24, wires=0) expected = _rotation_resources(epsilon=epsilon) - assert ResourceRZ.resources(**op.resource_rep(epsilon=epsilon).params) == expected + assert resource_class.resources(**op.resource_rep(epsilon=epsilon).params) == expected From e7ec7357de5d3965823d036ddf13bc5fdd57d96f Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 09:44:57 -0400 Subject: [PATCH 039/335] adding S --- .../ops/qubit/non_parametric_ops.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index be09a373c32..50af2e9ff46 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -13,6 +13,24 @@ def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: def resource_rep(self) -> CompressedResourceOp: return CompressedResourceOp(qml.Hadamard, {}) +class ResourceS(qml.S, ResourceConstructor): + """Resource class for S""" + + @staticmethod + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + gate_types = {} + t = ResourceT.compute_resource_rep() + gate_types[t] = 2 + + return gate_types + + @staticmethod + def compute_resource_rep() -> CompressedResourceOp: + return CompressedResourceOp(qml.S, {}) + + def resource_rep(self) -> CompressedResourceOp: + return ResourceS.compute_resource_rep() + class ResourceT(qml.T, ResourceConstructor): """Resource class for T""" @@ -20,5 +38,9 @@ class ResourceT(qml.T, ResourceConstructor): def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: raise ResourcesNotDefined - def resource_rep(self) -> CompressedResourceOp: + @staticmethod + def compute_resource_rep() -> CompressedResourceOp: return CompressedResourceOp(qml.T, {}) + + def resource_rep(self) -> CompressedResourceOp: + return ResourceT.compute_resource_rep() From 9bba0f1fa92285a6ea84693f9fa9ccfe9548011b Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 10:02:32 -0400 Subject: [PATCH 040/335] adding S --- .../labs/resource_estimation/__init__.py | 1 + .../labs/resource_estimation/ops/__init__.py | 1 + .../resource_estimation/ops/qubit/__init__.py | 1 + .../ops/qubit/test_non_parametric_ops.py | 23 ++++++++++++++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 4ae041c2f59..68ddcc9258c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -23,6 +23,7 @@ ResourceRX, ResourceRZ, ResourceRY, + ResourceS, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 895829d5c0b..87fe3ac4e1e 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -3,6 +3,7 @@ ResourceRX, ResourceRY, ResourceRZ, + ResourceS, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index 23761116bdc..629714e7b89 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -1,5 +1,6 @@ from .non_parametric_ops import( ResourceHadamard, + ResourceS, ResourceT, ) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 0c5aed6ad6e..961ecdb7c3b 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -1,7 +1,7 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceHadamard, ResourceT, ResourcesNotDefined +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceHadamard, ResourceS, ResourceT, ResourcesNotDefined class TestHadamard(): """Tests for ResourceHadamard""" @@ -18,6 +18,27 @@ def test_resource_rep(self): expected = CompressedResourceOp(qml.Hadamard, {}) assert op.resource_rep() == expected +class TestS(): + """Tests for ResourceS""" + + def test_resources(self): + """Test that S decomposes into two Ts""" + op = ResourceS(0) + expected = {CompressedResourceOp(qml.T, {}): 2} + assert op.resources() == expected + + def test_resource_rep(self): + """Test that the compressed representation is correct""" + op = ResourceS(0) + expected = CompressedResourceOp(qml.S, {}) + assert op.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the compressed representation yields the correct resources""" + op = ResourceS(0) + expected = {CompressedResourceOp(qml.T, {}): 2} + assert op.resources(**op.resource_rep().params) == expected + class TestT(): """Tests for ResourceT""" From 1351285a79e5f064542b32e47c114fcd91d06818 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 13:45:42 -0400 Subject: [PATCH 041/335] ResourceRot --- .../ops/qubit/parametric_ops_single_qubit.py | 37 +++++++++++++++++-- .../qubit/test_parametric_ops_single_qubit.py | 3 ++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index db01089c5db..e53b3c4af40 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -20,9 +20,13 @@ class ResourceRX(qml.RX, ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + @staticmethod + def compute_resource_rep(epsilon=10e-3) -> CompressedResourceOp: return CompressedResourceOp(qml.RX, {"epsilon": epsilon}) + def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + return ResourceRX.compute_resource_rep(epsilon=epsilon) + class ResourceRY(qml.RY, ResourceConstructor): """Resource class for RY""" @@ -30,9 +34,13 @@ class ResourceRY(qml.RY, ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + @staticmethod + def compute_resource_rep(epsilon=10e-3) -> CompressedResourceOp: return CompressedResourceOp(qml.RY, {"epsilon": epsilon}) + def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + return ResourceRY.compute_resource_rep(epsilon=epsilon) + class ResourceRZ(qml.RZ, ResourceConstructor): """Resource class for RZ""" @@ -40,5 +48,28 @@ class ResourceRZ(qml.RZ, ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + @staticmethod + def compute_resource_rep(epsilon=10e-3) -> CompressedResourceOp: return CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + + def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: + return ResourceRZ.compute_resource_rep(epsilon=epsilon) + +class ResourceRot(qml.Rot, ResourceConstructor): + """Resource class for Rot""" + + @staticmethod + def _resource_decomp() -> Dict[CompressedResourceOp, int]: + rx = ResourceRX.compute_resource_rep() + ry = ResourceRY.compute_resource_rep() + rz = ResourceRZ.compute_resource_rep() + + gate_types = {rx: 1, ry: 1, rz: 1} + return gate_types + + @staticmethod + def compute_resource_rep() -> CompressedResourceOp: + return CompressedResourceOp(qml.Rot, {}) + + def resource_rep(self) -> CompressedResourceOp: + return ResourceRot.compute_resource_rep() diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index d9600176c24..ff73dea4750 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -46,3 +46,6 @@ def test_resources_from_rep(self, epsilon, resource_class): expected = _rotation_resources(epsilon=epsilon) assert resource_class.resources(**op.resource_rep(epsilon=epsilon).params) == expected + +class TestRot: + """Test ResourceRot""" From 8a1727136c17a1000798d36b85821d6a3fea4c8e Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 13:50:33 -0400 Subject: [PATCH 042/335] making resource_rep static --- .../labs/resource_estimation/resource_constructor.py | 3 ++- .../resource_estimation/test_resource_constructor.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index c88e85ae69e..a6e33bf2ada 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -64,8 +64,9 @@ def set_resources(cls, new_func: Callable) -> None: """Set a custom resource method.""" cls.resources = new_func + @staticmethod @abstractmethod - def resource_rep(self) -> CompressedResourceOp: + def resource_rep() -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 350a0fbb45e..67452b1611a 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -5,7 +5,8 @@ def test_abstract_resource_decomp(): class DummyClass(ResourceConstructor): - def resource_rep(self): + @staticmethod + def resource_rep(): return with pytest.raises( @@ -17,6 +18,7 @@ def resource_rep(self): def test_abstract_resource_rep(): class DummyClass(ResourceConstructor): + @staticmethod def _resource_decomp(): return @@ -29,9 +31,11 @@ def _resource_decomp(): def test_set_resources(): class DummyClass(ResourceConstructor): - def resource_rep(self): + @staticmethod + def resource_rep(): return + @staticmethod def _resource_decomp(): return From d961edd4bec8f9e69b819767dbd53403537d25d7 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 15:30:06 -0400 Subject: [PATCH 043/335] updating ResourceQFT and friends with static methods --- .../labs/resource_estimation/__init__.py | 1 + .../labs/resource_estimation/ops/__init__.py | 1 + .../ops/op_math/controlled_ops.py | 26 ++++++----- .../resource_estimation/ops/qubit/__init__.py | 1 + .../ops/qubit/non_parametric_ops.py | 39 +++++++++++----- .../ops/qubit/parametric_ops_single_qubit.py | 13 +++--- .../templates/resource_qft.py | 17 ++++--- .../ops/op_math/test_controlled_ops.py | 26 +++++------ .../ops/qubit/test_non_parametric_ops.py | 45 ++++++++++++++----- .../qubit/test_parametric_ops_single_qubit.py | 15 +++---- .../templates/test_resource_qft.py | 30 ++++++------- 11 files changed, 130 insertions(+), 84 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 70e8a96e46e..9ce4aa49049 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -21,6 +21,7 @@ ResourceControlledPhaseShift, ResourceHadamard, ResourceRZ, + ResourceSWAP, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 924e1f88d64..39d8327c0e3 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,6 +1,7 @@ from .qubit import( ResourceHadamard, ResourceRZ, + ResourceSWAP, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 1fbb66cb02a..4714f977d8c 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -1,34 +1,36 @@ from typing import Dict import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourcesNotDefined +import pennylane.labs.resource_estimation as re #pylint: disable=too-many-ancestors -class ResourceControlledPhaseShift(qml.ControlledPhaseShift, ResourceConstructor): +class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): """Resource class for ControlledPhaseShift""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - cnot = CompressedResourceOp(qml.CNOT, {}) - rz = CompressedResourceOp(qml.RZ, {}) + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 3 return gate_types - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.ControlledPhaseShift, {}) + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.ControlledPhaseShift, {}) -class ResourceCNOT(qml.CNOT, ResourceConstructor): +class ResourceCNOT(qml.CNOT, re.ResourceConstructor): """Resource class for CNOT""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: - raise ResourcesNotDefined + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.CNOT, {}) + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.CNOT, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index ff95ef9b423..bce433a7d0e 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -1,5 +1,6 @@ from .non_parametric_ops import( ResourceHadamard, + ResourceSWAP, ResourceT, ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index be09a373c32..a04af778187 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -1,24 +1,41 @@ from typing import Dict import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourcesNotDefined +import pennylane.labs.resource_estimation as re -class ResourceHadamard(qml.Hadamard, ResourceConstructor): +class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): """Resource class for Hadamard""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: - raise ResourcesNotDefined + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.Hadamard, {}) + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.Hadamard, {}) + +class ResourceSWAP(qml.SWAP, re.ResourceConstructor): + """Resource class for SWAP""" + + @staticmethod + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + gate_types = {} + cnot = re.ResourceCNOT.resource_rep() + gate_types[cnot] = 3 -class ResourceT(qml.T, ResourceConstructor): + return gate_types + + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.SWAP, {}) + +class ResourceT(qml.T, re.ResourceConstructor): """Resource class for T""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: - raise ResourcesNotDefined + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined - def resource_rep(self) -> CompressedResourceOp: - return CompressedResourceOp(qml.T, {}) + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.T, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index fc907bf6c9b..e15a224ddb5 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -2,23 +2,24 @@ from typing import Dict import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor +import pennylane.labs.resource_estimation as re def _rotation_resources(epsilon=10e-3): gate_types = {} num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) - t = CompressedResourceOp(qml.T, {}) + t = re.ResourceT.resource_rep() gate_types[t] = num_gates return gate_types -class ResourceRZ(qml.RZ, ResourceConstructor): +class ResourceRZ(qml.RZ, re.ResourceConstructor): """Resource class for RZ""" @staticmethod - def _resource_decomp(epsilon=10e-3) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - def resource_rep(self, epsilon=10e-3) -> CompressedResourceOp: - return CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + @staticmethod + def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index f4f4626f492..827954b2dfc 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -1,12 +1,14 @@ +from typing import Dict + import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourceHadamard, ResourceSWAP, ResourceControlledPhaseShift class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @staticmethod - def _resource_decomp(num_wires) -> dict: + def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: if not isinstance(num_wires, int): raise TypeError("num_wires must be an int.") @@ -15,9 +17,9 @@ def _resource_decomp(num_wires) -> dict: gate_types = {} - hadamard = CompressedResourceOp(qml.Hadamard, {}) - swap = CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) + hadamard = ResourceHadamard.resource_rep() + swap = ResourceSWAP.resource_rep() + ctrl_phase_shift = ResourceControlledPhaseShift.resource_rep() gate_types[hadamard] = num_wires gate_types[swap] = num_wires // 2 @@ -25,6 +27,7 @@ def _resource_decomp(num_wires) -> dict: return gate_types - def resource_rep(self) -> CompressedResourceOp: - params = {"num_wires": len(self.wires)} + @staticmethod + def resource_rep(num_wires) -> CompressedResourceOp: + params = {"num_wires": num_wires} return CompressedResourceOp(qml.QFT, params) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index b34c62a6325..5873289a730 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -1,7 +1,7 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceCNOT, ResourceControlledPhaseShift, ResourcesNotDefined +import pennylane.labs.resource_estimation as re class TestControlledPhaseShift: """Test ResourceControlledPhaseShift""" @@ -13,11 +13,11 @@ class TestControlledPhaseShift: ]) def test_resources(self, phi, wires): """Test the resources method""" - op = ResourceControlledPhaseShift(phi, wires) + op = re.ResourceControlledPhaseShift(phi, wires) expected = { - CompressedResourceOp(qml.CNOT, {}): 2, - CompressedResourceOp(qml.RZ, {}): 3, + re.CompressedResourceOp(qml.CNOT, {}): 2, + re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } assert op.resources() == expected @@ -29,8 +29,8 @@ def test_resources(self, phi, wires): ]) def test_resource_rep(self, phi, wires): """Test the compressed representation""" - op = ResourceControlledPhaseShift(phi, wires) - expected = CompressedResourceOp(qml.ControlledPhaseShift, {}) + op = re.ResourceControlledPhaseShift(phi, wires) + expected = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) assert op.resource_rep() == expected @@ -42,11 +42,11 @@ def test_resource_rep(self, phi, wires): ]) def test_resources_from_rep(self, phi, wires): """Compute the resources from the compressed representation""" - op = ResourceControlledPhaseShift(phi, wires) + op = re.ResourceControlledPhaseShift(phi, wires) expected = { - CompressedResourceOp(qml.CNOT, {}): 2, - CompressedResourceOp(qml.RZ, {}): 3, + re.CompressedResourceOp(qml.CNOT, {}): 2, + re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } assert op.resources(*op.resource_rep().params) == expected @@ -56,12 +56,12 @@ class TestCNOT: def test_resources(self): """Test that the resources method is not implemented""" - op = ResourceCNOT([0, 1]) - with pytest.raises(ResourcesNotDefined): + op = re.ResourceCNOT([0, 1]) + with pytest.raises(re.ResourcesNotDefined): op.resources() def test_resource_rep(self): """Test the compressed representation""" - op = ResourceCNOT([0, 1]) - expected = CompressedResourceOp(qml.CNOT, {}) + op = re.ResourceCNOT([0, 1]) + expected = re.CompressedResourceOp(qml.CNOT, {}) assert op.resource_rep() == expected diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 0c5aed6ad6e..e9aac1a42b2 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -1,34 +1,57 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceHadamard, ResourceT, ResourcesNotDefined +import pennylane.labs.resource_estimation as re class TestHadamard(): """Tests for ResourceHadamard""" def test_resources(self): """Test that ResourceHadamard does not implement a decomposition""" - op = ResourceHadamard(0) - with pytest.raises(ResourcesNotDefined): + op = re.ResourceHadamard(0) + with pytest.raises(re.ResourcesNotDefined): op.resources() def test_resource_rep(self): """Test that the compact representation is correct""" - op = ResourceHadamard(0) - expected = CompressedResourceOp(qml.Hadamard, {}) - assert op.resource_rep() == expected + expected = re.CompressedResourceOp(qml.Hadamard, {}) + assert re.ResourceHadamard.resource_rep() == expected + +class TestSWAP(): + """Tests for ResourceSWAP""" + + def test_resources(self): + """Test that SWAP decomposes into three CNOTs""" + op = re.ResourceSWAP([0, 1]) + cnot = re.ResourceCNOT.resource_rep() + expected = {cnot: 3} + + assert op.resources() == expected + + def test_resource_rep(self): + """Test the compact representation""" + expected = re.CompressedResourceOp(qml.SWAP, {}) + assert re.ResourceSWAP.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation""" + + op = re.ResourceSWAP([0, 1]) + cnot = re.ResourceCNOT.resource_rep() + expected = {cnot: 3} + + assert op.resources(**re.ResourceSWAP.resource_rep().params) == expected class TestT(): """Tests for ResourceT""" def test_resources(self): """Test that ResourceT does not implement a decomposition""" - op = ResourceT(0) - with pytest.raises(ResourcesNotDefined): + op = re.ResourceT(0) + with pytest.raises(re.ResourcesNotDefined): op.resources() def test_resource_rep(self): """Test that the compact representation is correct""" - op = ResourceT(0) - expected = CompressedResourceOp(qml.T, {}) - assert op.resource_rep() == expected + expected = re.CompressedResourceOp(qml.T, {}) + assert re.ResourceT.resource_rep() == expected diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index abd98b94185..b0c666848aa 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -2,7 +2,7 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourcesNotDefined, ResourceRZ +import pennylane.labs.resource_estimation as re from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import _rotation_resources @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) @@ -11,7 +11,7 @@ def test_rotation_resources(epsilon): gate_types = {} num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) - t = CompressedResourceOp(qml.T, {}) + t = re.CompressedResourceOp(qml.T, {}) gate_types[t] = num_gates assert gate_types == _rotation_resources(epsilon=epsilon) @@ -21,21 +21,20 @@ class TestRZ: @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_resources(self, epsilon): """Test the resources method""" - op = ResourceRZ(1.24, wires=0) + op = re.ResourceRZ(1.24, wires=0) assert op.resources(epsilon=epsilon) == _rotation_resources(epsilon=epsilon) @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_resource_rep(self, epsilon): """Test the compact representation""" - op = ResourceRZ(1.24, wires=0) - expected = CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + op = re.ResourceRZ(1.24, wires=0) + expected = re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) assert op.resource_rep(epsilon=epsilon) == expected @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_resources_from_rep(self, epsilon): """Test the resources can be obtained from the compact representation""" - op = ResourceRZ(1.24, wires=0) - expected = _rotation_resources(epsilon=epsilon) - assert ResourceRZ.resources(**op.resource_rep(epsilon=epsilon).params) == expected + expected = _rotation_resources(epsilon=epsilon) + assert re.ResourceRZ.resources(**re.ResourceRZ.resource_rep(epsilon=epsilon).params) == expected diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 024195a1c69..91da44b6257 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -1,7 +1,7 @@ import pytest import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceQFT +import pennylane.labs.resource_estimation as re class TestQFT: """Test the ResourceQFT class""" @@ -16,9 +16,9 @@ class TestQFT: ) def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test the resources method returns the correct dictionary""" - hadamard = CompressedResourceOp(qml.Hadamard, {}) - swap = CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) + hadamard = re.CompressedResourceOp(qml.Hadamard, {}) + swap = re.CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) expected = { hadamard: num_hadamard, @@ -26,16 +26,14 @@ def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift ctrl_phase_shift: num_ctrl_phase_shift } - assert ResourceQFT.resources(num_wires) == expected + assert re.ResourceQFT.resources(num_wires) == expected @pytest.mark.parametrize("num_wires", [1, 2, 3, 4]) def test_resource_rep(self, num_wires): """Test the resource_rep returns the correct CompressedResourceOp""" - expected = CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) - op = ResourceQFT(wires=range(num_wires)) - - assert op.resource_rep() == expected + expected = re.CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) + assert re.ResourceQFT.resource_rep(num_wires) == expected @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", [ @@ -48,9 +46,9 @@ def test_resource_rep(self, num_wires): def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test that computing the resources from a compressed representation works""" - hadamard = CompressedResourceOp(qml.Hadamard, {}) - swap = CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) + hadamard = re.CompressedResourceOp(qml.Hadamard, {}) + swap = re.CompressedResourceOp(qml.SWAP, {}) + ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) expected = { hadamard: num_hadamard, @@ -58,8 +56,8 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph ctrl_phase_shift: num_ctrl_phase_shift } - rep = ResourceQFT(wires=range(num_wires)).resource_rep() - actual = ResourceQFT.resources(**rep.params) + rep = re.ResourceQFT.resource_rep(num_wires) + actual = re.ResourceQFT.resources(**rep.params) assert actual == expected @@ -67,10 +65,10 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph def test_type_error(self, num_wires): """Test that resources correctly raises a TypeError""" with pytest.raises(TypeError, match="num_wires must be an int."): - ResourceQFT.resources(num_wires) + re.ResourceQFT.resources(num_wires) @pytest.mark.parametrize("num_wires", [0, -1]) def test_value_error(self, num_wires): """Test that resources correctly raises a ValueError""" with pytest.raises(ValueError, match="num_wires must be greater than 0."): - ResourceQFT.resources(num_wires) + re.ResourceQFT.resources(num_wires) From 0be620aaec1e58d255dab99b5a9d84dd858085a8 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 17:00:48 -0400 Subject: [PATCH 044/335] ResourceSWAP --- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 3d84c7ecb56..34bf92dc3dc 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -39,6 +39,10 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: return gate_types + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.SWAP, {}) + class ResourceT(qml.T, re.ResourceConstructor): """Resource class for T""" From e40b12e4584d1db57130cda198fce3bd9e9d09f0 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 17:07:12 -0400 Subject: [PATCH 045/335] update to static methods --- .../ops/qubit/parametric_ops_single_qubit.py | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 6d17e07f6d0..6222d44c637 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -21,12 +21,9 @@ def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) @staticmethod - def compute_resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: + def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RX, {"epsilon": epsilon}) - def resource_rep(self, epsilon=10e-3) -> re.CompressedResourceOp: - return ResourceRX.compute_resource_rep(epsilon=epsilon) - class ResourceRY(qml.RY, re.ResourceConstructor): """Resource class for RY""" @@ -35,12 +32,9 @@ def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) @staticmethod - def compute_resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: + def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RY, {"epsilon": epsilon}) - def resource_rep(self, epsilon=10e-3) -> re.CompressedResourceOp: - return ResourceRY.compute_resource_rep(epsilon=epsilon) - class ResourceRZ(qml.RZ, re.ResourceConstructor): """Resource class for RZ""" @@ -52,7 +46,8 @@ def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: def compute_resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) - def resource_rep(self, epsilon=10e-3) -> re.CompressedResourceOp: + @staticmethod + def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return ResourceRZ.compute_resource_rep(epsilon=epsilon) class ResourceRot(qml.Rot, re.ResourceConstructor): @@ -60,16 +55,13 @@ class ResourceRot(qml.Rot, re.ResourceConstructor): @staticmethod def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: - rx = ResourceRX.compute_resource_rep() - ry = ResourceRY.compute_resource_rep() - rz = ResourceRZ.compute_resource_rep() + rx = ResourceRX.resource_rep() + ry = ResourceRY.resource_rep() + rz = ResourceRZ.resource_rep() gate_types = {rx: 1, ry: 1, rz: 1} return gate_types @staticmethod - def compute_resource_rep() -> re.CompressedResourceOp: + def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Rot, {}) - - def resource_rep(self) -> re.CompressedResourceOp: - return ResourceRot.compute_resource_rep() From a9b710e1d2874f5a4f1f78dbf5b99923be1243be Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 29 Oct 2024 17:10:46 -0400 Subject: [PATCH 046/335] fixing S tests --- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 2 ++ .../resource_estimation/ops/qubit/test_non_parametric_ops.py | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 34bf92dc3dc..e73135cd0a4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -23,6 +23,8 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: t = ResourceT.resource_rep() gate_types[t] = 2 + return gate_types + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.S, {}) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 384d1636682..5d589d79903 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -53,15 +53,14 @@ def test_resources(self): def test_resource_rep(self): """Test that the compressed representation is correct""" - op = re.ResourceS(0) expected = re.CompressedResourceOp(qml.S, {}) - assert op.resource_rep() == expected + assert re.ResourceS.resource_rep() == expected def test_resources_from_rep(self): """Test that the compressed representation yields the correct resources""" op = re.ResourceS(0) expected = {re.CompressedResourceOp(qml.T, {}): 2} - assert op.resources(**op.resource_rep().params) == expected + assert op.resources(**re.ResourceS.resource_rep().params) == expected class TestT(): """Tests for ResourceT""" From c22bbe1a89124c1fff9bc0fd6d878f96d1c9830a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 10:25:39 -0400 Subject: [PATCH 047/335] identity operator --- .../labs/resource_estimation/__init__.py | 1 + .../labs/resource_estimation/ops/__init__.py | 2 ++ .../labs/resource_estimation/ops/identity.py | 16 ++++++++++++++ .../ops/qubit/parametric_ops_single_qubit.py | 6 +---- .../qubit/test_parametric_ops_single_qubit.py | 15 +++++++++++-- .../resource_estimation/ops/test_identity.py | 22 +++++++++++++++++++ 6 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 pennylane/labs/resource_estimation/ops/identity.py create mode 100644 pennylane/labs/tests/resource_estimation/ops/test_identity.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index fc9b9251928..b3544fe0992 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -20,6 +20,7 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceHadamard, + ResourceIdentity, ResourceRX, ResourceRZ, ResourceRY, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 497920648d9..a33ac8815d2 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,3 +1,5 @@ +from .identity import ResourceIdentity + from .qubit import( ResourceHadamard, ResourceRX, diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py new file mode 100644 index 00000000000..1e92e5d6f29 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -0,0 +1,16 @@ +from typing import Dict + +import pennylane as qml +import pennylane.labs.resource_estimation as re + +#pylint: disable=too-many-ancestors + +class ResourceIdentity(qml.Identity, re.ResourceConstructor): + + @staticmethod + def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.Identity, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 6222d44c637..7e7378ed3e6 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -42,13 +42,9 @@ class ResourceRZ(qml.RZ, re.ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - @staticmethod - def compute_resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) - @staticmethod def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: - return ResourceRZ.compute_resource_rep(epsilon=epsilon) + return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) class ResourceRot(qml.Rot, re.ResourceConstructor): """Resource class for Rot""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 66f912fdcc8..a8de3e7a1e7 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -35,7 +35,6 @@ def test_resource_rep(self, epsilon, resource_class): op = resource_class(1.24, wires=0) pl_class = globals()[resource_class.__name__[8:]] expected = re.CompressedResourceOp(pl_class, {"epsilon": epsilon}) - assert op.resource_rep(epsilon=epsilon) == expected @pytest.mark.parametrize("resource_class, epsilon", params) @@ -44,8 +43,20 @@ def test_resources_from_rep(self, epsilon, resource_class): op = resource_class(1.24, wires=0) expected = _rotation_resources(epsilon=epsilon) - assert resource_class.resources(**op.resource_rep(epsilon=epsilon).params) == expected class TestRot: """Test ResourceRot""" + + def test_resources(self): + """Test the resources method""" + + op = re.ResourceRot(0.1, 0.2, 0.3) + config = { "error_rx": 10e-3, "error_ry": 10e-3, "error_rz": 10e-3 } + + + def test_resource_rep(self): + """Test the compressed representation""" + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compact representation""" diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py new file mode 100644 index 00000000000..ee4ab86985a --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -0,0 +1,22 @@ +import pennylane as qml +import pennylane.labs.resource_estimation as re + +#pylint: disable=use-implicit-booleaness-not-comparison + +class TestIdentity: + """Test ResourceIdentity""" + + def test_resources(self): + """ResourceIdentity should have empty resources""" + op = re.ResourceIdentity() + assert op.resources() == {} + + def test_resource_rep(self): + """Test the compressed representation""" + expected = re.CompressedResourceOp(qml.Identity, {}) + assert re.ResourceIdentity.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation""" + op = re.ResourceIdentity() + assert op.resources(**re.ResourceIdentity.resource_rep().params) == {} From 0f04125aeedbc0000ebbf33f6a32f0ac32917593 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 10:33:37 -0400 Subject: [PATCH 048/335] adding resource_params abstract method --- .../labs/resource_estimation/resource_constructor.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index a6e33bf2ada..b9ecc0033f7 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -64,12 +64,22 @@ def set_resources(cls, new_func: Callable) -> None: """Set a custom resource method.""" cls.resources = new_func + @abstractmethod + def resource_params(self) -> dict: + """Returns a dictionary containing the minimal information needed to + compute a comparessed representation""" + @staticmethod @abstractmethod - def resource_rep() -> CompressedResourceOp: + def resource_rep(**kwargs) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" + def resource_rep_from_op(self) -> CompressedResourceOp: + """Returns a compressed representation directly from the operator""" + params = self.resource_params() + return self.__class__.resource_rep(**params) + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceConstructor`` does not implement _resource_decomp""" From 4930f0f89ebc0a9cdc2a4238d5d55889b1d90ee4 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 10:39:49 -0400 Subject: [PATCH 049/335] test for resource_params --- .../test_resource_constructor.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 67452b1611a..4d90a11111b 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -5,6 +5,9 @@ def test_abstract_resource_decomp(): class DummyClass(ResourceConstructor): + def resource_params(): + return + @staticmethod def resource_rep(): return @@ -16,12 +19,31 @@ def resource_rep(): DummyClass() +def test_abstract_resource_params(): + class DummyClass(ResourceConstructor): + @staticmethod + def _resource_decomp(): + return + + def resource_rep(): + return + + with pytest.raises( + TypeError, + match="Can't instantiate abstract class DummyClass with abstract method resource_params", + ): + DummyClass() + + def test_abstract_resource_rep(): class DummyClass(ResourceConstructor): @staticmethod def _resource_decomp(): return + def resource_params(): + return + with pytest.raises( TypeError, match="Can't instantiate abstract class DummyClass with abstract method resource_rep", @@ -31,6 +53,9 @@ def _resource_decomp(): def test_set_resources(): class DummyClass(ResourceConstructor): + def resource_params(): + return + @staticmethod def resource_rep(): return From d40600c47de3fceb49e4fc21925ea70cbf5dcdc9 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 11:12:19 -0400 Subject: [PATCH 050/335] adding resource_params to qft --- .../ops/op_math/controlled_ops.py | 6 +++ .../ops/qubit/non_parametric_ops.py | 9 +++++ .../ops/qubit/parametric_ops_single_qubit.py | 3 ++ .../ops/op_math/test_controlled_ops.py | 38 +++++++++++-------- .../ops/qubit/test_non_parametric_ops.py | 17 +++++++++ 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 4714f977d8c..2a90710fdbb 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -20,6 +20,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: return gate_types + def resource_params(self): + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.ControlledPhaseShift, {}) @@ -31,6 +34,9 @@ class ResourceCNOT(qml.CNOT, re.ResourceConstructor): def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.CNOT, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index a04af778187..78ecb2578f8 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -10,6 +10,9 @@ class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Hadamard, {}) @@ -25,6 +28,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: return gate_types + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.SWAP, {}) @@ -36,6 +42,9 @@ class ResourceT(qml.T, re.ResourceConstructor): def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.T, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index e15a224ddb5..a912ff741d3 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -20,6 +20,9 @@ class ResourceRZ(qml.RZ, re.ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) + def resource_params(): + return {} + @staticmethod def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 5873289a730..0f8914186d9 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -6,13 +6,12 @@ class TestControlledPhaseShift: """Test ResourceControlledPhaseShift""" - @pytest.mark.parametrize("phi, wires", - [ - (1.2, [0, 1]), - (2.4, [2, 3]), - ]) + params = [(1.2, [0, 1]), (2.4, [2, 3])] + + @pytest.mark.parametrize("phi, wires", params) def test_resources(self, phi, wires): """Test the resources method""" + op = re.ResourceControlledPhaseShift(phi, wires) expected = { @@ -22,26 +21,33 @@ def test_resources(self, phi, wires): assert op.resources() == expected - @pytest.mark.parametrize("phi, wires", - [ - (1.2, [0, 1]), - (2.4, [2, 3]), - ]) + @pytest.mark.parametrize("phi, wires", params) + def test_resource_params(self, phi, wires): + """Test the resource parameters""" + + op = re.ResourceControlledPhaseShift(phi, wires) + assert op.resource_params() == {} #pylint: disable=use-implicit-booleaness-not-comparison + + @pytest.mark.parametrize("phi, wires", params) def test_resource_rep(self, phi, wires): """Test the compressed representation""" + op = re.ResourceControlledPhaseShift(phi, wires) expected = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) assert op.resource_rep() == expected + @pytest.mark.parametrize("phi, wires", params) + def test_resource_rep_from_op(self, phi, wires): + """Test resource_rep_from_op method""" + + op = re.ResourceControlledPhaseShift(phi, wires) + assert op.resource_rep_from_op() == re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()) - @pytest.mark.parametrize("phi, wires", - [ - (1.2, [0, 1]), - (2.4, [2, 3]), - ]) + @pytest.mark.parametrize("phi, wires", params) def test_resources_from_rep(self, phi, wires): """Compute the resources from the compressed representation""" + op = re.ResourceControlledPhaseShift(phi, wires) expected = { @@ -49,7 +55,7 @@ def test_resources_from_rep(self, phi, wires): re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } - assert op.resources(*op.resource_rep().params) == expected + assert op.resources(**re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()).params) == expected class TestCNOT: """Test ResourceCNOT""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index e9aac1a42b2..e120854dbf0 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -3,6 +3,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re +#pylint: disable=use-implicit-booleaness-not-comparison + class TestHadamard(): """Tests for ResourceHadamard""" @@ -12,6 +14,11 @@ def test_resources(self): with pytest.raises(re.ResourcesNotDefined): op.resources() + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceHadamard(0) + assert op.resource_params() == {} + def test_resource_rep(self): """Test that the compact representation is correct""" expected = re.CompressedResourceOp(qml.Hadamard, {}) @@ -28,6 +35,11 @@ def test_resources(self): assert op.resources() == expected + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceSWAP([0, 1]) + assert op.resource_params() == {} + def test_resource_rep(self): """Test the compact representation""" expected = re.CompressedResourceOp(qml.SWAP, {}) @@ -51,6 +63,11 @@ def test_resources(self): with pytest.raises(re.ResourcesNotDefined): op.resources() + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceT(0) + assert op.resource_params() == {} + def test_resource_rep(self): """Test that the compact representation is correct""" expected = re.CompressedResourceOp(qml.T, {}) From 09d64c334e716f16b5b706e2c1359a3c603ec16a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 11:18:36 -0400 Subject: [PATCH 051/335] adding resource_params to qft --- .../labs/resource_estimation/templates/resource_qft.py | 3 +++ .../resource_estimation/templates/test_resource_qft.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 827954b2dfc..360401c384c 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -27,6 +27,9 @@ def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: return gate_types + def resource_params(self): + return {"num_wires": len(self.wires)} + @staticmethod def resource_rep(num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 91da44b6257..347f35f84a3 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -28,6 +28,12 @@ def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift assert re.ResourceQFT.resources(num_wires) == expected + @pytest.mark.parametrize("wires", [range(1), range(2), range(3), range(4)]) + def test_resource_params(self, wires): + """Test that the resource params are correct""" + op = re.ResourceQFT(wires) + assert op.resource_params() == {"num_wires": len(wires)} + @pytest.mark.parametrize("num_wires", [1, 2, 3, 4]) def test_resource_rep(self, num_wires): """Test the resource_rep returns the correct CompressedResourceOp""" From 2b658b550eb2ae046099cf441c15c5f5d3afa11b Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 11:39:43 -0400 Subject: [PATCH 052/335] adding resource_params --- pennylane/labs/resource_estimation/__init__.py | 1 + pennylane/labs/resource_estimation/ops/__init__.py | 1 + pennylane/labs/resource_estimation/ops/identity.py | 3 +++ .../labs/resource_estimation/ops/qubit/__init__.py | 1 + .../ops/qubit/non_parametric_ops.py | 3 +++ .../ops/qubit/parametric_ops_single_qubit.py | 11 ++++++++++- .../ops/qubit/test_parametric_ops_single_qubit.py | 2 +- 7 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index b3544fe0992..1f93ec8faff 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -21,6 +21,7 @@ ResourceControlledPhaseShift, ResourceHadamard, ResourceIdentity, + ResourceRot, ResourceRX, ResourceRZ, ResourceRY, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index a33ac8815d2..1c375fae8a4 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -2,6 +2,7 @@ from .qubit import( ResourceHadamard, + ResourceRot, ResourceRX, ResourceRY, ResourceRZ, diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 1e92e5d6f29..e3511d1a334 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -11,6 +11,9 @@ class ResourceIdentity(qml.Identity, re.ResourceConstructor): def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: return {} + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Identity, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index 7a9f9871108..c45acc93fbe 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -6,6 +6,7 @@ ) from .parametric_ops_single_qubit import( + ResourceRot, ResourceRX, ResourceRY, ResourceRZ, diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 3a032e69021..12fa67069c8 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -28,6 +28,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: return gate_types + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.S, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 813ff331006..a5a90a0d764 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -20,6 +20,9 @@ class ResourceRX(qml.RX, re.ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) + def resource_params(self) -> dict: + return {} + @staticmethod def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RX, {"epsilon": epsilon}) @@ -31,6 +34,9 @@ class ResourceRY(qml.RY, re.ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) + def resource_params(self): + return {} + @staticmethod def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RY, {"epsilon": epsilon}) @@ -42,7 +48,7 @@ class ResourceRZ(qml.RZ, re.ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - def resource_params(): + def resource_params(self) -> dict: return {} @staticmethod @@ -61,6 +67,9 @@ def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: gate_types = {rx: 1, ry: 1, rz: 1} return gate_types + def resource_params(self): + return {} + @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Rot, {}) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index a8de3e7a1e7..8307b48bebd 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -51,7 +51,7 @@ class TestRot: def test_resources(self): """Test the resources method""" - op = re.ResourceRot(0.1, 0.2, 0.3) + op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) config = { "error_rx": 10e-3, "error_ry": 10e-3, "error_rz": 10e-3 } From a7b39bd97ed01a65b35d78c22bf6197deb6a7a3e Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 11:51:10 -0400 Subject: [PATCH 053/335] adding global phase --- .../labs/resource_estimation/ops/identity.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index e3511d1a334..bfb2a351725 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -6,6 +6,7 @@ #pylint: disable=too-many-ancestors class ResourceIdentity(qml.Identity, re.ResourceConstructor): + """Resource class for Identity""" @staticmethod def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: @@ -17,3 +18,17 @@ def resource_params(self) -> dict: @staticmethod def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Identity, {}) + +class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceConstructor): + """Resource class for GlobalPhase""" + + @staticmethod + def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + def resource_params(self) -> dict: + return {} + + @staticmethod + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.GlobalPhase, {}) From 99be5cd83b81eec2ca438614e671b492dc96a99a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 13:17:35 -0400 Subject: [PATCH 054/335] phaseshift --- .../labs/resource_estimation/__init__.py | 1 + .../labs/resource_estimation/ops/__init__.py | 5 ++++- .../ops/qubit/parametric_ops_single_qubit.py | 22 ++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 1f93ec8faff..6109de69961 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -19,6 +19,7 @@ from .ops import( ResourceCNOT, ResourceControlledPhaseShift, + ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, ResourceRot, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 1c375fae8a4..325f57db87f 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,4 +1,7 @@ -from .identity import ResourceIdentity +from .identity import( + ResourceGlobalPhase, + ResourceIdentity, +) from .qubit import( ResourceHadamard, diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index a5a90a0d764..9fc568d637e 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -13,6 +13,26 @@ def _rotation_resources(epsilon=10e-3): return gate_types +class ResourcePhaseShift(qml.PhaseShift, re.ResourceConstructor): + """Resource class for PhaseShift""" + + @staticmethod + def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + gate_types = {} + rz = re.ResourceRZ.resource_rep() + global_phase = re.ResourceGlobalPhase.resource_rep() + gate_types[rz] = 1 + gate_types[global_phase] = 1 + + return gate_types + + def resource_params(self) -> dict: + return {} + + def resource_rep() -> re.CompressedResourceOp: + return re.CompressedResourceOp(qml.PhaseShift, {}) + + class ResourceRX(qml.RX, re.ResourceConstructor): """Resource class for RX""" @@ -34,7 +54,7 @@ class ResourceRY(qml.RY, re.ResourceConstructor): def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=epsilon) - def resource_params(self): + def resource_params(self) -> dict: return {} @staticmethod From 2efd1f082ba3bb754dc96f26ad02a16800a8aff0 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 15:00:03 -0400 Subject: [PATCH 055/335] black --- .../labs/resource_estimation/__init__.py | 22 +++++++++--------- .../labs/resource_estimation/ops/__init__.py | 16 ++++++------- .../ops/op_math/controlled_ops.py | 4 +++- .../resource_estimation/ops/qubit/__init__.py | 16 ++++++------- .../ops/qubit/non_parametric_ops.py | 3 +++ .../ops/qubit/parametric_ops_single_qubit.py | 5 +++- .../templates/resource_qft.py | 10 ++++++-- .../ops/op_math/test_controlled_ops.py | 23 +++++++++++++------ .../ops/qubit/test_non_parametric_ops.py | 11 +++++---- .../qubit/test_parametric_ops_single_qubit.py | 11 +++++++-- .../templates/test_resource_qft.py | 23 ++++++++----------- 11 files changed, 86 insertions(+), 58 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 9ce4aa49049..6d33e6d73c8 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -16,15 +16,15 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources -from .ops import( - ResourceCNOT, - ResourceControlledPhaseShift, - ResourceHadamard, - ResourceRZ, - ResourceSWAP, - ResourceT, - ) +from .ops import ( + ResourceCNOT, + ResourceControlledPhaseShift, + ResourceHadamard, + ResourceRZ, + ResourceSWAP, + ResourceT, +) -from .templates import( - ResourceQFT, - ) +from .templates import ( + ResourceQFT, +) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 39d8327c0e3..e4f5cab4678 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,11 +1,11 @@ -from .qubit import( - ResourceHadamard, - ResourceRZ, - ResourceSWAP, - ResourceT, +from .qubit import ( + ResourceHadamard, + ResourceRZ, + ResourceSWAP, + ResourceT, ) -from .op_math import( - ResourceCNOT, - ResourceControlledPhaseShift, +from .op_math import ( + ResourceCNOT, + ResourceControlledPhaseShift, ) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 2a90710fdbb..b2ade289971 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -3,7 +3,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=too-many-ancestors +# pylint: disable=too-many-ancestors + class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): """Resource class for ControlledPhaseShift""" @@ -27,6 +28,7 @@ def resource_params(self): def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + class ResourceCNOT(qml.CNOT, re.ResourceConstructor): """Resource class for CNOT""" diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index bce433a7d0e..1b60a91548b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -1,9 +1,9 @@ -from .non_parametric_ops import( - ResourceHadamard, - ResourceSWAP, - ResourceT, - ) +from .non_parametric_ops import ( + ResourceHadamard, + ResourceSWAP, + ResourceT, +) -from .parametric_ops_single_qubit import( - ResourceRZ, - ) +from .parametric_ops_single_qubit import ( + ResourceRZ, +) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 78ecb2578f8..05f75553cc4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -3,6 +3,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re + class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): """Resource class for Hadamard""" @@ -17,6 +18,7 @@ def resource_params(self) -> dict: def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Hadamard, {}) + class ResourceSWAP(qml.SWAP, re.ResourceConstructor): """Resource class for SWAP""" @@ -35,6 +37,7 @@ def resource_params(self) -> dict: def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.SWAP, {}) + class ResourceT(qml.T, re.ResourceConstructor): """Resource class for T""" diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index a912ff741d3..6f0fe8f233e 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -1,9 +1,11 @@ -import numpy as np from typing import Dict +import numpy as np + import pennylane as qml import pennylane.labs.resource_estimation as re + def _rotation_resources(epsilon=10e-3): gate_types = {} @@ -13,6 +15,7 @@ def _rotation_resources(epsilon=10e-3): return gate_types + class ResourceRZ(qml.RZ, re.ResourceConstructor): """Resource class for RZ""" diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 360401c384c..1ba88722dd4 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -1,8 +1,14 @@ from typing import Dict import pennylane as qml +from pennylane.labs.resource_estimation import ( + CompressedResourceOp, + ResourceConstructor, + ResourceControlledPhaseShift, + ResourceHadamard, + ResourceSWAP, +) -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourceHadamard, ResourceSWAP, ResourceControlledPhaseShift class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @@ -23,7 +29,7 @@ def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: gate_types[hadamard] = num_wires gate_types[swap] = num_wires // 2 - gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 + gate_types[ctrl_phase_shift] = num_wires * (num_wires - 1) // 2 return gate_types diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 0f8914186d9..b7673c5ad3c 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -3,6 +3,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re + class TestControlledPhaseShift: """Test ResourceControlledPhaseShift""" @@ -15,8 +16,8 @@ def test_resources(self, phi, wires): op = re.ResourceControlledPhaseShift(phi, wires) expected = { - re.CompressedResourceOp(qml.CNOT, {}): 2, - re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(qml.CNOT, {}): 2, + re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } assert op.resources() == expected @@ -26,7 +27,7 @@ def test_resource_params(self, phi, wires): """Test the resource parameters""" op = re.ResourceControlledPhaseShift(phi, wires) - assert op.resource_params() == {} #pylint: disable=use-implicit-booleaness-not-comparison + assert op.resource_params() == {} # pylint: disable=use-implicit-booleaness-not-comparison @pytest.mark.parametrize("phi, wires", params) def test_resource_rep(self, phi, wires): @@ -42,7 +43,9 @@ def test_resource_rep_from_op(self, phi, wires): """Test resource_rep_from_op method""" op = re.ResourceControlledPhaseShift(phi, wires) - assert op.resource_rep_from_op() == re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()) + assert op.resource_rep_from_op() == re.ResourceControlledPhaseShift.resource_rep( + **op.resource_params() + ) @pytest.mark.parametrize("phi, wires", params) def test_resources_from_rep(self, phi, wires): @@ -51,11 +54,17 @@ def test_resources_from_rep(self, phi, wires): op = re.ResourceControlledPhaseShift(phi, wires) expected = { - re.CompressedResourceOp(qml.CNOT, {}): 2, - re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(qml.CNOT, {}): 2, + re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } - assert op.resources(**re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()).params) == expected + assert ( + op.resources( + **re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()).params + ) + == expected + ) + class TestCNOT: """Test ResourceCNOT""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index e120854dbf0..a543fd06e49 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -3,9 +3,10 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison -class TestHadamard(): + +class TestHadamard: """Tests for ResourceHadamard""" def test_resources(self): @@ -24,7 +25,8 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(qml.Hadamard, {}) assert re.ResourceHadamard.resource_rep() == expected -class TestSWAP(): + +class TestSWAP: """Tests for ResourceSWAP""" def test_resources(self): @@ -54,7 +56,8 @@ def test_resources_from_rep(self): assert op.resources(**re.ResourceSWAP.resource_rep().params) == expected -class TestT(): + +class TestT: """Tests for ResourceT""" def test_resources(self): diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index b0c666848aa..a9474d24948 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -3,7 +3,10 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import _rotation_resources +from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import ( + _rotation_resources, +) + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_rotation_resources(epsilon): @@ -15,6 +18,7 @@ def test_rotation_resources(epsilon): gate_types[t] = num_gates assert gate_types == _rotation_resources(epsilon=epsilon) + class TestRZ: """Test ResourceRZ""" @@ -37,4 +41,7 @@ def test_resources_from_rep(self, epsilon): """Test the resources can be obtained from the compact representation""" expected = _rotation_resources(epsilon=epsilon) - assert re.ResourceRZ.resources(**re.ResourceRZ.resource_rep(epsilon=epsilon).params) == expected + assert ( + re.ResourceRZ.resources(**re.ResourceRZ.resource_rep(epsilon=epsilon).params) + == expected + ) diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 347f35f84a3..2fb113ced88 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -3,16 +3,18 @@ import pennylane as qml import pennylane.labs.resource_estimation as re + class TestQFT: """Test the ResourceQFT class""" - @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", + @pytest.mark.parametrize( + "num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", [ (1, 1, 0, 0), (2, 2, 1, 1), (3, 3, 1, 3), (4, 4, 2, 6), - ] + ], ) def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test the resources method returns the correct dictionary""" @@ -20,11 +22,7 @@ def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift swap = re.CompressedResourceOp(qml.SWAP, {}) ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) - expected = { - hadamard: num_hadamard, - swap: num_swap, - ctrl_phase_shift: num_ctrl_phase_shift - } + expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} assert re.ResourceQFT.resources(num_wires) == expected @@ -41,13 +39,14 @@ def test_resource_rep(self, num_wires): expected = re.CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) assert re.ResourceQFT.resource_rep(num_wires) == expected - @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", + @pytest.mark.parametrize( + "num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", [ (1, 1, 0, 0), (2, 2, 1, 1), (3, 3, 1, 3), (4, 4, 2, 6), - ] + ], ) def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test that computing the resources from a compressed representation works""" @@ -56,11 +55,7 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph swap = re.CompressedResourceOp(qml.SWAP, {}) ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) - expected = { - hadamard: num_hadamard, - swap: num_swap, - ctrl_phase_shift: num_ctrl_phase_shift - } + expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} rep = re.ResourceQFT.resource_rep(num_wires) actual = re.ResourceQFT.resources(**rep.params) From 8e296b878af2013afdbc465a0d1701b8395c097d Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 30 Oct 2024 15:23:27 -0400 Subject: [PATCH 056/335] black --- .../labs/resource_estimation/__init__.py | 34 +++++++++---------- .../labs/resource_estimation/ops/__init__.py | 30 ++++++++-------- .../labs/resource_estimation/ops/identity.py | 4 ++- .../ops/op_math/controlled_ops.py | 4 ++- .../resource_estimation/ops/qubit/__init__.py | 24 ++++++------- .../ops/qubit/non_parametric_ops.py | 2 ++ .../ops/qubit/parametric_ops_single_qubit.py | 8 ++++- .../templates/resource_qft.py | 10 ++++-- .../ops/op_math/test_controlled_ops.py | 23 +++++++++---- .../ops/qubit/test_non_parametric_ops.py | 14 +++++--- .../qubit/test_parametric_ops_single_qubit.py | 12 ++++--- .../resource_estimation/ops/test_identity.py | 3 +- .../templates/test_resource_qft.py | 23 +++++-------- 13 files changed, 111 insertions(+), 80 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 6109de69961..921a2fdedbe 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -16,21 +16,21 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources -from .ops import( - ResourceCNOT, - ResourceControlledPhaseShift, - ResourceGlobalPhase, - ResourceHadamard, - ResourceIdentity, - ResourceRot, - ResourceRX, - ResourceRZ, - ResourceRY, - ResourceS, - ResourceSWAP, - ResourceT, - ) +from .ops import ( + ResourceCNOT, + ResourceControlledPhaseShift, + ResourceGlobalPhase, + ResourceHadamard, + ResourceIdentity, + ResourceRot, + ResourceRX, + ResourceRZ, + ResourceRY, + ResourceS, + ResourceSWAP, + ResourceT, +) -from .templates import( - ResourceQFT, - ) +from .templates import ( + ResourceQFT, +) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 325f57db87f..a3d183fd74c 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,20 +1,20 @@ -from .identity import( - ResourceGlobalPhase, - ResourceIdentity, +from .identity import ( + ResourceGlobalPhase, + ResourceIdentity, ) -from .qubit import( - ResourceHadamard, - ResourceRot, - ResourceRX, - ResourceRY, - ResourceRZ, - ResourceS, - ResourceSWAP, - ResourceT, +from .qubit import ( + ResourceHadamard, + ResourceRot, + ResourceRX, + ResourceRY, + ResourceRZ, + ResourceS, + ResourceSWAP, + ResourceT, ) -from .op_math import( - ResourceCNOT, - ResourceControlledPhaseShift, +from .op_math import ( + ResourceCNOT, + ResourceControlledPhaseShift, ) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index bfb2a351725..efb90c7ffb1 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -3,7 +3,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=too-many-ancestors +# pylint: disable=too-many-ancestors + class ResourceIdentity(qml.Identity, re.ResourceConstructor): """Resource class for Identity""" @@ -19,6 +20,7 @@ def resource_params(self) -> dict: def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Identity, {}) + class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceConstructor): """Resource class for GlobalPhase""" diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 2a90710fdbb..b2ade289971 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -3,7 +3,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=too-many-ancestors +# pylint: disable=too-many-ancestors + class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): """Resource class for ControlledPhaseShift""" @@ -27,6 +28,7 @@ def resource_params(self): def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + class ResourceCNOT(qml.CNOT, re.ResourceConstructor): """Resource class for CNOT""" diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index c45acc93fbe..65f06bef63b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -1,13 +1,13 @@ -from .non_parametric_ops import( - ResourceHadamard, - ResourceS, - ResourceSWAP, - ResourceT, - ) +from .non_parametric_ops import ( + ResourceHadamard, + ResourceS, + ResourceSWAP, + ResourceT, +) -from .parametric_ops_single_qubit import( - ResourceRot, - ResourceRX, - ResourceRY, - ResourceRZ, - ) +from .parametric_ops_single_qubit import ( + ResourceRot, + ResourceRX, + ResourceRY, + ResourceRZ, +) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 12fa67069c8..d32534b15f3 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -3,6 +3,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re + class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): """Resource class for Hadamard""" @@ -17,6 +18,7 @@ def resource_params(self) -> dict: def resource_rep() -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.Hadamard, {}) + class ResourceS(qml.S, re.ResourceConstructor): """Resource class for S""" diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 9fc568d637e..4d47f5b127a 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -1,9 +1,11 @@ -import numpy as np from typing import Dict +import numpy as np + import pennylane as qml import pennylane.labs.resource_estimation as re + def _rotation_resources(epsilon=10e-3): gate_types = {} @@ -13,6 +15,7 @@ def _rotation_resources(epsilon=10e-3): return gate_types + class ResourcePhaseShift(qml.PhaseShift, re.ResourceConstructor): """Resource class for PhaseShift""" @@ -47,6 +50,7 @@ def resource_params(self) -> dict: def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RX, {"epsilon": epsilon}) + class ResourceRY(qml.RY, re.ResourceConstructor): """Resource class for RY""" @@ -61,6 +65,7 @@ def resource_params(self) -> dict: def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RY, {"epsilon": epsilon}) + class ResourceRZ(qml.RZ, re.ResourceConstructor): """Resource class for RZ""" @@ -75,6 +80,7 @@ def resource_params(self) -> dict: def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + class ResourceRot(qml.Rot, re.ResourceConstructor): """Resource class for Rot""" diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 360401c384c..1ba88722dd4 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -1,8 +1,14 @@ from typing import Dict import pennylane as qml +from pennylane.labs.resource_estimation import ( + CompressedResourceOp, + ResourceConstructor, + ResourceControlledPhaseShift, + ResourceHadamard, + ResourceSWAP, +) -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor, ResourceHadamard, ResourceSWAP, ResourceControlledPhaseShift class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @@ -23,7 +29,7 @@ def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: gate_types[hadamard] = num_wires gate_types[swap] = num_wires // 2 - gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 + gate_types[ctrl_phase_shift] = num_wires * (num_wires - 1) // 2 return gate_types diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 0f8914186d9..b7673c5ad3c 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -3,6 +3,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re + class TestControlledPhaseShift: """Test ResourceControlledPhaseShift""" @@ -15,8 +16,8 @@ def test_resources(self, phi, wires): op = re.ResourceControlledPhaseShift(phi, wires) expected = { - re.CompressedResourceOp(qml.CNOT, {}): 2, - re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(qml.CNOT, {}): 2, + re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } assert op.resources() == expected @@ -26,7 +27,7 @@ def test_resource_params(self, phi, wires): """Test the resource parameters""" op = re.ResourceControlledPhaseShift(phi, wires) - assert op.resource_params() == {} #pylint: disable=use-implicit-booleaness-not-comparison + assert op.resource_params() == {} # pylint: disable=use-implicit-booleaness-not-comparison @pytest.mark.parametrize("phi, wires", params) def test_resource_rep(self, phi, wires): @@ -42,7 +43,9 @@ def test_resource_rep_from_op(self, phi, wires): """Test resource_rep_from_op method""" op = re.ResourceControlledPhaseShift(phi, wires) - assert op.resource_rep_from_op() == re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()) + assert op.resource_rep_from_op() == re.ResourceControlledPhaseShift.resource_rep( + **op.resource_params() + ) @pytest.mark.parametrize("phi, wires", params) def test_resources_from_rep(self, phi, wires): @@ -51,11 +54,17 @@ def test_resources_from_rep(self, phi, wires): op = re.ResourceControlledPhaseShift(phi, wires) expected = { - re.CompressedResourceOp(qml.CNOT, {}): 2, - re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(qml.CNOT, {}): 2, + re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, } - assert op.resources(**re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()).params) == expected + assert ( + op.resources( + **re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()).params + ) + == expected + ) + class TestCNOT: """Test ResourceCNOT""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 2f2d335cb86..cac93de10ca 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -3,9 +3,10 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison -class TestHadamard(): + +class TestHadamard: """Tests for ResourceHadamard""" def test_resources(self): @@ -24,7 +25,8 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(qml.Hadamard, {}) assert re.ResourceHadamard.resource_rep() == expected -class TestSWAP(): + +class TestSWAP: """Tests for ResourceSWAP""" def test_resources(self): @@ -54,7 +56,8 @@ def test_resources_from_rep(self): assert op.resources(**re.ResourceSWAP.resource_rep().params) == expected -class TestS(): + +class TestS: """Tests for ResourceS""" def test_resources(self): @@ -74,7 +77,8 @@ def test_resources_from_rep(self): expected = {re.CompressedResourceOp(qml.T, {}): 2} assert op.resources(**re.ResourceS.resource_rep().params) == expected -class TestT(): + +class TestT: """Tests for ResourceT""" def test_resources(self): diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 8307b48bebd..1dbc234d6e0 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -2,9 +2,12 @@ import pytest import pennylane as qml -from pennylane import RX, RY, RZ #pylint: disable=unused-import import pennylane.labs.resource_estimation as re -from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import _rotation_resources +from pennylane import RX, RY, RZ # pylint: disable=unused-import +from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import ( + _rotation_resources, +) + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_rotation_resources(epsilon): @@ -16,6 +19,7 @@ def test_rotation_resources(epsilon): gate_types[t] = num_gates assert gate_types == _rotation_resources(epsilon=epsilon) + class TestPauliRotation: """Test ResourceRX, ResourceRY, and ResourceRZ""" @@ -45,6 +49,7 @@ def test_resources_from_rep(self, epsilon, resource_class): expected = _rotation_resources(epsilon=epsilon) assert resource_class.resources(**op.resource_rep(epsilon=epsilon).params) == expected + class TestRot: """Test ResourceRot""" @@ -52,8 +57,7 @@ def test_resources(self): """Test the resources method""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) - config = { "error_rx": 10e-3, "error_ry": 10e-3, "error_rz": 10e-3 } - + config = {"error_rx": 10e-3, "error_ry": 10e-3, "error_rz": 10e-3} def test_resource_rep(self): """Test the compressed representation""" diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py index ee4ab86985a..70fcb93d957 100644 --- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -1,7 +1,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison + class TestIdentity: """Test ResourceIdentity""" diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 347f35f84a3..2fb113ced88 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -3,16 +3,18 @@ import pennylane as qml import pennylane.labs.resource_estimation as re + class TestQFT: """Test the ResourceQFT class""" - @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", + @pytest.mark.parametrize( + "num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", [ (1, 1, 0, 0), (2, 2, 1, 1), (3, 3, 1, 3), (4, 4, 2, 6), - ] + ], ) def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test the resources method returns the correct dictionary""" @@ -20,11 +22,7 @@ def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift swap = re.CompressedResourceOp(qml.SWAP, {}) ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) - expected = { - hadamard: num_hadamard, - swap: num_swap, - ctrl_phase_shift: num_ctrl_phase_shift - } + expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} assert re.ResourceQFT.resources(num_wires) == expected @@ -41,13 +39,14 @@ def test_resource_rep(self, num_wires): expected = re.CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) assert re.ResourceQFT.resource_rep(num_wires) == expected - @pytest.mark.parametrize("num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", + @pytest.mark.parametrize( + "num_wires, num_hadamard, num_swap, num_ctrl_phase_shift", [ (1, 1, 0, 0), (2, 2, 1, 1), (3, 3, 1, 3), (4, 4, 2, 6), - ] + ], ) def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test that computing the resources from a compressed representation works""" @@ -56,11 +55,7 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph swap = re.CompressedResourceOp(qml.SWAP, {}) ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) - expected = { - hadamard: num_hadamard, - swap: num_swap, - ctrl_phase_shift: num_ctrl_phase_shift - } + expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} rep = re.ResourceQFT.resource_rep(num_wires) actual = re.ResourceQFT.resources(**rep.params) From f034a7319e4ff151bd16737a31b67a9449486bcd Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 31 Oct 2024 10:25:28 -0400 Subject: [PATCH 057/335] op_type in compressed operator now must be a resource constructor --- pennylane/labs/resource_estimation/resource_container.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 86809b87199..f1378a95905 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -16,6 +16,8 @@ from collections import defaultdict from dataclasses import dataclass, field +from .resource_constructor import ResourceConstructor + class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. @@ -74,6 +76,9 @@ def __init__(self, op_type: type, params: dict) -> None: >>> print(op_tp) QSVT(num_wires=5, num_angles=100) """ + if not issubclass(op_type, ResourceConstructor): + raise TypeError("op_type must be of type ResourceConstructor.") + self._name = op_type.__name__ self.op_type = op_type self.params = params From 6ede84d3df6f248c2f34a1d3e99ed49998ed81b4 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 31 Oct 2024 10:53:20 -0400 Subject: [PATCH 058/335] updating tests --- .../ops/op_math/test_controlled_ops.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index b7673c5ad3c..591efc8274d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -16,8 +16,8 @@ def test_resources(self, phi, wires): op = re.ResourceControlledPhaseShift(phi, wires) expected = { - re.CompressedResourceOp(qml.CNOT, {}): 2, - re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(re.ResourceCNOT, {}): 2, + re.CompressedResourceOp(re.ResourceRZ, {"epsilon": 10e-3}): 3, } assert op.resources() == expected @@ -34,7 +34,7 @@ def test_resource_rep(self, phi, wires): """Test the compressed representation""" op = re.ResourceControlledPhaseShift(phi, wires) - expected = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + expected = re.CompressedResourceOp(re.ResourceControlledPhaseShift, {}) assert op.resource_rep() == expected @@ -54,8 +54,8 @@ def test_resources_from_rep(self, phi, wires): op = re.ResourceControlledPhaseShift(phi, wires) expected = { - re.CompressedResourceOp(qml.CNOT, {}): 2, - re.CompressedResourceOp(qml.RZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(re.ResourceCNOT, {}): 2, + re.CompressedResourceOp(re.ResourceRZ, {"epsilon": 10e-3}): 3, } assert ( @@ -78,5 +78,5 @@ def test_resources(self): def test_resource_rep(self): """Test the compressed representation""" op = re.ResourceCNOT([0, 1]) - expected = re.CompressedResourceOp(qml.CNOT, {}) + expected = re.CompressedResourceOp(re.ResourceCNOT, {}) assert op.resource_rep() == expected From 6ddb120445d5773d2f1869a88beac0707cb3da03 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 31 Oct 2024 11:35:53 -0400 Subject: [PATCH 059/335] updating to new class method --- .../ops/op_math/controlled_ops.py | 12 ++++++------ .../ops/qubit/parametric_ops_single_qubit.py | 6 +++--- .../labs/resource_estimation/resource_container.py | 7 +++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index b2ade289971..061a8a03403 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -24,9 +24,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self): return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceCNOT(qml.CNOT, re.ResourceConstructor): @@ -39,6 +39,6 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.CNOT, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 6f0fe8f233e..5617728f2b4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -26,6 +26,6 @@ def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: def resource_params(): return {} - @staticmethod - def resource_rep(epsilon=10e-3) -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + @classmethod + def resource_rep(cls, epsilon=10e-3) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {"epsilon": epsilon}) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index f1378a95905..6b8655be29e 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -16,8 +16,7 @@ from collections import defaultdict from dataclasses import dataclass, field -from .resource_constructor import ResourceConstructor - +import pennylane.labs.resource_estimation.resource_constructor as rc class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. @@ -76,8 +75,8 @@ def __init__(self, op_type: type, params: dict) -> None: >>> print(op_tp) QSVT(num_wires=5, num_angles=100) """ - if not issubclass(op_type, ResourceConstructor): - raise TypeError("op_type must be of type ResourceConstructor.") + if not issubclass(op_type, rc.ResourceConstructor): + raise TypeError(f"op_type must be a subclass of ResourceConstructor. Got type {type(op_type)}.") self._name = op_type.__name__ self.op_type = op_type From 979f0c32d3dd35088c34f78cf3c48d13b53e06e3 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 31 Oct 2024 11:40:25 -0400 Subject: [PATCH 060/335] adding class methods --- pennylane/labs/resource_estimation/ops/identity.py | 12 ++++++------ .../ops/op_math/controlled_ops.py | 10 +++++----- .../ops/qubit/non_parametric_ops.py | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index efb90c7ffb1..9543d0d0959 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -16,9 +16,9 @@ def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.Identity, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceConstructor): @@ -31,6 +31,6 @@ def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.GlobalPhase, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index b2ade289971..0144432c2f8 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -24,9 +24,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self): return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceCNOT(qml.CNOT, re.ResourceConstructor): @@ -40,5 +40,5 @@ def resource_params(self) -> dict: return {} @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.CNOT, {}) + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index d32534b15f3..4b5716b26d1 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -14,9 +14,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.Hadamard, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceS(qml.S, re.ResourceConstructor): @@ -33,9 +33,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.S, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceSWAP(qml.SWAP, re.ResourceConstructor): From 4d74f437d434427755d8bbac6877862707f7715e Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 31 Oct 2024 12:03:23 -0400 Subject: [PATCH 061/335] fixing failing tests --- .../ops/qubit/non_parametric_ops.py | 18 +++++++++--------- .../ops/qubit/test_non_parametric_ops.py | 6 +++--- .../qubit/test_parametric_ops_single_qubit.py | 5 ++--- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 05f75553cc4..05b2bfe88eb 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -14,9 +14,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.Hadamard, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceSWAP(qml.SWAP, re.ResourceConstructor): @@ -33,9 +33,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.SWAP, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) class ResourceT(qml.T, re.ResourceConstructor): @@ -48,6 +48,6 @@ def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: def resource_params(self) -> dict: return {} - @staticmethod - def resource_rep() -> re.CompressedResourceOp: - return re.CompressedResourceOp(qml.T, {}) + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index a543fd06e49..69f70edaa6d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -22,7 +22,7 @@ def test_resource_params(self): def test_resource_rep(self): """Test that the compact representation is correct""" - expected = re.CompressedResourceOp(qml.Hadamard, {}) + expected = re.CompressedResourceOp(re.ResourceHadamard, {}) assert re.ResourceHadamard.resource_rep() == expected @@ -44,7 +44,7 @@ def test_resource_params(self): def test_resource_rep(self): """Test the compact representation""" - expected = re.CompressedResourceOp(qml.SWAP, {}) + expected = re.CompressedResourceOp(re.ResourceSWAP, {}) assert re.ResourceSWAP.resource_rep() == expected def test_resources_from_rep(self): @@ -73,5 +73,5 @@ def test_resource_params(self): def test_resource_rep(self): """Test that the compact representation is correct""" - expected = re.CompressedResourceOp(qml.T, {}) + expected = re.CompressedResourceOp(re.ResourceT, {}) assert re.ResourceT.resource_rep() == expected diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index a9474d24948..8a137129a68 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -1,7 +1,6 @@ import numpy as np import pytest -import pennylane as qml import pennylane.labs.resource_estimation as re from pennylane.labs.resource_estimation.ops.qubit.parametric_ops_single_qubit import ( _rotation_resources, @@ -14,7 +13,7 @@ def test_rotation_resources(epsilon): gate_types = {} num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) - t = re.CompressedResourceOp(qml.T, {}) + t = re.CompressedResourceOp(re.ResourceT, {}) gate_types[t] = num_gates assert gate_types == _rotation_resources(epsilon=epsilon) @@ -32,7 +31,7 @@ def test_resources(self, epsilon): def test_resource_rep(self, epsilon): """Test the compact representation""" op = re.ResourceRZ(1.24, wires=0) - expected = re.CompressedResourceOp(qml.RZ, {"epsilon": epsilon}) + expected = re.CompressedResourceOp(re.ResourceRZ, {"epsilon": epsilon}) assert op.resource_rep(epsilon=epsilon) == expected From 920519190a779f60755c704ff57fc3be7ecaa2a3 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 1 Nov 2024 08:49:43 -0400 Subject: [PATCH 062/335] class method --- .../labs/resource_estimation/templates/resource_qft.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 1ba88722dd4..398172c3711 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -36,7 +36,7 @@ def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: def resource_params(self): return {"num_wires": len(self.wires)} - @staticmethod - def resource_rep(num_wires) -> CompressedResourceOp: + @classmethod + def resource_rep(cls, num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} - return CompressedResourceOp(qml.QFT, params) + return CompressedResourceOp(cls, params) From 04c0edade0b24f2cf3247a4261060f08e311ee4f Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 1 Nov 2024 10:24:59 -0400 Subject: [PATCH 063/335] Added for resource tracking --- .../labs/resource_estimation/__init__.py | 1 + .../resource_constructor.py | 4 +- .../resource_estimation/resource_container.py | 8 +- .../resource_estimation/resource_tracking.py | 233 ++++++++++++++++++ 4 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index eb28d5b321b..bd6b8216fbd 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -15,3 +15,4 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources +from .resource_tracking import get_resources, DefaultGateSet, _StandardGateSet diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index b9ecc0033f7..4c109229b3a 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -69,9 +69,9 @@ def resource_params(self) -> dict: """Returns a dictionary containing the minimal information needed to compute a comparessed representation""" - @staticmethod + @classmethod @abstractmethod - def resource_rep(**kwargs) -> CompressedResourceOp: + def resource_rep(cls, **kwargs) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index f1378a95905..568cbf28cb9 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -140,13 +140,13 @@ def __mul__(self, scaler: int) -> "Resources": __rmul__ = __mul__ # same implementation - def __iadd__(self, other: "Resources") -> None: + def __iadd__(self, other: "Resources") -> "Resources": """Add two resources objects in series""" - add_in_series(self, other, in_place=True) + return add_in_series(self, other, in_place=True) - def __imull__(self, scaler: int) -> None: + def __imull__(self, scaler: int) -> "Resources": """Scale a resources object in series""" - mul_in_series(self, scaler, in_place=True) + return mul_in_series(self, scaler, in_place=True) def __str__(self): """String representation of the Resources object.""" diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py new file mode 100644 index 00000000000..b908297d6dd --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -0,0 +1,233 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Core resource tracking logic.""" +from collections import defaultdict +from collections.abc import Callable +from functools import singledispatch, wraps +from typing import Dict, Iterable, List, Set, Union + +import pennylane as qml +from pennylane.measurements import MeasurementProcess +from pennylane.operation import Operation +from pennylane.queuing import AnnotatedQueue +from pennylane.tape import QuantumScript + +from .resource_constructor import ResourceConstructor +from .resource_container import CompressedResourceOp, Resources, mul_in_series + +_StandardGateSet = { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "SWAP", + "CNOT", + "S", + "T", + "Toffoli", + "RX", + "RY", + "RZ", + "PhaseShift", +} + + +DefaultGateSet = { + "Hadamard", + "CNOT", + "S", + "T", + "Toffoli", +} + + +resource_config = { + "error_rx": 10e-3, + "error_ry": 10e-3, + "error_rz": 10e-3, +} + + +@singledispatch +def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: + """Obtain the resources from a quantum circuit or operation in terms of the gates provided + in the gate_set. + + Args: + obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. + gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. + config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. + + Returns: + Resources: The total resources of the quantum circuit. + + Rasies: + TypeError: "Could not obtain resources for obj of type (type(obj)). + """ + + raise TypeError( + f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" + ) + + +@get_resources.register +def resources_from_operation( + obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from an operation""" + res = Resources() # TODO: Add implementation here! + return res + + +@get_resources.register +def resources_from_qfunc( + obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum function which queues operations!""" + + @wraps(obj) + def wrapper(*args, **kwargs): + with AnnotatedQueue() as q: + obj(*args, **kwargs) + + operations = (op for op in q.queue if not isinstance(op, MeasurementProcess)) + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + num_wires = len(set.union((op.wires.toset() for op in operations))) + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + return wrapper + + +@get_resources.register +def resources_from_tape( + obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum tape""" + num_wires = obj.num_wires + operations = obj.operations + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + +def _counts_from_compressed_res_op( + cp_rep: CompressedResourceOp, + gate_counts_dict, + gate_set: Set, + scalar: int = 1, + config: Dict = resource_config, +) -> None: + """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. + + Args: + cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from + gate_counts_dict (_type_): base dictionary to modify with the resource counts + gate_set (Set): the set of operations to track resources with respect too + scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. + config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. + """ + ## If op in gate_set add to resources + if cp_rep._name in gate_set: + gate_counts_dict[cp_rep] += scalar + return + + ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources + resource_decomp = cp_rep.op_type.resources(**cp_rep.params, config=config) + + for sub_cp_rep, counts in resource_decomp.items(): + _counts_from_compressed_res_op( + sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config + ) + return + + +def _temp_map_func(op: Operation) -> ResourceConstructor: + """Temp map function""" + raise NotImplementedError + + +def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: + """Map resources with gate_types made from CompressedResourceOps + into one which tracks just strings of operations! + + Args: + gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops + + Returns: + Dict[str, int]: gate counts in terms of names of operations + """ + clean_gate_counts = defaultdict(int) + + for cmp_res_op, counts in gate_counts.items(): + clean_gate_counts[cmp_res_op._name] += counts + + return clean_gate_counts + + +@qml.QueuingManager.stop_recording() +def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: + """Convert the sequence of operations to a list of compressed resource ops. + + Args: + ops (Iterable[Operation]): set of operations to convert. + + Returns: + List[CompressedResourceOp]: set of converted compressed resource ops. + """ + cmp_rep_ops = [] + for op in ops: + if isinstance(op, ResourceConstructor): + cmp_rep_ops.append(op.resource_rep_from_op()) + + else: + try: + cmp_rep_ops.append(_temp_map_func(op).resource_rep_from_op()) + + except NotImplementedError: + decomp = op.decomposition() + cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) + + return cmp_rep_ops From 749a4cfd21a6e42377dd8df511c9ab4745946ccd Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 1 Nov 2024 10:56:35 -0400 Subject: [PATCH 064/335] adding config param --- .../labs/resource_estimation/__init__.py | 1 + .../ops/op_math/controlled_ops.py | 11 +- .../ops/qubit/non_parametric_ops.py | 8 +- .../ops/qubit/parametric_ops_single_qubit.py | 11 +- .../resource_estimation/resource_tracking.py | 233 ++++++++++++++++++ .../templates/resource_qft.py | 3 +- 6 files changed, 256 insertions(+), 11 deletions(-) create mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 6d33e6d73c8..db4ddc9c5a4 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -15,6 +15,7 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources +from .resource_tracking import resource_config from .ops import ( ResourceCNOT, diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 061a8a03403..26789901cda 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -3,18 +3,21 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -# pylint: disable=too-many-ancestors +# pylint: disable=arguments-differ,too-many-ancestors class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): """Resource class for ControlledPhaseShift""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config=None) -> Dict[re.CompressedResourceOp, int]: + if config is None: + config=re.resource_config + gate_types = {} cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep() + rz = re.ResourceRZ.resource_rep(config=config) gate_types[cnot] = 2 gate_types[rz] = 3 @@ -33,7 +36,7 @@ class ResourceCNOT(qml.CNOT, re.ResourceConstructor): """Resource class for CNOT""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 05b2bfe88eb..513aa19e4de 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -3,12 +3,14 @@ import pennylane as qml import pennylane.labs.resource_estimation as re +#pylint: disable=arguments-differ + class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): """Resource class for Hadamard""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: @@ -23,7 +25,7 @@ class ResourceSWAP(qml.SWAP, re.ResourceConstructor): """Resource class for SWAP""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() gate_types[cnot] = 3 @@ -42,7 +44,7 @@ class ResourceT(qml.T, re.ResourceConstructor): """Resource class for T""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 5617728f2b4..47710109c65 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -5,6 +5,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re +#pylint: disable=arguments-differ + def _rotation_resources(epsilon=10e-3): gate_types = {} @@ -20,10 +22,13 @@ class ResourceRZ(qml.RZ, re.ResourceConstructor): """Resource class for RZ""" @staticmethod - def _resource_decomp(epsilon=10e-3) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=epsilon) + def _resource_decomp(config=None) -> Dict[re.CompressedResourceOp, int]: + if config is None: + config = re.resource_config + + return _rotation_resources(epsilon=config['error_rz']) - def resource_params(): + def resource_params(self): return {} @classmethod diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py new file mode 100644 index 00000000000..b908297d6dd --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -0,0 +1,233 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Core resource tracking logic.""" +from collections import defaultdict +from collections.abc import Callable +from functools import singledispatch, wraps +from typing import Dict, Iterable, List, Set, Union + +import pennylane as qml +from pennylane.measurements import MeasurementProcess +from pennylane.operation import Operation +from pennylane.queuing import AnnotatedQueue +from pennylane.tape import QuantumScript + +from .resource_constructor import ResourceConstructor +from .resource_container import CompressedResourceOp, Resources, mul_in_series + +_StandardGateSet = { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "SWAP", + "CNOT", + "S", + "T", + "Toffoli", + "RX", + "RY", + "RZ", + "PhaseShift", +} + + +DefaultGateSet = { + "Hadamard", + "CNOT", + "S", + "T", + "Toffoli", +} + + +resource_config = { + "error_rx": 10e-3, + "error_ry": 10e-3, + "error_rz": 10e-3, +} + + +@singledispatch +def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: + """Obtain the resources from a quantum circuit or operation in terms of the gates provided + in the gate_set. + + Args: + obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. + gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. + config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. + + Returns: + Resources: The total resources of the quantum circuit. + + Rasies: + TypeError: "Could not obtain resources for obj of type (type(obj)). + """ + + raise TypeError( + f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" + ) + + +@get_resources.register +def resources_from_operation( + obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from an operation""" + res = Resources() # TODO: Add implementation here! + return res + + +@get_resources.register +def resources_from_qfunc( + obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum function which queues operations!""" + + @wraps(obj) + def wrapper(*args, **kwargs): + with AnnotatedQueue() as q: + obj(*args, **kwargs) + + operations = (op for op in q.queue if not isinstance(op, MeasurementProcess)) + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + num_wires = len(set.union((op.wires.toset() for op in operations))) + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + return wrapper + + +@get_resources.register +def resources_from_tape( + obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum tape""" + num_wires = obj.num_wires + operations = obj.operations + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + +def _counts_from_compressed_res_op( + cp_rep: CompressedResourceOp, + gate_counts_dict, + gate_set: Set, + scalar: int = 1, + config: Dict = resource_config, +) -> None: + """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. + + Args: + cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from + gate_counts_dict (_type_): base dictionary to modify with the resource counts + gate_set (Set): the set of operations to track resources with respect too + scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. + config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. + """ + ## If op in gate_set add to resources + if cp_rep._name in gate_set: + gate_counts_dict[cp_rep] += scalar + return + + ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources + resource_decomp = cp_rep.op_type.resources(**cp_rep.params, config=config) + + for sub_cp_rep, counts in resource_decomp.items(): + _counts_from_compressed_res_op( + sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config + ) + return + + +def _temp_map_func(op: Operation) -> ResourceConstructor: + """Temp map function""" + raise NotImplementedError + + +def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: + """Map resources with gate_types made from CompressedResourceOps + into one which tracks just strings of operations! + + Args: + gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops + + Returns: + Dict[str, int]: gate counts in terms of names of operations + """ + clean_gate_counts = defaultdict(int) + + for cmp_res_op, counts in gate_counts.items(): + clean_gate_counts[cmp_res_op._name] += counts + + return clean_gate_counts + + +@qml.QueuingManager.stop_recording() +def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: + """Convert the sequence of operations to a list of compressed resource ops. + + Args: + ops (Iterable[Operation]): set of operations to convert. + + Returns: + List[CompressedResourceOp]: set of converted compressed resource ops. + """ + cmp_rep_ops = [] + for op in ops: + if isinstance(op, ResourceConstructor): + cmp_rep_ops.append(op.resource_rep_from_op()) + + else: + try: + cmp_rep_ops.append(_temp_map_func(op).resource_rep_from_op()) + + except NotImplementedError: + decomp = op.decomposition() + cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) + + return cmp_rep_ops diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 398172c3711..ace89d9ec85 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -9,12 +9,13 @@ ResourceSWAP, ) +#pylint: disable=arguments-differ class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @staticmethod - def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(num_wires, **kwargs) -> Dict[CompressedResourceOp, int]: if not isinstance(num_wires, int): raise TypeError("num_wires must be an int.") From 643f61c1f78c69fa12edfa22b4c05a575acaa2ad Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 1 Nov 2024 13:40:44 -0400 Subject: [PATCH 065/335] adding try/except block to handle undefined resource decomp --- pennylane/labs/resource_estimation/resource_tracking.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index b908297d6dd..b835a961dfc 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -23,7 +23,7 @@ from pennylane.queuing import AnnotatedQueue from pennylane.tape import QuantumScript -from .resource_constructor import ResourceConstructor +from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources, mul_in_series _StandardGateSet = { @@ -175,7 +175,10 @@ def _counts_from_compressed_res_op( return ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources - resource_decomp = cp_rep.op_type.resources(**cp_rep.params, config=config) + try: + resource_decomp = cp_rep.op_type.resources(**cp_rep.params, config=config) + except ResourcesNotDefined: + return for sub_cp_rep, counts in resource_decomp.items(): _counts_from_compressed_res_op( From 584ee0d2993cbe2d3ab62ce7911b6d215087bac7 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 1 Nov 2024 13:43:03 -0400 Subject: [PATCH 066/335] adding config param --- pennylane/labs/resource_estimation/__init__.py | 2 +- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 2 +- .../ops/qubit/parametric_ops_single_qubit.py | 7 +++++-- pennylane/labs/resource_estimation/resource_container.py | 2 +- pennylane/labs/resource_estimation/resource_tracking.py | 1 + .../labs/resource_estimation/templates/resource_qft.py | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 7ae5aa49e6d..4adaefcb735 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -15,7 +15,7 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources -from .resource_tracking import get_resources, DefaultGateSet, _StandardGateSet +from .resource_tracking import get_resources, DefaultGateSet, _StandardGateSet, resource_config from .ops import ( ResourceCNOT, diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 26789901cda..2b481299baa 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -36,7 +36,7 @@ class ResourceCNOT(qml.CNOT, re.ResourceConstructor): """Resource class for CNOT""" @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config=None) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 47710109c65..c35f36b459e 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -32,5 +32,8 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, epsilon=10e-3) -> re.CompressedResourceOp: - return re.CompressedResourceOp(cls, {"epsilon": epsilon}) + def resource_rep(cls, config=None) -> re.CompressedResourceOp: + if config is None: + config = re.resource_config + + return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 57ff4934cd9..4068d34f394 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -78,7 +78,7 @@ def __init__(self, op_type: type, params: dict) -> None: if not issubclass(op_type, rc.ResourceConstructor): raise TypeError(f"op_type must be a subclass of ResourceConstructor. Got type {type(op_type)}.") - self._name = op_type.__name__ + self._name = op_type.__name__[8:] self.op_type = op_type self.params = params self._hashable_params = tuple(params.items()) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index b835a961dfc..cc09d0d348e 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -184,6 +184,7 @@ def _counts_from_compressed_res_op( _counts_from_compressed_res_op( sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config ) + return diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index ace89d9ec85..5d8eeb5597f 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -15,7 +15,7 @@ class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @staticmethod - def _resource_decomp(num_wires, **kwargs) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(num_wires, config=None) -> Dict[CompressedResourceOp, int]: if not isinstance(num_wires, int): raise TypeError("num_wires must be an int.") From 5d4b95a1cb61b2e2d5e6ceaf5245a47217a65138 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 1 Nov 2024 13:56:58 -0400 Subject: [PATCH 067/335] get_resources for ResourceConstructors --- .../labs/resource_estimation/resource_tracking.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index b835a961dfc..efaa343b72c 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -15,7 +15,7 @@ from collections import defaultdict from collections.abc import Callable from functools import singledispatch, wraps -from typing import Dict, Iterable, List, Set, Union +from typing import Dict, Iterable, List, Set import pennylane as qml from pennylane.measurements import MeasurementProcess @@ -24,7 +24,9 @@ from pennylane.tape import QuantumScript from .resource_constructor import ResourceConstructor, ResourcesNotDefined -from .resource_container import CompressedResourceOp, Resources, mul_in_series +from .resource_container import CompressedResourceOp, Resources + +#pylint: disable=dangerous-default-value _StandardGateSet = { "PauliX", @@ -86,6 +88,13 @@ def resources_from_operation( obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config ) -> Resources: """Get resources from an operation""" + + if isinstance(obj, ResourceConstructor): + cp_rep = obj.resource_rep_from_op() + gate_counts_dict = defaultdict(int) + _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) + return Resources(gate_types=gate_counts_dict) + res = Resources() # TODO: Add implementation here! return res From 673a83b3981853e166aaf667365a8c0544d810f1 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 1 Nov 2024 13:59:27 -0400 Subject: [PATCH 068/335] classmethod --- pennylane/labs/resource_estimation/resource_constructor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index b9ecc0033f7..4c109229b3a 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -69,9 +69,9 @@ def resource_params(self) -> dict: """Returns a dictionary containing the minimal information needed to compute a comparessed representation""" - @staticmethod + @classmethod @abstractmethod - def resource_rep(**kwargs) -> CompressedResourceOp: + def resource_rep(cls, **kwargs) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" From f12fbc51fb10d5f8999877219a3f8b61fc758686 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 1 Nov 2024 16:37:00 -0400 Subject: [PATCH 069/335] modifications to allow for resource tracking --- pennylane/labs/resource_estimation/resource_container.py | 2 +- pennylane/labs/resource_estimation/resource_tracking.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 568cbf28cb9..6b21fd55c3f 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -79,7 +79,7 @@ def __init__(self, op_type: type, params: dict) -> None: if not issubclass(op_type, ResourceConstructor): raise TypeError("op_type must be of type ResourceConstructor.") - self._name = op_type.__name__ + self._name = (op_type.__name__).strip("Resource") self.op_type = op_type self.params = params self._hashable_params = tuple(params.items()) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index efaa343b72c..dd9293826ef 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -22,6 +22,7 @@ from pennylane.operation import Operation from pennylane.queuing import AnnotatedQueue from pennylane.tape import QuantumScript +from pennylane.wires import Wires from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources @@ -110,7 +111,7 @@ def wrapper(*args, **kwargs): with AnnotatedQueue() as q: obj(*args, **kwargs) - operations = (op for op in q.queue if not isinstance(op, MeasurementProcess)) + operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) compressed_res_ops_lst = _operations_to_compressed_reps(operations) gate_counts_dict = defaultdict(int) @@ -128,7 +129,7 @@ def wrapper(*args, **kwargs): clean_gate_counts = _clean_gate_counts(condensed_gate_counts) num_gates = sum(clean_gate_counts.values()) - num_wires = len(set.union((op.wires.toset() for op in operations))) + num_wires = len(Wires.shared_wires(tuple(op.wires for op in operations))) return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) return wrapper @@ -184,10 +185,14 @@ def _counts_from_compressed_res_op( return ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources +<<<<<<< Updated upstream try: resource_decomp = cp_rep.op_type.resources(**cp_rep.params, config=config) except ResourcesNotDefined: return +======= + resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) +>>>>>>> Stashed changes for sub_cp_rep, counts in resource_decomp.items(): _counts_from_compressed_res_op( From 24bb68558e125a6cc3370ff9eb70fa9aeeebc4fa Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 1 Nov 2024 16:48:59 -0400 Subject: [PATCH 070/335] remove try-except --- pennylane/labs/resource_estimation/resource_tracking.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index dd9293826ef..c1e7fb39ef5 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -27,7 +27,7 @@ from .resource_constructor import ResourceConstructor, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources -#pylint: disable=dangerous-default-value +# pylint: disable=dangerous-default-value _StandardGateSet = { "PauliX", @@ -185,14 +185,7 @@ def _counts_from_compressed_res_op( return ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources -<<<<<<< Updated upstream - try: - resource_decomp = cp_rep.op_type.resources(**cp_rep.params, config=config) - except ResourcesNotDefined: - return -======= resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) ->>>>>>> Stashed changes for sub_cp_rep, counts in resource_decomp.items(): _counts_from_compressed_res_op( From 3626e5f870b1afa08e0e179a8f4ae6e058701c2a Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 08:16:18 -0500 Subject: [PATCH 071/335] minor changes --- .../resource_estimation/ops/op_math/controlled_ops.py | 9 +++------ .../ops/qubit/parametric_ops_single_qubit.py | 10 ++-------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 2b481299baa..cc43a3a5afe 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -10,14 +10,11 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstruc """Resource class for ControlledPhaseShift""" @staticmethod - def _resource_decomp(config=None) -> Dict[re.CompressedResourceOp, int]: - if config is None: - config=re.resource_config - + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep(config=config) + rz = re.ResourceRZ.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 3 @@ -36,7 +33,7 @@ class ResourceCNOT(qml.CNOT, re.ResourceConstructor): """Resource class for CNOT""" @staticmethod - def _resource_decomp(config=None) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index c35f36b459e..8c785be2910 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -22,18 +22,12 @@ class ResourceRZ(qml.RZ, re.ResourceConstructor): """Resource class for RZ""" @staticmethod - def _resource_decomp(config=None) -> Dict[re.CompressedResourceOp, int]: - if config is None: - config = re.resource_config - + def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config['error_rz']) def resource_params(self): return {} @classmethod - def resource_rep(cls, config=None) -> re.CompressedResourceOp: - if config is None: - config = re.resource_config - + def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) From 9eda998cfbc97795df85e257f684a0034da428ed Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 08:45:47 -0500 Subject: [PATCH 072/335] updating controlled tests --- .../resource_estimation/ops/op_math/test_controlled_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 591efc8274d..25648d3eef3 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -17,7 +17,7 @@ def test_resources(self, phi, wires): expected = { re.CompressedResourceOp(re.ResourceCNOT, {}): 2, - re.CompressedResourceOp(re.ResourceRZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(re.ResourceRZ, {}): 3, } assert op.resources() == expected @@ -55,7 +55,7 @@ def test_resources_from_rep(self, phi, wires): expected = { re.CompressedResourceOp(re.ResourceCNOT, {}): 2, - re.CompressedResourceOp(re.ResourceRZ, {"epsilon": 10e-3}): 3, + re.CompressedResourceOp(re.ResourceRZ, {}): 3, } assert ( From df37479ab5525427b1537ebef4f82d488c3f69c6 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 08:49:07 -0500 Subject: [PATCH 073/335] updating qft tests --- .../templates/test_resource_qft.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 2fb113ced88..a4896f515ba 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -1,6 +1,5 @@ import pytest -import pennylane as qml import pennylane.labs.resource_estimation as re @@ -18,9 +17,9 @@ class TestQFT: ) def test_resources(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test the resources method returns the correct dictionary""" - hadamard = re.CompressedResourceOp(qml.Hadamard, {}) - swap = re.CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + hadamard = re.CompressedResourceOp(re.ResourceHadamard, {}) + swap = re.CompressedResourceOp(re.ResourceSWAP, {}) + ctrl_phase_shift = re.CompressedResourceOp(re.ResourceControlledPhaseShift, {}) expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} @@ -36,7 +35,7 @@ def test_resource_params(self, wires): def test_resource_rep(self, num_wires): """Test the resource_rep returns the correct CompressedResourceOp""" - expected = re.CompressedResourceOp(qml.QFT, {"num_wires": num_wires}) + expected = re.CompressedResourceOp(re.ResourceQFT, {"num_wires": num_wires}) assert re.ResourceQFT.resource_rep(num_wires) == expected @pytest.mark.parametrize( @@ -51,9 +50,9 @@ def test_resource_rep(self, num_wires): def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_phase_shift): """Test that computing the resources from a compressed representation works""" - hadamard = re.CompressedResourceOp(qml.Hadamard, {}) - swap = re.CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = re.CompressedResourceOp(qml.ControlledPhaseShift, {}) + hadamard = re.CompressedResourceOp(re.ResourceHadamard, {}) + swap = re.CompressedResourceOp(re.ResourceSWAP, {}) + ctrl_phase_shift = re.CompressedResourceOp(re.ResourceControlledPhaseShift, {}) expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} From 5a372b03d8ec2aabf5d1cb8bed2cb61c4bee7119 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 09:01:34 -0500 Subject: [PATCH 074/335] fixing circular import, updating tests --- .../labs/resource_estimation/resource_constructor.py | 9 ++++----- .../labs/resource_estimation/resource_container.py | 5 ++--- .../resource_estimation/test_resource_constructor.py | 10 +++++----- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 4c109229b3a..6b962508018 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -1,8 +1,7 @@ from abc import ABC, abstractmethod from typing import Callable, Dict -from .resource_container import CompressedResourceOp - +import pennylane.labs.resource_estimation.resource_container as rc class ResourceConstructor(ABC): r"""This is an abstract class that defines the methods a PennyLane Operator @@ -49,7 +48,7 @@ def resource_rep(self) -> CompressedResourceOp: @staticmethod @abstractmethod - def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(*args, **kwargs) -> Dict[rc.CompressedResourceOp, int]: """Returns the Resource object. This method is only to be used inside the methods of classes inheriting from ResourceConstructor.""" @@ -71,11 +70,11 @@ def resource_params(self) -> dict: @classmethod @abstractmethod - def resource_rep(cls, **kwargs) -> CompressedResourceOp: + def resource_rep(cls, **kwargs) -> rc.CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" - def resource_rep_from_op(self) -> CompressedResourceOp: + def resource_rep_from_op(self) -> rc.CompressedResourceOp: """Returns a compressed representation directly from the operator""" params = self.resource_params() return self.__class__.resource_rep(**params) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index f1378a95905..e6818a089d0 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -16,8 +16,7 @@ from collections import defaultdict from dataclasses import dataclass, field -from .resource_constructor import ResourceConstructor - +import pennylane.labs.resource_estimation.resource_constructor as rc class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. @@ -76,7 +75,7 @@ def __init__(self, op_type: type, params: dict) -> None: >>> print(op_tp) QSVT(num_wires=5, num_angles=100) """ - if not issubclass(op_type, ResourceConstructor): + if not issubclass(op_type, rc.ResourceConstructor): raise TypeError("op_type must be of type ResourceConstructor.") self._name = op_type.__name__ diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 4d90a11111b..9c5d696b780 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -1,10 +1,10 @@ import pytest -from pennylane.labs.resource_estimation import ResourceConstructor +import pennylane.labs.resource_estimation as re def test_abstract_resource_decomp(): - class DummyClass(ResourceConstructor): + class DummyClass(re.ResourceConstructor): def resource_params(): return @@ -20,7 +20,7 @@ def resource_rep(): def test_abstract_resource_params(): - class DummyClass(ResourceConstructor): + class DummyClass(re.ResourceConstructor): @staticmethod def _resource_decomp(): return @@ -36,7 +36,7 @@ def resource_rep(): def test_abstract_resource_rep(): - class DummyClass(ResourceConstructor): + class DummyClass(re.ResourceConstructor): @staticmethod def _resource_decomp(): return @@ -52,7 +52,7 @@ def resource_params(): def test_set_resources(): - class DummyClass(ResourceConstructor): + class DummyClass(re.ResourceConstructor): def resource_params(): return From ff106069773715979ccaad894878e432c268107c Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 09:24:38 -0500 Subject: [PATCH 075/335] updating test --- .../ops/qubit/test_parametric_ops_single_qubit.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 8a137129a68..c5fb5d8d2f8 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -25,22 +25,23 @@ class TestRZ: def test_resources(self, epsilon): """Test the resources method""" op = re.ResourceRZ(1.24, wires=0) - assert op.resources(epsilon=epsilon) == _rotation_resources(epsilon=epsilon) + config = {"error_rz": epsilon} + assert op.resources(config) == _rotation_resources(epsilon=epsilon) @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_resource_rep(self, epsilon): """Test the compact representation""" op = re.ResourceRZ(1.24, wires=0) - expected = re.CompressedResourceOp(re.ResourceRZ, {"epsilon": epsilon}) + expected = re.CompressedResourceOp(re.ResourceRZ, {}) - assert op.resource_rep(epsilon=epsilon) == expected + assert op.resource_rep() == expected @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_resources_from_rep(self, epsilon): """Test the resources can be obtained from the compact representation""" - + config = {"error_rz": epsilon} expected = _rotation_resources(epsilon=epsilon) assert ( - re.ResourceRZ.resources(**re.ResourceRZ.resource_rep(epsilon=epsilon).params) + re.ResourceRZ.resources(config, **re.ResourceRZ.resource_rep().params) == expected ) From b1bd4aca250be95012082c3fde3348aedfd21ff9 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 10:08:26 -0500 Subject: [PATCH 076/335] pylint --- .../resource_constructor.py | 15 +++++++ .../resource_estimation/resource_container.py | 1 + .../test_resource_constructor.py | 42 +++++++++++++++++-- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 6b962508018..681e2697966 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -1,8 +1,23 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Abstract base class for resource operators.""" from abc import ABC, abstractmethod from typing import Callable, Dict import pennylane.labs.resource_estimation.resource_container as rc + class ResourceConstructor(ABC): r"""This is an abstract class that defines the methods a PennyLane Operator must implement in order to be used for resource estimation. diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index e6818a089d0..352fe312fa8 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -18,6 +18,7 @@ import pennylane.labs.resource_estimation.resource_constructor as rc + class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 9c5d696b780..55356ce7005 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -1,11 +1,33 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Test the abstract ResourceConstructor class +""" import pytest import pennylane.labs.resource_estimation as re +#pylint: disable=abstract-class-instantiated,arguments-differ + def test_abstract_resource_decomp(): + """Test that the _resource_decomp method is abstract.""" + class DummyClass(re.ResourceConstructor): - def resource_params(): + """Dummy class for testing""" + + def resource_params(self): return @staticmethod @@ -20,12 +42,16 @@ def resource_rep(): def test_abstract_resource_params(): + """Test that the resource_params method is abstract""" + class DummyClass(re.ResourceConstructor): + """Dummy class for testing""" + @staticmethod def _resource_decomp(): return - def resource_rep(): + def resource_rep(self): return with pytest.raises( @@ -36,12 +62,16 @@ def resource_rep(): def test_abstract_resource_rep(): + """Test that the resource_rep method is abstract""" + class DummyClass(re.ResourceConstructor): + """Dummy class for testing""" + @staticmethod def _resource_decomp(): return - def resource_params(): + def resource_params(self): return with pytest.raises( @@ -52,8 +82,12 @@ def resource_params(): def test_set_resources(): + """Test that the resources method can be overriden""" + class DummyClass(re.ResourceConstructor): - def resource_params(): + """Dummy class for testing""" + + def resource_params(self): return @staticmethod From d4aaf70846b2a15316656039f6fd83a1842e067c Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 10:17:12 -0500 Subject: [PATCH 077/335] black --- .../labs/tests/resource_estimation/test_resource_constructor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 55356ce7005..0a0e3a161e8 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -18,7 +18,7 @@ import pennylane.labs.resource_estimation as re -#pylint: disable=abstract-class-instantiated,arguments-differ +# pylint: disable=abstract-class-instantiated,arguments-differ def test_abstract_resource_decomp(): From de686024572593fdc15623d24c0a6f56a2e0a612 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 10:34:14 -0500 Subject: [PATCH 078/335] pylint --- pennylane/labs/resource_estimation/resource_container.py | 4 ---- pennylane/labs/resource_estimation/templates/resource_qft.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index aa90081a8fc..21a3278d0fb 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -76,11 +76,7 @@ def __init__(self, op_type: type, params: dict) -> None: QSVT(num_wires=5, num_angles=100) """ if not issubclass(op_type, rc.ResourceConstructor): -<<<<<<< HEAD raise TypeError(f"op_type must be a subclass of ResourceConstructor. Got type {type(op_type)}.") -======= - raise TypeError("op_type must be of type ResourceConstructor.") ->>>>>>> d4aaf70846b2a15316656039f6fd83a1842e067c self._name = (op_type.__name__).strip("Resource") self.op_type = op_type diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 5d8eeb5597f..d2c76b34411 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -15,7 +15,7 @@ class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" @staticmethod - def _resource_decomp(num_wires, config=None) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: if not isinstance(num_wires, int): raise TypeError("num_wires must be an int.") From 6c0f5d05d6e6301b42a0354c9818e58a15b2cb6a Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 10:43:57 -0500 Subject: [PATCH 079/335] pylint --- .../labs/resource_estimation/ops/__init__.py | 15 +++++++++++++++ .../resource_estimation/ops/op_math/__init__.py | 15 +++++++++++++++ .../ops/op_math/controlled_ops.py | 14 ++++++++++++++ .../resource_estimation/ops/qubit/__init__.py | 15 +++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index e4f5cab4678..8e1ecf7f583 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -1,3 +1,18 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""This module contains experimental resource estimation functionality. """ + from .qubit import ( ResourceHadamard, ResourceRZ, diff --git a/pennylane/labs/resource_estimation/ops/op_math/__init__.py b/pennylane/labs/resource_estimation/ops/op_math/__init__.py index e8e76804b47..b65c404b698 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/__init__.py +++ b/pennylane/labs/resource_estimation/ops/op_math/__init__.py @@ -1 +1,16 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""This module contains experimental resource estimation functionality. """ + from .controlled_ops import * diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index cc43a3a5afe..e8a382938ed 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -1,3 +1,17 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for controlled operations.""" from typing import Dict import pennylane as qml diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index 1b60a91548b..960e4f116c2 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -1,3 +1,18 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""This module contains experimental resource estimation functionality. """ + from .non_parametric_ops import ( ResourceHadamard, ResourceSWAP, From 9e4a5ee1d76d30a0ee2a77c2840f78d3162cd857 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 10:57:51 -0500 Subject: [PATCH 080/335] pylint --- pennylane/labs/resource_estimation/resource_tracking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index c1e7fb39ef5..0193997cd2d 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -24,10 +24,10 @@ from pennylane.tape import QuantumScript from pennylane.wires import Wires -from .resource_constructor import ResourceConstructor, ResourcesNotDefined +from .resource_constructor import ResourceConstructor from .resource_container import CompressedResourceOp, Resources -# pylint: disable=dangerous-default-value +# pylint: disable=dangerous-default-value,protected-access _StandardGateSet = { "PauliX", From 8016ea074c6459b1000462002bae9f3d99a88aa0 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 11:27:11 -0500 Subject: [PATCH 081/335] formatting --- .../ops/qubit/non_parametric_ops.py | 16 ++++++++++++- .../ops/qubit/parametric_ops_single_qubit.py | 18 ++++++++++++-- .../resource_estimation/resource_container.py | 5 +++- .../resource_estimation/templates/__init__.py | 14 +++++++++++ .../templates/resource_qft.py | 17 ++++++++++++- .../ops/qubit/test_non_parametric_ops.py | 17 ++++++++++++- .../qubit/test_parametric_ops_single_qubit.py | 24 ++++++++++++++----- 7 files changed, 99 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 513aa19e4de..3be483aa959 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -1,9 +1,23 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for non parametric single qubit operations.""" from typing import Dict import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=arguments-differ +# pylint: disable=arguments-differ class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 8c785be2910..3f3368f296b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -1,3 +1,17 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for parametric single qubit operations.""" from typing import Dict import numpy as np @@ -5,7 +19,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=arguments-differ +# pylint: disable=arguments-differ def _rotation_resources(epsilon=10e-3): @@ -23,7 +37,7 @@ class ResourceRZ(qml.RZ, re.ResourceConstructor): @staticmethod def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=config['error_rz']) + return _rotation_resources(epsilon=config["error_rz"]) def resource_params(self): return {} diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 21a3278d0fb..910af32a5f8 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -18,6 +18,7 @@ import pennylane.labs.resource_estimation.resource_constructor as rc + class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. @@ -76,7 +77,9 @@ def __init__(self, op_type: type, params: dict) -> None: QSVT(num_wires=5, num_angles=100) """ if not issubclass(op_type, rc.ResourceConstructor): - raise TypeError(f"op_type must be a subclass of ResourceConstructor. Got type {type(op_type)}.") + raise TypeError( + f"op_type must be a subclass of ResourceConstructor. Got type {type(op_type)}." + ) self._name = (op_type.__name__).strip("Resource") self.op_type = op_type diff --git a/pennylane/labs/resource_estimation/templates/__init__.py b/pennylane/labs/resource_estimation/templates/__init__.py index cead6cd577a..e5426bb269c 100644 --- a/pennylane/labs/resource_estimation/templates/__init__.py +++ b/pennylane/labs/resource_estimation/templates/__init__.py @@ -1 +1,15 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""This module contains experimental resource estimation functionality. """ from .resource_qft import ResourceQFT diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index d2c76b34411..29118820086 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -1,3 +1,17 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operator for the QFT template.""" from typing import Dict import pennylane as qml @@ -9,7 +23,8 @@ ResourceSWAP, ) -#pylint: disable=arguments-differ +# pylint: disable=arguments-differ + class ResourceQFT(qml.QFT, ResourceConstructor): """Resource class for QFT""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 69f70edaa6d..6870f100ed2 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -1,6 +1,21 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for non parametric resource operators. +""" import pytest -import pennylane as qml import pennylane.labs.resource_estimation as re # pylint: disable=use-implicit-booleaness-not-comparison diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index c5fb5d8d2f8..cdcc46b7cbc 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -1,3 +1,19 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for parametric single qubit resource operators. +""" import numpy as np import pytest @@ -28,8 +44,7 @@ def test_resources(self, epsilon): config = {"error_rz": epsilon} assert op.resources(config) == _rotation_resources(epsilon=epsilon) - @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) - def test_resource_rep(self, epsilon): + def test_resource_rep(self): """Test the compact representation""" op = re.ResourceRZ(1.24, wires=0) expected = re.CompressedResourceOp(re.ResourceRZ, {}) @@ -41,7 +56,4 @@ def test_resources_from_rep(self, epsilon): """Test the resources can be obtained from the compact representation""" config = {"error_rz": epsilon} expected = _rotation_resources(epsilon=epsilon) - assert ( - re.ResourceRZ.resources(config, **re.ResourceRZ.resource_rep().params) - == expected - ) + assert re.ResourceRZ.resources(config, **re.ResourceRZ.resource_rep().params) == expected From 31cb75d205155e2ae16146ea33a106337252101c Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 4 Nov 2024 12:01:10 -0500 Subject: [PATCH 082/335] updating resourcerot tests --- .../ops/qubit/non_parametric_ops.py | 1 - .../ops/qubit/parametric_ops_single_qubit.py | 4 +-- .../qubit/test_parametric_ops_single_qubit.py | 33 +++++++++---------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index ea26c3d6cc5..ef1545408a7 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -54,7 +54,6 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - class ResourceSWAP(qml.SWAP, re.ResourceConstructor): """Resource class for SWAP""" diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index c0966c2770a..7a397b3f111 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -58,7 +58,7 @@ class ResourceRX(qml.RX, re.ResourceConstructor): @staticmethod def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=config['error_rx']) + return _rotation_resources(epsilon=config["error_rx"]) def resource_params(self) -> dict: return {} @@ -73,7 +73,7 @@ class ResourceRY(qml.RY, re.ResourceConstructor): @staticmethod def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=config['error_ry']) + return _rotation_resources(epsilon=config["error_ry"]) def resource_params(self) -> dict: return {} diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 9f441881e50..5f8daa69a4e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -43,13 +43,13 @@ class TestPauliRotation: def test_resources(self, resource_class, epsilon): """Test the resources method""" - label = "error_" + resource_class.__name__.replace("Resource", '').lower() + label = "error_" + resource_class.__name__.replace("Resource", "").lower() config = {label: epsilon} op = resource_class(1.24, wires=0) assert op.resources(config) == _rotation_resources(epsilon=epsilon) @pytest.mark.parametrize("resource_class, epsilon", params) - def test_resource_rep(self, resource_class, epsilon): #pylint: disable=unused-argument + def test_resource_rep(self, resource_class, epsilon): # pylint: disable=unused-argument """Test the compact representation""" op = resource_class(1.24, wires=0) expected = re.CompressedResourceOp(resource_class, {}) @@ -59,7 +59,7 @@ def test_resource_rep(self, resource_class, epsilon): #pylint: disable=unused-ar def test_resources_from_rep(self, resource_class, epsilon): """Test the resources can be obtained from the compact representation""" - label = "error_" + resource_class.__name__.replace("Resource", '').lower() + label = "error_" + resource_class.__name__.replace("Resource", "").lower() config = {label: epsilon} op = resource_class(1.24, wires=0) expected = _rotation_resources(epsilon=epsilon) @@ -69,28 +69,27 @@ def test_resources_from_rep(self, resource_class, epsilon): class TestRot: """Test ResourceRot""" - errors = [10e-3, 10e-4, 10e-5] - params = list(zip(errors, errors, errors)) - - @pytest.mark.parameterize("error_rx, error_ry, error_rz", params) - def test_resources(self, error_rx, error_ry, error_rz): + def test_resources(self): """Test the resources method""" - op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) - config = {"error_rx": error_rx, "error_ry": error_ry, "error_rz": error_rz} + rx = re.ResourceRX.resource_rep() + ry = re.ResourceRY.resource_rep() + rz = re.ResourceRZ.resource_rep() + expected = {rx: 1, ry: 1, rz: 1} + + assert op.resources() == expected def test_resource_rep(self): """Test the compressed representation""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) - rx = re.CompressedResourceOp(re.ResourceRX, {}) - ry = re.CompressedResourceOp(re.ResourceRY, {}) - rz = re.CompressedResourceOp(re.ResourceRZ, {}) - expected = {rx: 1, ry: 1, rz: 1} + expected = re.CompressedResourceOp(re.ResourceRot, {}) assert op.resource_rep() == expected - @pytest.mark.parameterize("error_rx, error_ry, error_rz", params) def test_resources_from_rep(self): """Test that the resources can be obtained from the compact representation""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) - - assert re.ResourceRot.resources(config, **op.resource_rep().params) == expected + rx = re.CompressedResourceOp(re.ResourceRX, {}) + ry = re.CompressedResourceOp(re.ResourceRY, {}) + rz = re.CompressedResourceOp(re.ResourceRZ, {}) + expected = {rx: 1, ry: 1, rz: 1} + assert re.ResourceRot.resources(**op.resource_rep().params) == expected From 90312a3df96786116756ce0b4c9a169e0e1709b3 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 4 Nov 2024 16:37:13 -0500 Subject: [PATCH 083/335] fix sphinx --- pennylane/templates/subroutines/trotter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/templates/subroutines/trotter.py b/pennylane/templates/subroutines/trotter.py index 87dfac458d6..339b6adca4e 100644 --- a/pennylane/templates/subroutines/trotter.py +++ b/pennylane/templates/subroutines/trotter.py @@ -253,11 +253,11 @@ def queue(self, context=qml.QueuingManager): context.append(self) return self - def resources(self) -> Resources: - """The resource requirements for a given instance of the Suzuki-Trotter product. + def resources(self) -> qml.resource.Resources: + r"""The resource requirements for a given instance of the Suzuki-Trotter product. Returns: - Resources: The resources for an instance of ``TrotterProduct``. + :class:`~.resource.Resources`: The resources for an instance of ``TrotterProduct``. """ with qml.QueuingManager.stop_recording(): decomp = self.compute_decomposition(*self.parameters, **self.hyperparameters) From 1d3a2019047f41aea5f86d135223a8acf4e7e367 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 08:56:20 -0500 Subject: [PATCH 084/335] swapping .strip with .replace --- pennylane/labs/resource_estimation/resource_container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 99eeffdcc1d..32817bed411 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -79,7 +79,7 @@ def __init__(self, op_type: type, params: dict) -> None: if not issubclass(op_type, rc.ResourceConstructor): raise TypeError("op_type must be of type ResourceConstructor.") - self._name = (op_type.__name__).strip("Resource") + self._name = (op_type.__name__).replace("Resource", "") self.op_type = op_type self.params = params self._hashable_params = tuple(params.items()) From 87b4ee365e405101a9a76132e874f99654cc9e01 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 09:21:50 -0500 Subject: [PATCH 085/335] adding .pylintrc --- pennylane/labs/tests/.pylintrc | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 pennylane/labs/tests/.pylintrc diff --git a/pennylane/labs/tests/.pylintrc b/pennylane/labs/tests/.pylintrc new file mode 100644 index 00000000000..e400d9d33c4 --- /dev/null +++ b/pennylane/labs/tests/.pylintrc @@ -0,0 +1,58 @@ +[MASTER] +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +#extension-pkg-whitelist= + +[TYPECHECK] + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +# Cyclical import checks are disabled for now as they are frequently used in +# the code base, but this can be removed in the future once cycles are resolved. +disable= + line-too-long, + invalid-name, + too-many-lines, + redefined-builtin, + too-many-locals, + duplicate-code, + cyclic-import, + import-error, + bad-option-value, + import-outside-toplevel, + missing-class-docstring, + missing-function-docstring, + no-self-use, + no-member, # because of qnode decorator + comparison-with-callable, + unsubscriptable-object, # because of qnode decorator + not-callable, # because of qnode decorator + unexpected-keyword-arg, + arguments-differ, + no-value-for-parameter + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes= From a53d5f56e9971e41b1dd4fd8034d22bd9f958c19 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 09:23:52 -0500 Subject: [PATCH 086/335] pylintrc --- pennylane/labs/tests/.pylintrc | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 pennylane/labs/tests/.pylintrc diff --git a/pennylane/labs/tests/.pylintrc b/pennylane/labs/tests/.pylintrc new file mode 100644 index 00000000000..e400d9d33c4 --- /dev/null +++ b/pennylane/labs/tests/.pylintrc @@ -0,0 +1,58 @@ +[MASTER] +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +#extension-pkg-whitelist= + +[TYPECHECK] + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +[MESSAGES CONTROL] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +# Cyclical import checks are disabled for now as they are frequently used in +# the code base, but this can be removed in the future once cycles are resolved. +disable= + line-too-long, + invalid-name, + too-many-lines, + redefined-builtin, + too-many-locals, + duplicate-code, + cyclic-import, + import-error, + bad-option-value, + import-outside-toplevel, + missing-class-docstring, + missing-function-docstring, + no-self-use, + no-member, # because of qnode decorator + comparison-with-callable, + unsubscriptable-object, # because of qnode decorator + not-callable, # because of qnode decorator + unexpected-keyword-arg, + arguments-differ, + no-value-for-parameter + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes= From eb2f87a52b7d8e261cf8a6a022dff25d5ae25dd2 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 09:35:52 -0500 Subject: [PATCH 087/335] removing pylintrc --- pennylane/labs/tests/.pylintrc | 58 ---------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 pennylane/labs/tests/.pylintrc diff --git a/pennylane/labs/tests/.pylintrc b/pennylane/labs/tests/.pylintrc deleted file mode 100644 index e400d9d33c4..00000000000 --- a/pennylane/labs/tests/.pylintrc +++ /dev/null @@ -1,58 +0,0 @@ -[MASTER] -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -#extension-pkg-whitelist= - -[TYPECHECK] - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). This supports can work -# with qualified names. -ignored-classes= - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. -enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). -# Cyclical import checks are disabled for now as they are frequently used in -# the code base, but this can be removed in the future once cycles are resolved. -disable= - line-too-long, - invalid-name, - too-many-lines, - redefined-builtin, - too-many-locals, - duplicate-code, - cyclic-import, - import-error, - bad-option-value, - import-outside-toplevel, - missing-class-docstring, - missing-function-docstring, - no-self-use, - no-member, # because of qnode decorator - comparison-with-callable, - unsubscriptable-object, # because of qnode decorator - not-callable, # because of qnode decorator - unexpected-keyword-arg, - arguments-differ, - no-value-for-parameter - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes= From 007f166803e798c002e9b4f94a763f34b00f55cb Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 09:37:00 -0500 Subject: [PATCH 088/335] remove pylintrc --- pennylane/labs/tests/.pylintrc | 58 ---------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 pennylane/labs/tests/.pylintrc diff --git a/pennylane/labs/tests/.pylintrc b/pennylane/labs/tests/.pylintrc deleted file mode 100644 index e400d9d33c4..00000000000 --- a/pennylane/labs/tests/.pylintrc +++ /dev/null @@ -1,58 +0,0 @@ -[MASTER] -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -#extension-pkg-whitelist= - -[TYPECHECK] - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). This supports can work -# with qualified names. -ignored-classes= - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. -enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). -# Cyclical import checks are disabled for now as they are frequently used in -# the code base, but this can be removed in the future once cycles are resolved. -disable= - line-too-long, - invalid-name, - too-many-lines, - redefined-builtin, - too-many-locals, - duplicate-code, - cyclic-import, - import-error, - bad-option-value, - import-outside-toplevel, - missing-class-docstring, - missing-function-docstring, - no-self-use, - no-member, # because of qnode decorator - comparison-with-callable, - unsubscriptable-object, # because of qnode decorator - not-callable, # because of qnode decorator - unexpected-keyword-arg, - arguments-differ, - no-value-for-parameter - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes= From 708e991265b7a580f0a226775128fd12827556f3 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 09:43:59 -0500 Subject: [PATCH 089/335] docstring --- .../templates/test_resource_qft.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index a4896f515ba..43e93b6984d 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -1,3 +1,19 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Test the ResourceQFT class +""" import pytest import pennylane.labs.resource_estimation as re From 528d4cfd1dbbf4ddb005857e81801046df75212f Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 09:51:36 -0500 Subject: [PATCH 090/335] formatting --- pennylane/labs/resource_estimation/resource_container.py | 1 - .../resource_estimation/ops/op_math/test_controlled_ops.py | 3 ++- .../resource_estimation/ops/qubit/test_non_parametric_ops.py | 2 +- .../ops/qubit/test_parametric_ops_single_qubit.py | 2 ++ .../tests/resource_estimation/templates/test_resource_qft.py | 2 ++ 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 159c31c78ef..99196e5244b 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -81,7 +81,6 @@ def __init__(self, op_type: type, params: dict) -> None: f"op_type must be a subclass of ResourceConstructor. Got type {type(op_type)}." ) - self._name = (op_type.__name__).replace("Resource", "") self.op_type = op_type self.params = params diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 25648d3eef3..94765bf8b2e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -1,8 +1,9 @@ import pytest -import pennylane as qml import pennylane.labs.resource_estimation as re +# pylint: disable=no-self-use + class TestControlledPhaseShift: """Test ResourceControlledPhaseShift""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 6870f100ed2..95f43f37686 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -18,7 +18,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=no-self-use,use-implicit-booleaness-not-comparison class TestHadamard: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index cdcc46b7cbc..df666ea965f 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -22,6 +22,8 @@ _rotation_resources, ) +# pylint: disable=no-self-use + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_rotation_resources(epsilon): diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 43e93b6984d..fbd90f6ff47 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -18,6 +18,8 @@ import pennylane.labs.resource_estimation as re +# pylint: disable=no-self-use + class TestQFT: """Test the ResourceQFT class""" From 246764f948f6e0d97d26d4ddd5c75b5ca06f1d43 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 5 Nov 2024 09:55:21 -0500 Subject: [PATCH 091/335] re-run CI From 46d9749bd964b03dab9f89fe41b520bffc2fb3f1 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 10:01:13 -0500 Subject: [PATCH 092/335] docstring --- .../ops/op_math/test_controlled_ops.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 94765bf8b2e..2397173f814 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -1,3 +1,19 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for controlled resource operators. +""" import pytest import pennylane.labs.resource_estimation as re From fb43c45832fac3e35a19b7fb211ecd10f93b7258 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 10:18:18 -0500 Subject: [PATCH 093/335] formatting --- .../labs/resource_estimation/ops/identity.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 9543d0d0959..fd00fdd20d2 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -1,23 +1,37 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for identity operations.""" from typing import Dict import pennylane as qml import pennylane.labs.resource_estimation as re -# pylint: disable=too-many-ancestors +# pylint: disable=no-self-use,too-many-ancestors class ResourceIdentity(qml.Identity, re.ResourceConstructor): """Resource class for Identity""" @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: return {} def resource_params(self) -> dict: return {} @classmethod - def resource_rep(cls) -> re.CompressedResourceOp: + def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @@ -25,12 +39,12 @@ class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceConstructor): """Resource class for GlobalPhase""" @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: return {} def resource_params(self) -> dict: return {} @classmethod - def resource_rep(cls) -> re.CompressedResourceOp: + def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) From dd96aad9ec82028c347973b7bbf97e9a8fd8a10d Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 13:47:59 -0500 Subject: [PATCH 094/335] adding a type hint --- pennylane/labs/resource_estimation/resource_constructor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 681e2697966..77306051432 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -68,7 +68,7 @@ def _resource_decomp(*args, **kwargs) -> Dict[rc.CompressedResourceOp, int]: the methods of classes inheriting from ResourceConstructor.""" @classmethod - def resources(cls, *args, **kwargs): + def resources(cls, *args, **kwargs) -> Dict[rc.CompressedResourceOp, int]: """Returns the Resource object. This method is intended to be user facing and overridable.""" return cls._resource_decomp(*args, **kwargs) From 63d249274153fae8e4aa4dfd9bc1962fac208ea4 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 5 Nov 2024 13:49:43 -0500 Subject: [PATCH 095/335] pylint --- .../resource_estimation/ops/test_identity.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py index 5e2ffa952cc..4f4c0630320 100644 --- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -1,6 +1,22 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for identity resource operators +""" import pennylane.labs.resource_estimation as re -# pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=no-self-use,use-implicit-booleaness-not-comparison class TestIdentity: From be595b893dc1725c407a90b335e50c961c3fb31f Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 09:55:36 -0500 Subject: [PATCH 096/335] adding some controlled_ops --- .../labs/resource_estimation/ops/__init__.py | 10 + .../ops/op_math/controlled_ops.py | 251 +++++++++++++++++- .../resource_constructor.py | 2 +- 3 files changed, 257 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 0593aee9f87..d659b86602d 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -30,6 +30,16 @@ ) from .op_math import ( + ResourceCY, + ResourceCH, + ResourceCZ, + ResourceCSWAP, + ResourceCCZ, + ResourceCRX, + ResourceCRY, + ResourceCRZ, + ResourceToffoli, + ResourceMultiControlledX, ResourceCNOT, ResourceControlledPhaseShift, ) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index e8a382938ed..cc7af82235f 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -20,22 +20,150 @@ # pylint: disable=arguments-differ,too-many-ancestors -class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): - """Resource class for ControlledPhaseShift""" +class ResourceCH(qml.CH, re.ResourceConstructor): + r"""Resource class for CH gate. + + Resources: + The resources are derived from the following identities: + + .. math:: + + \begin{align} + \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \dot \hat{Z} \dot \hat{R}_{y}(\frac{-\pi}{4}), \\ + \hat{Z} &= \hat{H} \dot \hat{X} \dot \hat{H} + \end{align} + + + We can control on the Pauli-X gate to obtain our controlled Hadamard gate. + + """ + + # TODO: Reference this: + # https://quantumcomputing.stackexchange.com/questions/15734/how-to-construct-a-controlled-hadamard-gate-using-single-qubit-gates-and-control @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} + ry = re.ResourceRY.resource_rep() + h = re.ResourceHadamard.resource_rep() cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep() + gate_types[h] = 2 + gate_types[ry] = 2 + gate_types[cnot] = 1 + + return gate_types + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCY(qml.CY, re.ResourceConstructor): + """Resource class for CY gate. + + Resources: + The resources are derived from the following identity: + + .. math:: \hat{Y} = \hat{S} \dot \hat{X} \dot \hat{S}^{\dagger}. + + We can control on the Pauli-X gate to obtain our controlled-Y gate. + + """ + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + s = re.ResourceS.resource_rep() + + gate_types[cnot] = 1 + gate_types[s] = 2 # Assuming S^dagg ~ S in cost! + + return gate_types + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCZ(qml.CZ, re.ResourceConstructor): + """Resource class for CZ + + Resources: + The resources are derived from the following identity: + + .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H}. + + We can control on the Pauli-X gate to obtain our controlled-Z gate. + + """ + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + h = re.ResourceHadamard.resource_rep() + + gate_types[cnot] = 1 + gate_types[h] = 2 + + return gate_types + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCSWAP(qml.CSWAP, re.ResourceConstructor): + """Resource class for CSWAP + + Resources: + The resources are taken from the paper "Shallow unitary decompositions + of quantum Fredkin and Toffoli gates for connectivity-aware equivalent circuit averaging" + (figure 1d). + """ # TODO: Reference this https://arxiv.org/pdf/2305.18128 + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + gate_types = {} + + tof = re.ResourceToffoli.resource_rep() + cnot = re.ResourceCNOT.resource_rep() + + gate_types[tof] = 1 gate_types[cnot] = 2 - gate_types[rz] = 3 return gate_types - def resource_params(self): + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCCZ(qml.CCZ, re.ResourceConstructor): + """Resource class for CCZ""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: return {} @classmethod @@ -56,3 +184,116 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + +class ResourceToffoli(qml.Toffoli, re.ResourceConstructor): + """Resource class for Toffoli""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceConstructor): + """Resource class for MultiControlledX""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCRX(qml.CRX, re.ResourceConstructor): + """Resource class for CRX""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCRY(qml.CRY, re.ResourceConstructor): + """Resource class for CRY""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCRZ(qml.CRZ, re.ResourceConstructor): + """Resource class for CRZ""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceCRot(qml.CRot, re.ResourceConstructor): + """Resource class for CRot""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + raise re.ResourcesNotDefined + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + + +class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): + """Resource class for ControlledPhaseShift""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() + + gate_types[cnot] = 2 + gate_types[rz] = 3 + + return gate_types + + def resource_params(self): + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_constructor.py index 681e2697966..dfe8034b43b 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_constructor.py @@ -81,7 +81,7 @@ def set_resources(cls, new_func: Callable) -> None: @abstractmethod def resource_params(self) -> dict: """Returns a dictionary containing the minimal information needed to - compute a comparessed representation""" + compute a compressed representation""" @classmethod @abstractmethod From f628f21863b996c5ad05e524a90c0bb2c2472396 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 11:03:01 -0500 Subject: [PATCH 097/335] update docs --- doc/code/qml_labs_resource_estimation.rst | 7 ++++++ pennylane/labs/__init__.py | 13 +--------- .../labs/resource_estimation/__init__.py | 25 +++++++++++++++++-- 3 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 doc/code/qml_labs_resource_estimation.rst diff --git a/doc/code/qml_labs_resource_estimation.rst b/doc/code/qml_labs_resource_estimation.rst new file mode 100644 index 00000000000..cf4f14d93ab --- /dev/null +++ b/doc/code/qml_labs_resource_estimation.rst @@ -0,0 +1,7 @@ +qml.labs.resource_estimation +================ + +.. currentmodule:: pennylane.labs.resource_estimation + +.. automodule:: pennylane.labs.resource_estimation + \ No newline at end of file diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index d51d8e8c53a..58300046821 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -14,7 +14,7 @@ r""" .. currentmodule:: pennylane -This module module contains experimental features enabling +This module contains experimental features enabling advanced quantum computing research. .. warning:: @@ -22,17 +22,6 @@ This module is experimental. Frequent changes will occur, with no guarantees of stability or backwards compatibility. -.. currentmodule:: pennylane.labs.resource_estimation - -Resource Estimation -~~~~~~~~~~~~~~~~~~~ - -.. autosummary:: - :toctree: api - - ~Resources - ~CompressedResourceOp - """ from .resource_estimation import ( diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index c73465a6bcb..6aafe8ad564 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -11,7 +11,28 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""This module contains experimental resource estimation functionality. """ +r""" +.. currentmodule:: pennylane + +As part of the labs module, this module contains experimental features for +resource estimation. + +.. warning:: + + This module is experimental. Frequent changes will occur, + with no guarantees of stability or backwards compatibility. + +.. currentmodule:: pennylane.labs.resource_estimation + +Resource Estimation +~~~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~Resources + ~CompressedResourceOp + +""" -# from .resource_constructor import ResourceConstructor from .resource_container import CompressedResourceOp, Resources From 4d2f17e8107ab51ba71e59898ce7664eabdb87d8 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 11:24:03 -0500 Subject: [PATCH 098/335] adding class stubs --- .../ops/qubit/parametric_ops_multi_qubit.py | 86 ++++++++++++++++ .../ops/qubit/qchem_ops.py | 98 +++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py create mode 100644 pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py new file mode 100644 index 00000000000..67b65c488d1 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -0,0 +1,86 @@ +import pennylane as qml +import pennylane.labs.resource_estimation.resource_constructor as rc + +class ResourceMultiRZ(qml.MultiRZ, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourcePauliRot(qml.PauliRot, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceIsingXX(qml.IsingXX, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceIsingYY(qml.IsingYY, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceIsingZZ(qml.IsingZZ, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceIsingXY(qml.IsingXY, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class PSWAP(qml.PSWAP, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py new file mode 100644 index 00000000000..7a75fb11554 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -0,0 +1,98 @@ +import pennylane as qml +import pennylane.labs.resource_estimation.resource_constructor as rc + +class ResourceSingleExcitation(qml.SingleExcitation, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceDoubleExcitation(qml.DoubleExcitation, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceOrbitalRotation(qml.OrbitalRotation, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return + +class ResourceFermionicSWAP(qml.FermionicSWAP, rc.ResourceConstructor): + @staticmethod + def _resource_decomp(*args, **kwargs): + return + + def resource_params(self): + return + + @classmethod + def resource_rep(cls, **kwargs): + return From c48b7ff139672b230636109fabc1b875aab1fee4 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 11:35:07 -0500 Subject: [PATCH 099/335] fix docs --- doc/code/qml_labs_resource_estimation.rst | 3 +-- pennylane/labs/__init__.py | 2 -- pennylane/labs/resource_estimation/__init__.py | 7 +++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/doc/code/qml_labs_resource_estimation.rst b/doc/code/qml_labs_resource_estimation.rst index cf4f14d93ab..910b22edb12 100644 --- a/doc/code/qml_labs_resource_estimation.rst +++ b/doc/code/qml_labs_resource_estimation.rst @@ -1,7 +1,6 @@ qml.labs.resource_estimation -================ +============================ .. currentmodule:: pennylane.labs.resource_estimation .. automodule:: pennylane.labs.resource_estimation - \ No newline at end of file diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 58300046821..707c6136b40 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. r""" -.. currentmodule:: pennylane - This module contains experimental features enabling advanced quantum computing research. diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 6aafe8ad564..2418042fbc1 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. r""" -.. currentmodule:: pennylane +.. currentmodule:: pennylane.labs.resource_estimation As part of the labs module, this module contains experimental features for resource estimation. @@ -22,10 +22,9 @@ This module is experimental. Frequent changes will occur, with no guarantees of stability or backwards compatibility. -.. currentmodule:: pennylane.labs.resource_estimation -Resource Estimation -~~~~~~~~~~~~~~~~~~~ +Base Objects +~~~~~~~~~~~~ .. autosummary:: :toctree: api From 6eb680dffde5419d5ce37cd914cbd5f20996c10e Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 11:44:45 -0500 Subject: [PATCH 100/335] fixing docstring --- pennylane/labs/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 2c1bba88831..47cfe9cea82 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -22,7 +22,6 @@ This module is experimental. Frequent changes will occur, with no guarantees of stability or backwards compatibility. -<<<<<<< HEAD .. currentmodule:: pennylane.labs.resource_estimation Resource Estimation @@ -42,8 +41,6 @@ :toctree: api ~ResourcesNotDefined -======= ->>>>>>> f628f21863b996c5ad05e524a90c0bb2c2472396 """ from .resource_estimation import ( From 49d9ee97545eb12d7ca563480654e36e0307aee2 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 11:50:01 -0500 Subject: [PATCH 101/335] add to toctree --- doc/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/index.rst b/doc/index.rst index b554310934a..e2e1706ca62 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -202,6 +202,7 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve code/qml_gradients code/qml_kernels code/qml_labs + code/qml_labs_resource_estimation code/qml_logging code/qml_math code/qml_noise From 8138fb17423326ffbf5c24cd7c1cf9d5418f543d Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 12:14:42 -0500 Subject: [PATCH 102/335] fix docs --- pennylane/labs/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 707c6136b40..58300046821 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. r""" +.. currentmodule:: pennylane + This module contains experimental features enabling advanced quantum computing research. From 3dc3de4daefd388341489f006d878f5ed2c54a5e Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 13:09:24 -0500 Subject: [PATCH 103/335] maybe this? --- pennylane/labs/resource_estimation/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 2418042fbc1..864663d8f2a 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. r""" -.. currentmodule:: pennylane.labs.resource_estimation - As part of the labs module, this module contains experimental features for resource estimation. @@ -22,6 +20,7 @@ This module is experimental. Frequent changes will occur, with no guarantees of stability or backwards compatibility. +.. currentmodule:: pennylane.labs.resource_estimation Base Objects ~~~~~~~~~~~~ From 3402028a1e63c150731c9ed9ae330e767d9058de Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 6 Nov 2024 13:19:58 -0500 Subject: [PATCH 104/335] re-run ci From 74eb5de249ed9b41b5b73f489db5ca4d8606b9b6 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 14:26:12 -0500 Subject: [PATCH 105/335] adding resource constructor to init --- pennylane/labs/resource_estimation/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index c78445a26c1..2d8025d7a1a 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -28,9 +28,18 @@ .. autosummary:: :toctree: api + ~ResourceConstructor ~Resources ~CompressedResourceOp +Exceptions +~~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~ResourcesNotDefined + """ from .resource_constructor import ResourceConstructor, ResourcesNotDefined From 696444ffc8da65d8f612839d4d31b151a19366e1 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 14:35:21 -0500 Subject: [PATCH 106/335] renaming ResourceConstructor to ResourceOperator --- pennylane/labs/__init__.py | 4 ++-- pennylane/labs/resource_estimation/__init__.py | 4 ++-- .../labs/resource_estimation/resource_container.py | 6 +++--- .../{resource_constructor.py => resource_operator.py} | 10 +++++----- .../resource_estimation/test_resource_constructor.py | 10 +++++----- 5 files changed, 17 insertions(+), 17 deletions(-) rename pennylane/labs/resource_estimation/{resource_constructor.py => resource_operator.py} (92%) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 47cfe9cea82..bf93b0a18c4 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -32,7 +32,7 @@ ~Resources ~CompressedResourceOp - ~ResourceConstructor + ~ResourceOperator Errors ~~~~~~ @@ -46,7 +46,7 @@ from .resource_estimation import ( Resources, CompressedResourceOp, - ResourceConstructor, + ResourceOperator, ResourcesNotDefined, ) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 2d8025d7a1a..a7a3187ff1b 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -28,7 +28,7 @@ .. autosummary:: :toctree: api - ~ResourceConstructor + ~ResourceOperator ~Resources ~CompressedResourceOp @@ -42,5 +42,5 @@ """ -from .resource_constructor import ResourceConstructor, ResourcesNotDefined +from .resource_operator import ResourceOperator, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 352fe312fa8..c4522546cc5 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -16,7 +16,7 @@ from collections import defaultdict from dataclasses import dataclass, field -import pennylane.labs.resource_estimation.resource_constructor as rc +import pennylane.labs.resource_estimation.resource_operator as rc class CompressedResourceOp: @@ -76,8 +76,8 @@ def __init__(self, op_type: type, params: dict) -> None: >>> print(op_tp) QSVT(num_wires=5, num_angles=100) """ - if not issubclass(op_type, rc.ResourceConstructor): - raise TypeError("op_type must be of type ResourceConstructor.") + if not issubclass(op_type, rc.ResourceOperator): + raise TypeError(f"op_type must be a subclass of ResourceOperator. Got {op_type}.") self._name = op_type.__name__ self.op_type = op_type diff --git a/pennylane/labs/resource_estimation/resource_constructor.py b/pennylane/labs/resource_estimation/resource_operator.py similarity index 92% rename from pennylane/labs/resource_estimation/resource_constructor.py rename to pennylane/labs/resource_estimation/resource_operator.py index 681e2697966..897d642a121 100644 --- a/pennylane/labs/resource_estimation/resource_constructor.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -18,7 +18,7 @@ import pennylane.labs.resource_estimation.resource_container as rc -class ResourceConstructor(ABC): +class ResourceOperator(ABC): r"""This is an abstract class that defines the methods a PennyLane Operator must implement in order to be used for resource estimation. @@ -32,9 +32,9 @@ class ResourceConstructor(ABC): .. code-block:: python import pennylane as qml - from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceConstructor + from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceOperator - class ResourceQFT(qml.QFT, ResourceConstructor): + class ResourceQFT(qml.QFT, ResourceOperator): @staticmethod def _resource_decomp(num_wires) -> dict: @@ -65,7 +65,7 @@ def resource_rep(self) -> CompressedResourceOp: @abstractmethod def _resource_decomp(*args, **kwargs) -> Dict[rc.CompressedResourceOp, int]: """Returns the Resource object. This method is only to be used inside - the methods of classes inheriting from ResourceConstructor.""" + the methods of classes inheriting from ResourceOperator.""" @classmethod def resources(cls, *args, **kwargs): @@ -96,4 +96,4 @@ def resource_rep_from_op(self) -> rc.CompressedResourceOp: class ResourcesNotDefined(Exception): - """Exception to be raised when a ``ResourceConstructor`` does not implement _resource_decomp""" + """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py index 0a0e3a161e8..c4425549d63 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Test the abstract ResourceConstructor class +Test the abstract ResourceOperator class """ import pytest @@ -24,7 +24,7 @@ def test_abstract_resource_decomp(): """Test that the _resource_decomp method is abstract.""" - class DummyClass(re.ResourceConstructor): + class DummyClass(re.ResourceOperator): """Dummy class for testing""" def resource_params(self): @@ -44,7 +44,7 @@ def resource_rep(): def test_abstract_resource_params(): """Test that the resource_params method is abstract""" - class DummyClass(re.ResourceConstructor): + class DummyClass(re.ResourceOperator): """Dummy class for testing""" @staticmethod @@ -64,7 +64,7 @@ def resource_rep(self): def test_abstract_resource_rep(): """Test that the resource_rep method is abstract""" - class DummyClass(re.ResourceConstructor): + class DummyClass(re.ResourceOperator): """Dummy class for testing""" @staticmethod @@ -84,7 +84,7 @@ def resource_params(self): def test_set_resources(): """Test that the resources method can be overriden""" - class DummyClass(re.ResourceConstructor): + class DummyClass(re.ResourceOperator): """Dummy class for testing""" def resource_params(self): From 7fdb538a4bdf492acafbead1f303ec579599ce8c Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 14:40:33 -0500 Subject: [PATCH 107/335] ResourceConstructor -> ResourceOperator --- .../resource_estimation/ops/op_math/controlled_ops.py | 4 ++-- .../resource_estimation/ops/qubit/non_parametric_ops.py | 6 +++--- .../ops/qubit/parametric_ops_single_qubit.py | 2 +- pennylane/labs/resource_estimation/resource_tracking.py | 8 ++++---- .../labs/resource_estimation/templates/resource_qft.py | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index e8a382938ed..87a729a2c3a 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -20,7 +20,7 @@ # pylint: disable=arguments-differ,too-many-ancestors -class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceConstructor): +class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): """Resource class for ControlledPhaseShift""" @staticmethod @@ -43,7 +43,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) -class ResourceCNOT(qml.CNOT, re.ResourceConstructor): +class ResourceCNOT(qml.CNOT, re.ResourceOperator): """Resource class for CNOT""" @staticmethod diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 3be483aa959..2992f3a34ba 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -20,7 +20,7 @@ # pylint: disable=arguments-differ -class ResourceHadamard(qml.Hadamard, re.ResourceConstructor): +class ResourceHadamard(qml.Hadamard, re.ResourceOperator): """Resource class for Hadamard""" @staticmethod @@ -35,7 +35,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) -class ResourceSWAP(qml.SWAP, re.ResourceConstructor): +class ResourceSWAP(qml.SWAP, re.ResourceOperator): """Resource class for SWAP""" @staticmethod @@ -54,7 +54,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) -class ResourceT(qml.T, re.ResourceConstructor): +class ResourceT(qml.T, re.ResourceOperator): """Resource class for T""" @staticmethod diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 3f3368f296b..69d7d0ddf71 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -32,7 +32,7 @@ def _rotation_resources(epsilon=10e-3): return gate_types -class ResourceRZ(qml.RZ, re.ResourceConstructor): +class ResourceRZ(qml.RZ, re.ResourceOperator): """Resource class for RZ""" @staticmethod diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index abfc99936c1..93b5d5463d0 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -24,7 +24,7 @@ from pennylane.tape import QuantumScript from pennylane.wires import Wires -from .resource_constructor import ResourceConstructor +from .resource_constructor import ResourceOperator from .resource_container import CompressedResourceOp, Resources # pylint: disable=dangerous-default-value,protected-access @@ -90,7 +90,7 @@ def resources_from_operation( ) -> Resources: """Get resources from an operation""" - if isinstance(obj, ResourceConstructor): + if isinstance(obj, ResourceOperator): cp_rep = obj.resource_rep_from_op() gate_counts_dict = defaultdict(int) _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) @@ -195,7 +195,7 @@ def _counts_from_compressed_res_op( return -def _temp_map_func(op: Operation) -> ResourceConstructor: +def _temp_map_func(op: Operation) -> ResourceOperator: """Temp map function""" raise NotImplementedError @@ -230,7 +230,7 @@ def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedR """ cmp_rep_ops = [] for op in ops: - if isinstance(op, ResourceConstructor): + if isinstance(op, ResourceOperator): cmp_rep_ops.append(op.resource_rep_from_op()) else: diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 29118820086..3094042ec03 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -17,7 +17,7 @@ import pennylane as qml from pennylane.labs.resource_estimation import ( CompressedResourceOp, - ResourceConstructor, + ResourceOperator, ResourceControlledPhaseShift, ResourceHadamard, ResourceSWAP, @@ -26,7 +26,7 @@ # pylint: disable=arguments-differ -class ResourceQFT(qml.QFT, ResourceConstructor): +class ResourceQFT(qml.QFT, ResourceOperator): """Resource class for QFT""" @staticmethod From 38e171ba643c259c3fa6b1c3ffeb95fb08144efe Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 14:56:02 -0500 Subject: [PATCH 108/335] ResourceConstructor -> ResourceOperator --- pennylane/labs/resource_estimation/resource_tracking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 0193997cd2d..1d27d84fec1 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -24,7 +24,7 @@ from pennylane.tape import QuantumScript from pennylane.wires import Wires -from .resource_constructor import ResourceConstructor +from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources # pylint: disable=dangerous-default-value,protected-access @@ -194,7 +194,7 @@ def _counts_from_compressed_res_op( return -def _temp_map_func(op: Operation) -> ResourceConstructor: +def _temp_map_func(op: Operation) -> ResourceOperator: """Temp map function""" raise NotImplementedError From ed42a8298cb41e3916b125b73e15d753f31c43ec Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 15:07:20 -0500 Subject: [PATCH 109/335] update --- pennylane/labs/resource_estimation/resource_tracking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 93b5d5463d0..77b113682e8 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -24,7 +24,7 @@ from pennylane.tape import QuantumScript from pennylane.wires import Wires -from .resource_constructor import ResourceOperator +from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources # pylint: disable=dangerous-default-value,protected-access From 9d0fa0b9fb7221f6cba5a76ac5f7891085c22122 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 16:50:04 -0500 Subject: [PATCH 110/335] adding kwargs to QFT decomp --- pennylane/labs/resource_estimation/templates/resource_qft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 3094042ec03..0985814f032 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -30,7 +30,7 @@ class ResourceQFT(qml.QFT, ResourceOperator): """Resource class for QFT""" @staticmethod - def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: + def _resource_decomp(num_wires, **kwargs) -> Dict[CompressedResourceOp, int]: if not isinstance(num_wires, int): raise TypeError("num_wires must be an int.") From b060f09def28885c718ffa234384a0be5566250c Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 6 Nov 2024 17:01:45 -0500 Subject: [PATCH 111/335] constructor -> operator --- pennylane/labs/resource_estimation/resource_tracking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 1d27d84fec1..893004a0c0a 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -90,7 +90,7 @@ def resources_from_operation( ) -> Resources: """Get resources from an operation""" - if isinstance(obj, ResourceConstructor): + if isinstance(obj, ResourceOperator): cp_rep = obj.resource_rep_from_op() gate_counts_dict = defaultdict(int) _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) @@ -229,7 +229,7 @@ def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedR """ cmp_rep_ops = [] for op in ops: - if isinstance(op, ResourceConstructor): + if isinstance(op, ResourceOperator): cmp_rep_ops.append(op.resource_rep_from_op()) else: From d46ed0c43111069b968610ff477c9d059861acbf Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 7 Nov 2024 08:51:41 -0500 Subject: [PATCH 112/335] removing exception --- pennylane/labs/resource_estimation/resource_operator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 897d642a121..32087fdcb14 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -95,5 +95,3 @@ def resource_rep_from_op(self) -> rc.CompressedResourceOp: return self.__class__.resource_rep(**params) -class ResourcesNotDefined(Exception): - """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" From 8eb92ddbfcfe5cfdad460bb46063f301f6c84f3b Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 7 Nov 2024 08:52:18 -0500 Subject: [PATCH 113/335] adding exception --- pennylane/labs/resource_estimation/resource_operator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 32087fdcb14..897d642a121 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -95,3 +95,5 @@ def resource_rep_from_op(self) -> rc.CompressedResourceOp: return self.__class__.resource_rep(**params) +class ResourcesNotDefined(Exception): + """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" From 15d95125e28f5ffd5e97a0cabbb9b56c3c9ca8a3 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 7 Nov 2024 09:31:44 -0500 Subject: [PATCH 114/335] addressing jay's comments --- pennylane/labs/__init__.py | 8 ---- .../labs/resource_estimation/__init__.py | 10 +---- .../resource_estimation/resource_container.py | 4 +- .../resource_estimation/resource_operator.py | 37 +++++++++---------- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index bf93b0a18c4..eee151ac661 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -34,20 +34,12 @@ ~CompressedResourceOp ~ResourceOperator -Errors -~~~~~~ - -.. autosummary:: - :toctree: api - - ~ResourcesNotDefined """ from .resource_estimation import ( Resources, CompressedResourceOp, ResourceOperator, - ResourcesNotDefined, ) __all__ = ["Resources", "CompressedResourceOp"] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index a7a3187ff1b..5a657aeefbf 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -32,15 +32,7 @@ ~Resources ~CompressedResourceOp -Exceptions -~~~~~~~~~~ - -.. autosummary:: - :toctree: api - - ~ResourcesNotDefined - """ -from .resource_operator import ResourceOperator, ResourcesNotDefined +from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index c4522546cc5..8c02ec42704 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -16,7 +16,7 @@ from collections import defaultdict from dataclasses import dataclass, field -import pennylane.labs.resource_estimation.resource_operator as rc +from pennylane.labs.resource_estimation import ResourceOperator class CompressedResourceOp: @@ -76,7 +76,7 @@ def __init__(self, op_type: type, params: dict) -> None: >>> print(op_tp) QSVT(num_wires=5, num_angles=100) """ - if not issubclass(op_type, rc.ResourceOperator): + if not issubclass(op_type, ResourceOperator): raise TypeError(f"op_type must be a subclass of ResourceOperator. Got {op_type}.") self._name = op_type.__name__ diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 32087fdcb14..a85bb48217b 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -12,10 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. r"""Abstract base class for resource operators.""" +from __future__ import annotations + from abc import ABC, abstractmethod -from typing import Callable, Dict +from typing import TYPE_CHECKING, Callable, Dict -import pennylane.labs.resource_estimation.resource_container as rc +if TYPE_CHECKING: + from pennylane.labs.resource_estimation import CompressedResourceOp class ResourceOperator(ABC): @@ -26,7 +29,7 @@ class ResourceOperator(ABC): **Example** - A PennyLane Operator can be extended for resource estimation by creating a new class that inherits from both the Operator and Resource Constructor. + A PennyLane Operator can be extended for resource estimation by creating a new class that inherits from both the Operator and ``ResourceOperator``. Here is an example showing how to extend ``qml.QFT`` for resource estimation. .. code-block:: python @@ -37,18 +40,12 @@ class ResourceOperator(ABC): class ResourceQFT(qml.QFT, ResourceOperator): @staticmethod - def _resource_decomp(num_wires) -> dict: - if not isinstance(num_wires, int): - raise TypeError("num_wires must be an int.") - - if num_wires < 1: - raise ValueError("num_wires must be greater than 0.") - + def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: gate_types = {} - hadamard = CompressedResourceOp(qml.Hadamard, {}) - swap = CompressedResourceOp(qml.SWAP, {}) - ctrl_phase_shift = CompressedResourceOp(qml.ControlledPhaseShift, {}) + hadamard = CompressedResourceOp(ResourceHadamard, {}) + swap = CompressedResourceOp(ResourceSWAP, {}) + ctrl_phase_shift = CompressedResourceOp(ResourceControlledPhaseShift, {}) gate_types[hadamard] = num_wires gate_types[swap] = num_wires // 2 @@ -56,14 +53,18 @@ def _resource_decomp(num_wires) -> dict: return gate_types - def resource_rep(self) -> CompressedResourceOp: - params = {"num_wires": len(self.wires)} - return CompressedResourceOp(qml.QFT, params) + def resource_params(self) -> dict: + return {"num_wires": len(self.wires)} + + @classmethod + def resource_rep(cls, num_wires) -> CompressedResourceOp: + params = {"num_wires": num_wires} + return CompressedResourceOp(cls, params) """ @staticmethod @abstractmethod - def _resource_decomp(*args, **kwargs) -> Dict[rc.CompressedResourceOp, int]: + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns the Resource object. This method is only to be used inside the methods of classes inheriting from ResourceOperator.""" @@ -93,5 +94,3 @@ def resource_rep_from_op(self) -> rc.CompressedResourceOp: """Returns a compressed representation directly from the operator""" params = self.resource_params() return self.__class__.resource_rep(**params) - - From 6ffdeb9eaede1308f99376f00ecab19f06b8b0c9 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 7 Nov 2024 09:35:33 -0500 Subject: [PATCH 115/335] minor fix --- pennylane/labs/resource_estimation/resource_operator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index a85bb48217b..32e262d754e 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -86,11 +86,11 @@ def resource_params(self) -> dict: @classmethod @abstractmethod - def resource_rep(cls, **kwargs) -> rc.CompressedResourceOp: + def resource_rep(cls, **kwargs) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" - def resource_rep_from_op(self) -> rc.CompressedResourceOp: + def resource_rep_from_op(self) -> CompressedResourceOp: """Returns a compressed representation directly from the operator""" params = self.resource_params() return self.__class__.resource_rep(**params) From 7cb76e72a2148e366de6a14aaaa1b5a07cca63d0 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 7 Nov 2024 13:24:39 -0500 Subject: [PATCH 116/335] adding *args to resource_rep --- pennylane/labs/resource_estimation/resource_operator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 32e262d754e..f74bb6a56e8 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -86,7 +86,7 @@ def resource_params(self) -> dict: @classmethod @abstractmethod - def resource_rep(cls, **kwargs) -> CompressedResourceOp: + def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: """Returns a compressed representation containing only the parameters of the Operator that are needed to compute a resource estimation.""" From c948338e25859250c707a0b58d5e03f78a0040be Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 7 Nov 2024 13:53:12 -0500 Subject: [PATCH 117/335] resource for MultiRZ --- .../ops/qubit/parametric_ops_multi_qubit.py | 61 +++++++++++++------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 67b65c488d1..635bbe217ca 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -1,19 +1,32 @@ import pennylane as qml -import pennylane.labs.resource_estimation.resource_constructor as rc +import pennylane.labs.resource_estimation as re + +#pylint: disable=arguments-differ + +class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): + """Resource class for MultiRZ""" -class ResourceMultiRZ(qml.MultiRZ, rc.ResourceConstructor): @staticmethod - def _resource_decomp(*args, **kwargs): - return + def _resource_decomp(num_wires, **kwargs): + cnot = re.CompressedResourceOp(re.ResourceCNOT, {}) + rz = re.CompressedResourceOp(re.ResourceRZ, {}) + + gate_types = {} + gate_types[cnot] = 2*(num_wires-1) + gate_types[rz] = 1 + + return gate_types def resource_params(self): - return + return {"num_wires": len(self.wires)} @classmethod - def resource_rep(cls, **kwargs): - return + def resource_rep(cls, num_wires, **kwargs): + return re.CompressedResourceOp(cls, {"num_wires": num_wires}) + +class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): + """Resource class for PauliRot""" -class ResourcePauliRot(qml.PauliRot, rc.ResourceConstructor): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -22,10 +35,12 @@ def resource_params(self): return @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls, *args, **kwargs): return -class ResourceIsingXX(qml.IsingXX, rc.ResourceConstructor): +class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): + """Resource class for IsingXX""" + @staticmethod def _resource_decomp(*args, **kwargs): return @@ -34,10 +49,12 @@ def resource_params(self): return @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls, *args, **kwargs): return -class ResourceIsingYY(qml.IsingYY, rc.ResourceConstructor): +class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): + """Resource class for IsingYY""" + @staticmethod def _resource_decomp(*args, **kwargs): return @@ -46,10 +63,12 @@ def resource_params(self): return @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls, *args, **kwargs): return -class ResourceIsingZZ(qml.IsingZZ, rc.ResourceConstructor): +class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): + """Resource class for IsingZZ""" + @staticmethod def _resource_decomp(*args, **kwargs): return @@ -58,10 +77,12 @@ def resource_params(self): return @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls, *args, **kwargs): return -class ResourceIsingXY(qml.IsingXY, rc.ResourceConstructor): +class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): + """Resource class for IsingXY""" + @staticmethod def _resource_decomp(*args, **kwargs): return @@ -70,10 +91,12 @@ def resource_params(self): return @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls, *args, **kwargs): return -class PSWAP(qml.PSWAP, rc.ResourceConstructor): +class PSWAP(qml.PSWAP, re.ResourceOperator): + """Resource class for PSWAP""" + @staticmethod def _resource_decomp(*args, **kwargs): return @@ -82,5 +105,5 @@ def resource_params(self): return @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls, *args, **kwargs): return From 740ffa65db98f1d4739ae12b7b1697e642d38c7f Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 7 Nov 2024 18:51:17 -0500 Subject: [PATCH 118/335] Address code review comments --- .../resource_estimation/resource_container.py | 70 +++++++------------ 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 86809b87199..9c492980cb9 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -21,7 +21,7 @@ class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. Args: - op_type (Type): the PennyLane type of the operation + op_type: the class object for of an operation which inherits from '~.ResourceOperator' params (dict): a dictionary containing the minimal pairs of parameter names and values required to compute the resources for the given operator! @@ -31,26 +31,16 @@ class CompressedResourceOp: **Example** - >>> op_tp = CompressedResourceOp(qml.Hadamard, {"num_wires":1}) + >>> op_tp = CompressedResourceOp(ResourceHadamard, {"num_wires":1}) >>> print(op_tp) Hadamard(num_wires=1) - - >>> op_tp = CompressedResourceOp( - qml.QSVT, - { - "num_wires": 5, - "num_angles": 100, - }, - ) - >>> print(op_tp) - QSVT(num_wires=5, num_angles=100) """ - def __init__(self, op_type: type, params: dict) -> None: + def __init__(self, op_type, params: dict) -> None: r"""Instantiate the light weight class corressponding to the operator type and parameters. Args: - op_type (Type): the PennyLane type of the operation + op_type: the class object for an operation which inherits from '~.ResourceOperator' params (dict): a dictionary containing the minimal pairs of parameter names and values required to compute the resources for the given operator! @@ -60,21 +50,11 @@ def __init__(self, op_type: type, params: dict) -> None: **Example** - >>> op_tp = CompressedResourceOp(qml.Hadamard, {"num_wires":1}) + >>> op_tp = CompressedResourceOp(ResourceHadamard, {"num_wires":1}) >>> print(op_tp) Hadamard(num_wires=1) - - >>> op_tp = CompressedResourceOp( - qml.QSVT, - { - "num_wires": 5, - "num_angles": 100, - }, - ) - >>> print(op_tp) - QSVT(num_wires=5, num_angles=100) """ - self._name = op_type.__name__ + self._name = (op_type.__name__).replace("Resource", "") self.op_type = op_type self.params = params self._hashable_params = tuple(params.items()) @@ -129,19 +109,19 @@ def __add__(self, other: "Resources") -> "Resources": """Add two resources objects in series""" return add_in_series(self, other) - def __mul__(self, scaler: int) -> "Resources": + def __mul__(self, scalar: int) -> "Resources": """Scale a resources object in series""" - return mul_in_series(self, scaler) + return mul_in_series(self, scalar) __rmul__ = __mul__ # same implementation - def __iadd__(self, other: "Resources") -> None: + def __iadd__(self, other: "Resources") -> "Resources": """Add two resources objects in series""" - add_in_series(self, other, in_place=True) + return add_in_series(self, other, in_place=True) - def __imull__(self, scaler: int) -> None: + def __imull__(self, scalar: int) -> "Resources": """Scale a resources object in series""" - mul_in_series(self, scaler, in_place=True) + return mul_in_series(self, scalar, in_place=True) def __str__(self): """String representation of the Resources object.""" @@ -209,19 +189,19 @@ def add_in_parallel(first: Resources, other: Resources, in_place=False) -> Resou return Resources(new_wires, new_gates, new_gate_types) -def mul_in_series(first: Resources, scaler: int, in_place=False) -> Resources: - r"""Multiply two resources assuming the circuits are executed in series. +def mul_in_series(first: Resources, scalar: int, in_place=False) -> Resources: + r"""Multiply the resources by a scalar assuming the circuits are executed in series. Args: first (Resources): first resource object to combine - scaler (int): integer value to scale the resources by + scalar (int): integer value to scale the resources by in_place (bool): determines if the first Resources are modified in place (default False) Returns: Resources: combined resources """ - new_gates = scaler * first.num_gates - new_gate_types = _scale_dict(first.gate_types, scaler, in_place=in_place) + new_gates = scalar * first.num_gates + new_gate_types = _scale_dict(first.gate_types, scalar, in_place=in_place) if in_place: first.num_gates = new_gates @@ -230,20 +210,20 @@ def mul_in_series(first: Resources, scaler: int, in_place=False) -> Resources: return Resources(first.num_wires, new_gates, new_gate_types) -def mul_in_parallel(first: Resources, scaler: int, in_place=False) -> Resources: - r"""Multiply two resources assuming the circuits are executed in parallel. +def mul_in_parallel(first: Resources, scalar: int, in_place=False) -> Resources: + r"""Multiply the resources by a scalar assuming the circuits are executed in parallel. Args: first (Resources): first resource object to combine - scaler (int): integer value to scale the resources by + scalar (int): integer value to scale the resources by in_place (bool): determines if the first Resources are modified in place (default False) Returns: Resources: combined resources """ - new_wires = scaler * first.num_wires - new_gates = scaler * first.num_gates - new_gate_types = _scale_dict(first.gate_types, scaler, in_place=in_place) + new_wires = scalar * first.num_wires + new_gates = scalar * first.num_gates + new_gate_types = _scale_dict(first.gate_types, scalar, in_place=in_place) if in_place: first.num_wires = new_wires @@ -263,12 +243,12 @@ def _combine_dict(dict1: defaultdict, dict2: defaultdict, in_place=False): return combined_dict -def _scale_dict(dict1: defaultdict, scaler: int, in_place=False): +def _scale_dict(dict1: defaultdict, scalar: int, in_place=False): r"""Private function which scales the values in a dictionary.""" combined_dict = dict1 if in_place else copy.copy(dict1) for k in combined_dict: - combined_dict[k] *= scaler + combined_dict[k] *= scalar return combined_dict From 9d3cefbc10cb50464357163fabd0cb05bcc8fcb7 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 8 Nov 2024 14:02:55 -0500 Subject: [PATCH 119/335] soran's comments --- .../resource_estimation/resource_operator.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index f74bb6a56e8..9d012716c0a 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -29,8 +29,9 @@ class ResourceOperator(ABC): **Example** - A PennyLane Operator can be extended for resource estimation by creating a new class that inherits from both the Operator and ``ResourceOperator``. - Here is an example showing how to extend ``qml.QFT`` for resource estimation. + A PennyLane Operator can be extended for resource estimation by creating a new class that + inherits from both the Operator and ``ResourceOperator``. Here is an example showing how to + extend ``qml.QFT`` for resource estimation. .. code-block:: python @@ -69,9 +70,9 @@ def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: the methods of classes inheriting from ResourceOperator.""" @classmethod - def resources(cls, *args, **kwargs): - """Returns the Resource object. This method is intended to be user facing - and overridable.""" + def resources(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: + """Returns a dictionary containing the counts of each operator type used to + compute the resources of the operator.""" return cls._resource_decomp(*args, **kwargs) @classmethod @@ -82,7 +83,7 @@ def set_resources(cls, new_func: Callable) -> None: @abstractmethod def resource_params(self) -> dict: """Returns a dictionary containing the minimal information needed to - compute a comparessed representation""" + compute a comparessed representation.""" @classmethod @abstractmethod @@ -92,5 +93,4 @@ def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: def resource_rep_from_op(self) -> CompressedResourceOp: """Returns a compressed representation directly from the operator""" - params = self.resource_params() - return self.__class__.resource_rep(**params) + return self.__class__.resource_rep(**self.resource_params()) From dfba3c2a81ba7542eb67a8521fd9474c7459da39 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 8 Nov 2024 14:15:44 -0500 Subject: [PATCH 120/335] formatting --- pennylane/labs/resource_estimation/resource_operator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 9d012716c0a..a639d3fefb5 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -29,7 +29,7 @@ class ResourceOperator(ABC): **Example** - A PennyLane Operator can be extended for resource estimation by creating a new class that + A PennyLane Operator can be extended for resource estimation by creating a new class that inherits from both the Operator and ``ResourceOperator``. Here is an example showing how to extend ``qml.QFT`` for resource estimation. From a61be636255c264761c36c137e59f81809d84296 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 11 Nov 2024 00:00:59 -0500 Subject: [PATCH 121/335] adding tests --- .../resource_estimation/resource_tracking.py | 6 +++--- .../test_resource_tracking.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 pennylane/labs/tests/resource_estimation/test_resource_tracking.py diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 893004a0c0a..600e6bf9cbc 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -76,7 +76,7 @@ def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_c Resources: The total resources of the quantum circuit. Rasies: - TypeError: "Could not obtain resources for obj of type (type(obj)). + TypeError: "Could not obtain resources for obj of type (type(obj))". """ raise TypeError( @@ -103,7 +103,7 @@ def resources_from_operation( @get_resources.register def resources_from_qfunc( obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: +) -> Callable: """Get resources from a quantum function which queues operations!""" @wraps(obj) @@ -174,7 +174,7 @@ def _counts_from_compressed_res_op( Args: cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from - gate_counts_dict (_type_): base dictionary to modify with the resource counts + gate_counts_dict (Dict): base dictionary to modify with the resource counts gate_set (Set): the set of operations to track resources with respect too scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py new file mode 100644 index 00000000000..b4acc50ba55 --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -0,0 +1,19 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Test the core resource tracking pipeline. +""" +# pylint:disable=protected-access, no-self-use +import copy +from collections import defaultdict From ede84c6a13839d7aabcc5cd6622579bb5b6986c1 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 11 Nov 2024 00:01:16 -0500 Subject: [PATCH 122/335] formatting --- .../resource_estimation/resource_tracking.py | 6 +- .../test_resource_tracking.py | 83 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 600e6bf9cbc..904fafa2417 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -24,8 +24,8 @@ from pennylane.tape import QuantumScript from pennylane.wires import Wires -from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources +from .resource_operator import ResourceOperator # pylint: disable=dangerous-default-value,protected-access @@ -114,10 +114,12 @@ def wrapper(*args, **kwargs): operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) compressed_res_ops_lst = _operations_to_compressed_reps(operations) + initial_gate_set = set.union(gate_set, _StandardGateSet) + gate_counts_dict = defaultdict(int) for cmp_rep_op in compressed_res_ops_lst: _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + cmp_rep_op, gate_counts_dict, gate_set=initial_gate_set, config=config ) # Validation: diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index b4acc50ba55..fe75f951c09 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -17,3 +17,86 @@ # pylint:disable=protected-access, no-self-use import copy from collections import defaultdict + +import pennylane as qml +from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceOperator +from pennylane.operation import Operation + + +class DummyX(qml.X, ResourceOperator): + + def _resource_decomp(*args, **kwargs): + raise NotImplementedError + + def resource_params(self) -> dict: + return dict() + + @classmethod + def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + return CompressedResourceOp(cls, {}) + + +class DummyY(qml.Y, ResourceOperator): + + def _resource_decomp(*args, **kwargs): + raise NotImplementedError + + def resource_params(self) -> dict: + return dict() + + @classmethod + def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + return CompressedResourceOp(cls, {}) + + +class DummyOp(qml.I, ResourceOperator): + + def _resource_decomp(*args, **kwargs): + x = DummyX.resource_rep() + + def resource_params(self) -> dict: + return {"num_wires": len(self.wires)} + + @classmethod + def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + return CompressedResourceOp(cls, {}) + + +class DummyX(qml.X, ResourceOperator): + + def _resource_decomp(*args, **kwargs): + raise NotImplementedError + + def resource_params(self) -> dict: + return dict() + + @classmethod + def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + return CompressedResourceOp(cls, {}) + + +class TestGetResources: + """Test the core resource tracking pipeline""" + + def test_resources_from_operation(): + """Test that we can extract the resources from an Operation.""" + + assert True + + def test_resources_from_qfunc(): + assert True + + def test_resources_from_tape(): + assert True + + def test_counts_from_compressed_res(): + assert True + + def test_clean_gate_counts(): + assert True + + def test_operations_to_compressed_reps(): + assert True + + def test_get_resources(): + assert True From d39f3a160bf9fa97f134241580186d160d5d0dc9 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 11 Nov 2024 00:17:59 -0500 Subject: [PATCH 123/335] remove tracking logic --- .../resource_estimation/resource_tracking.py | 244 ------------------ 1 file changed, 244 deletions(-) delete mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py deleted file mode 100644 index 77b113682e8..00000000000 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -r"""Core resource tracking logic.""" -from collections import defaultdict -from collections.abc import Callable -from functools import singledispatch, wraps -from typing import Dict, Iterable, List, Set - -import pennylane as qml -from pennylane.measurements import MeasurementProcess -from pennylane.operation import Operation -from pennylane.queuing import AnnotatedQueue -from pennylane.tape import QuantumScript -from pennylane.wires import Wires - -from .resource_operator import ResourceOperator -from .resource_container import CompressedResourceOp, Resources - -# pylint: disable=dangerous-default-value,protected-access - -_StandardGateSet = { - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "SWAP", - "CNOT", - "S", - "T", - "Toffoli", - "RX", - "RY", - "RZ", - "PhaseShift", -} - - -DefaultGateSet = { - "Hadamard", - "CNOT", - "S", - "T", - "Toffoli", -} - - -resource_config = { - "error_rx": 10e-3, - "error_ry": 10e-3, - "error_rz": 10e-3, -} - - -@singledispatch -def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: - """Obtain the resources from a quantum circuit or operation in terms of the gates provided - in the gate_set. - - Args: - obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. - gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. - config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. - - Returns: - Resources: The total resources of the quantum circuit. - - Rasies: - TypeError: "Could not obtain resources for obj of type (type(obj)). - """ - - raise TypeError( - f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" - ) - - -@get_resources.register -def resources_from_operation( - obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from an operation""" - - if isinstance(obj, ResourceOperator): - cp_rep = obj.resource_rep_from_op() - gate_counts_dict = defaultdict(int) - _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) - return Resources(gate_types=gate_counts_dict) - - res = Resources() # TODO: Add implementation here! - return res - - -@get_resources.register -def resources_from_qfunc( - obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from a quantum function which queues operations!""" - - @wraps(obj) - def wrapper(*args, **kwargs): - with AnnotatedQueue() as q: - obj(*args, **kwargs) - - operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) - compressed_res_ops_lst = _operations_to_compressed_reps(operations) - - gate_counts_dict = defaultdict(int) - for cmp_rep_op in compressed_res_ops_lst: - _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config - ) - - # Validation: - condensed_gate_counts = defaultdict(int) - for sub_cmp_rep, counts in gate_counts_dict.items(): - _counts_from_compressed_res_op( - sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config - ) - - clean_gate_counts = _clean_gate_counts(condensed_gate_counts) - num_gates = sum(clean_gate_counts.values()) - num_wires = len(Wires.shared_wires(tuple(op.wires for op in operations))) - return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) - - return wrapper - - -@get_resources.register -def resources_from_tape( - obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from a quantum tape""" - num_wires = obj.num_wires - operations = obj.operations - compressed_res_ops_lst = _operations_to_compressed_reps(operations) - - gate_counts_dict = defaultdict(int) - for cmp_rep_op in compressed_res_ops_lst: - _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config - ) - - # Validation: - condensed_gate_counts = defaultdict(int) - for sub_cmp_rep, counts in gate_counts_dict.items(): - _counts_from_compressed_res_op( - sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config - ) - - clean_gate_counts = _clean_gate_counts(condensed_gate_counts) - num_gates = sum(clean_gate_counts.values()) - - return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) - - -def _counts_from_compressed_res_op( - cp_rep: CompressedResourceOp, - gate_counts_dict, - gate_set: Set, - scalar: int = 1, - config: Dict = resource_config, -) -> None: - """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. - - Args: - cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from - gate_counts_dict (_type_): base dictionary to modify with the resource counts - gate_set (Set): the set of operations to track resources with respect too - scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. - config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. - """ - ## If op in gate_set add to resources - if cp_rep._name in gate_set: - gate_counts_dict[cp_rep] += scalar - return - - ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources - resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) - - for sub_cp_rep, counts in resource_decomp.items(): - _counts_from_compressed_res_op( - sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config - ) - - return - - -def _temp_map_func(op: Operation) -> ResourceOperator: - """Temp map function""" - raise NotImplementedError - - -def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: - """Map resources with gate_types made from CompressedResourceOps - into one which tracks just strings of operations! - - Args: - gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops - - Returns: - Dict[str, int]: gate counts in terms of names of operations - """ - clean_gate_counts = defaultdict(int) - - for cmp_res_op, counts in gate_counts.items(): - clean_gate_counts[cmp_res_op._name] += counts - - return clean_gate_counts - - -@qml.QueuingManager.stop_recording() -def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: - """Convert the sequence of operations to a list of compressed resource ops. - - Args: - ops (Iterable[Operation]): set of operations to convert. - - Returns: - List[CompressedResourceOp]: set of converted compressed resource ops. - """ - cmp_rep_ops = [] - for op in ops: - if isinstance(op, ResourceOperator): - cmp_rep_ops.append(op.resource_rep_from_op()) - - else: - try: - cmp_rep_ops.append(_temp_map_func(op).resource_rep_from_op()) - - except NotImplementedError: - decomp = op.decomposition() - cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) - - return cmp_rep_ops From 9050a8ea3e6ecc6dc34d54ad0990525a9d03ff60 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 11 Nov 2024 00:18:16 -0500 Subject: [PATCH 124/335] remove import --- pennylane/labs/resource_estimation/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 34e3d1228da..3883973a7d8 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -36,7 +36,6 @@ from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources -from .resource_tracking import get_resources, DefaultGateSet, _StandardGateSet, resource_config from .ops import ( ResourceCNOT, From a4d9089e1eb40bfbb39a7f7ed1150705a1dfe378 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 11 Nov 2024 01:11:06 -0500 Subject: [PATCH 125/335] Resource constructor class (#6442) Adding the `ResourceConstructor` class [sc-76752] --------- Co-authored-by: Jay Soni --- pennylane/labs/__init__.py | 13 +++ .../labs/resource_estimation/__init__.py | 2 + .../resource_estimation/resource_container.py | 5 + .../resource_estimation/resource_operator.py | 96 ++++++++++++++++ .../test_resource_constructor.py | 103 ++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 pennylane/labs/resource_estimation/resource_operator.py create mode 100644 pennylane/labs/tests/resource_estimation/test_resource_constructor.py diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 58300046821..eee151ac661 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -22,11 +22,24 @@ This module is experimental. Frequent changes will occur, with no guarantees of stability or backwards compatibility. +.. currentmodule:: pennylane.labs.resource_estimation + +Resource Estimation +~~~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~Resources + ~CompressedResourceOp + ~ResourceOperator + """ from .resource_estimation import ( Resources, CompressedResourceOp, + ResourceOperator, ) __all__ = ["Resources", "CompressedResourceOp"] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 864663d8f2a..5a657aeefbf 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -28,9 +28,11 @@ .. autosummary:: :toctree: api + ~ResourceOperator ~Resources ~CompressedResourceOp """ +from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 9c492980cb9..12d932f1786 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -16,6 +16,8 @@ from collections import defaultdict from dataclasses import dataclass, field +from pennylane.labs.resource_estimation import ResourceOperator + class CompressedResourceOp: r"""Instantiate the light weight class corressponding to the operator type and parameters. @@ -54,6 +56,9 @@ def __init__(self, op_type, params: dict) -> None: >>> print(op_tp) Hadamard(num_wires=1) """ + if not issubclass(op_type, ResourceOperator): + raise TypeError(f"op_type must be a subclass of ResourceOperator. Got {op_type}.") + self._name = (op_type.__name__).replace("Resource", "") self.op_type = op_type self.params = params diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py new file mode 100644 index 00000000000..a639d3fefb5 --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -0,0 +1,96 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Abstract base class for resource operators.""" +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Callable, Dict + +if TYPE_CHECKING: + from pennylane.labs.resource_estimation import CompressedResourceOp + + +class ResourceOperator(ABC): + r"""This is an abstract class that defines the methods a PennyLane Operator + must implement in order to be used for resource estimation. + + .. details:: + + **Example** + + A PennyLane Operator can be extended for resource estimation by creating a new class that + inherits from both the Operator and ``ResourceOperator``. Here is an example showing how to + extend ``qml.QFT`` for resource estimation. + + .. code-block:: python + + import pennylane as qml + from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceOperator + + class ResourceQFT(qml.QFT, ResourceOperator): + + @staticmethod + def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: + gate_types = {} + + hadamard = CompressedResourceOp(ResourceHadamard, {}) + swap = CompressedResourceOp(ResourceSWAP, {}) + ctrl_phase_shift = CompressedResourceOp(ResourceControlledPhaseShift, {}) + + gate_types[hadamard] = num_wires + gate_types[swap] = num_wires // 2 + gate_types[ctrl_phase_shift] = num_wires*(num_wires - 1) // 2 + + return gate_types + + def resource_params(self) -> dict: + return {"num_wires": len(self.wires)} + + @classmethod + def resource_rep(cls, num_wires) -> CompressedResourceOp: + params = {"num_wires": num_wires} + return CompressedResourceOp(cls, params) + """ + + @staticmethod + @abstractmethod + def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: + """Returns the Resource object. This method is only to be used inside + the methods of classes inheriting from ResourceOperator.""" + + @classmethod + def resources(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: + """Returns a dictionary containing the counts of each operator type used to + compute the resources of the operator.""" + return cls._resource_decomp(*args, **kwargs) + + @classmethod + def set_resources(cls, new_func: Callable) -> None: + """Set a custom resource method.""" + cls.resources = new_func + + @abstractmethod + def resource_params(self) -> dict: + """Returns a dictionary containing the minimal information needed to + compute a comparessed representation.""" + + @classmethod + @abstractmethod + def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + """Returns a compressed representation containing only the parameters of + the Operator that are needed to compute a resource estimation.""" + + def resource_rep_from_op(self) -> CompressedResourceOp: + """Returns a compressed representation directly from the operator""" + return self.__class__.resource_rep(**self.resource_params()) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py new file mode 100644 index 00000000000..c4425549d63 --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py @@ -0,0 +1,103 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Test the abstract ResourceOperator class +""" +import pytest + +import pennylane.labs.resource_estimation as re + +# pylint: disable=abstract-class-instantiated,arguments-differ + + +def test_abstract_resource_decomp(): + """Test that the _resource_decomp method is abstract.""" + + class DummyClass(re.ResourceOperator): + """Dummy class for testing""" + + def resource_params(self): + return + + @staticmethod + def resource_rep(): + return + + with pytest.raises( + TypeError, + match="Can't instantiate abstract class DummyClass with abstract method _resource_decomp", + ): + DummyClass() + + +def test_abstract_resource_params(): + """Test that the resource_params method is abstract""" + + class DummyClass(re.ResourceOperator): + """Dummy class for testing""" + + @staticmethod + def _resource_decomp(): + return + + def resource_rep(self): + return + + with pytest.raises( + TypeError, + match="Can't instantiate abstract class DummyClass with abstract method resource_params", + ): + DummyClass() + + +def test_abstract_resource_rep(): + """Test that the resource_rep method is abstract""" + + class DummyClass(re.ResourceOperator): + """Dummy class for testing""" + + @staticmethod + def _resource_decomp(): + return + + def resource_params(self): + return + + with pytest.raises( + TypeError, + match="Can't instantiate abstract class DummyClass with abstract method resource_rep", + ): + DummyClass() + + +def test_set_resources(): + """Test that the resources method can be overriden""" + + class DummyClass(re.ResourceOperator): + """Dummy class for testing""" + + def resource_params(self): + return + + @staticmethod + def resource_rep(): + return + + @staticmethod + def _resource_decomp(): + return + + dummy = DummyClass() + DummyClass.set_resources(lambda _: 5) + assert DummyClass.resources(10) == 5 From e6c3a7963e050380e167609fd7c4d69d5125b697 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 12 Nov 2024 11:32:19 -0500 Subject: [PATCH 126/335] Add testing --- .../resource_estimation/resource_tracking.py | 8 +- .../templates/resource_qft.py | 2 +- .../test_resource_tracking.py | 401 ++++++++++++++---- 3 files changed, 333 insertions(+), 78 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 14a4b90d879..5be45741d84 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -92,9 +92,13 @@ def resources_from_operation( if isinstance(obj, ResourceOperator): cp_rep = obj.resource_rep_from_op() + gate_counts_dict = defaultdict(int) _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) - return Resources(gate_types=gate_counts_dict) + gate_types = _clean_gate_counts(gate_counts_dict) + + n_gates = sum(gate_types.values()) + return Resources(num_wires=len(obj.wires), num_gates=n_gates, gate_types=gate_types) res = Resources() # TODO: Add implementation here! return res @@ -131,7 +135,7 @@ def wrapper(*args, **kwargs): clean_gate_counts = _clean_gate_counts(condensed_gate_counts) num_gates = sum(clean_gate_counts.values()) - num_wires = len(Wires.shared_wires(tuple(op.wires for op in operations))) + num_wires = len(Wires.all_wires(tuple(op.wires for op in operations))) return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) return wrapper diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/resource_qft.py index 0985814f032..7681d634685 100644 --- a/pennylane/labs/resource_estimation/templates/resource_qft.py +++ b/pennylane/labs/resource_estimation/templates/resource_qft.py @@ -17,9 +17,9 @@ import pennylane as qml from pennylane.labs.resource_estimation import ( CompressedResourceOp, - ResourceOperator, ResourceControlledPhaseShift, ResourceHadamard, + ResourceOperator, ResourceSWAP, ) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index fe75f951c09..e78a445537a 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -11,92 +11,343 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""" -Test the core resource tracking pipeline. -""" -# pylint:disable=protected-access, no-self-use -import copy +"""Test the core resource tracking pipeline.""" from collections import defaultdict +from copy import copy -import pennylane as qml -from pennylane.labs.resource_estimation import CompressedResourceOp, ResourceOperator -from pennylane.operation import Operation - - -class DummyX(qml.X, ResourceOperator): - - def _resource_decomp(*args, **kwargs): - raise NotImplementedError - - def resource_params(self) -> dict: - return dict() - - @classmethod - def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: - return CompressedResourceOp(cls, {}) - - -class DummyY(qml.Y, ResourceOperator): - - def _resource_decomp(*args, **kwargs): - raise NotImplementedError - - def resource_params(self) -> dict: - return dict() - - @classmethod - def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: - return CompressedResourceOp(cls, {}) - - -class DummyOp(qml.I, ResourceOperator): - - def _resource_decomp(*args, **kwargs): - x = DummyX.resource_rep() - - def resource_params(self) -> dict: - return {"num_wires": len(self.wires)} +# pylint:disable=protected-access, no-self-use +import pytest - @classmethod - def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: - return CompressedResourceOp(cls, {}) +import pennylane as qml +import pennylane.labs.resource_estimation as re +from pennylane.labs.resource_estimation.resource_tracking import ( + DefaultGateSet, + _clean_gate_counts, + _counts_from_compressed_res_op, + _operations_to_compressed_reps, + _StandardGateSet, + get_resources, + resource_config, + resources_from_operation, + resources_from_qfunc, + resources_from_tape, +) -class DummyX(qml.X, ResourceOperator): +class DummyOperation(qml.operation.Operation): - def _resource_decomp(*args, **kwargs): - raise NotImplementedError + def __init__(self, wires=None): + super().__init__(wires=wires) - def resource_params(self) -> dict: - return dict() + def decomposition(self): + decomp = [ + re.ResourceHadamard(wires=self.wires[0]), + re.ResourceHadamard(wires=self.wires[1]), + re.ResourceCNOT(wires=[self.wires[0], self.wires[1]]), + ] - @classmethod - def resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: - return CompressedResourceOp(cls, {}) + return decomp class TestGetResources: """Test the core resource tracking pipeline""" - def test_resources_from_operation(): + compressed_rep_data = ( + ( + [ + re.ResourceHadamard(0), + re.ResourceRX(1.23, 1), + re.ResourceCNOT(wires=[1, 2]), + ], + [ + re.CompressedResourceOp(re.ResourceHadamard, {}), + re.CompressedResourceOp(re.ResourceRX, {}), + re.CompressedResourceOp(re.ResourceCNOT, {}), + ], + ), + ( + [ + re.ResourceQFT(wires=[1, 2, 3]), + re.ResourceIdentity(wires=[1, 2, 3]), + re.ResourceRot(1.23, 0.45, -6, wires=0), + re.ResourceQFT(wires=[1, 2]), + ], + [ + re.CompressedResourceOp(re.ResourceQFT, {"num_wires": 3}), + re.CompressedResourceOp(re.ResourceIdentity, {}), + re.CompressedResourceOp(re.ResourceRot, {}), + re.CompressedResourceOp(re.ResourceQFT, {"num_wires": 2}), + ], + ), + ( + [ + re.ResourceQFT(wires=[0, 1]), + DummyOperation(wires=["a", "b"]), + re.ResourceRY(-0.5, wires=1), + ], + [ + re.CompressedResourceOp(re.ResourceQFT, {"num_wires": 2}), + re.CompressedResourceOp(re.ResourceHadamard, {}), + re.CompressedResourceOp(re.ResourceHadamard, {}), + re.CompressedResourceOp(re.ResourceCNOT, {}), + re.CompressedResourceOp(re.ResourceRY, {}), + ], + ), # Test decomposition logic + ) + + @pytest.mark.parametrize("ops_lst, compressed_reps", compressed_rep_data) + def test_operations_to_compressed_reps(self, ops_lst, compressed_reps): + """Test that we can transform a list of operations into compressed reps""" + computed_compressed_reps = _operations_to_compressed_reps(ops_lst) + for computed_cr, expected_cr in zip(computed_compressed_reps, compressed_reps): + assert computed_cr == expected_cr + + compressed_rep_counts = ( + ( + re.ResourceHadamard(wires=0).resource_rep_from_op(), + defaultdict(int, {re.CompressedResourceOp(re.ResourceHadamard, {}): 1}), + ), + ( + re.ResourceRX(1.23, wires=0).resource_rep_from_op(), + defaultdict(int, {re.CompressedResourceOp(re.ResourceT, {}): 17}), + ), + ( + re.ResourceIdentity(wires=[1, 2, 3]).resource_rep_from_op(), + defaultdict(int, {}), # Identity has no resources + ), + ( + re.ResourceControlledPhaseShift(1.23, wires=[0, 1]).resource_rep_from_op(), + defaultdict( + int, + { + re.CompressedResourceOp(re.ResourceT, {}): 51, + re.CompressedResourceOp(re.ResourceCNOT, {}): 2, + }, + ), + ), + ( + re.ResourceQFT(wires=[1, 2, 3, 4]).resource_rep_from_op(), + defaultdict( + int, + { + re.CompressedResourceOp(re.ResourceT, {}): 306, + re.CompressedResourceOp(re.ResourceCNOT, {}): 18, + re.CompressedResourceOp(re.ResourceHadamard, {}): 4, + }, + ), + ), + ) + + @pytest.mark.parametrize("op_in_gate_set", [True, False]) + @pytest.mark.parametrize("scalar", [1, 2, 5]) + @pytest.mark.parametrize("compressed_rep, expected_counts", compressed_rep_counts) + def test_counts_from_compressed_res( + self, scalar, compressed_rep, expected_counts, op_in_gate_set + ): + """Test that we can obtain counts from a compressed resource op""" + + if op_in_gate_set: + # Test that we add op directly to counts if its in the gate_set + custom_gate_set = {compressed_rep._name} + + base_gate_counts = defaultdict(int) + _counts_from_compressed_res_op( + compressed_rep, + gate_counts_dict=base_gate_counts, + gate_set=custom_gate_set, + scalar=scalar, + ) + + assert base_gate_counts == defaultdict(int, {compressed_rep: scalar}) + + else: + expected_counts = copy(expected_counts) + for resource_op, counts in expected_counts.items(): # scale expected counts + expected_counts[resource_op] = scalar * counts + + base_gate_counts = defaultdict(int) + _counts_from_compressed_res_op( + compressed_rep, + gate_counts_dict=base_gate_counts, + gate_set=DefaultGateSet, + scalar=scalar, + ) + + assert base_gate_counts == expected_counts + + @pytest.mark.parametrize( + "custom_config, num_T_gates", + ( + ( + { + "error_rz": 10e-2, + }, + 13, + ), + ( + { + "error_rz": 10e-3, + }, + 17, + ), + ( + { + "error_rz": 10e-4, + }, + 21, + ), + ), + ) + def test_counts_from_compressed_res_custom_config(self, custom_config, num_T_gates): + """Test that the function works with custom configs and a non-empty gate_counts_dict""" + base_gate_counts = defaultdict( + int, {re.ResourceT.resource_rep(): 3, re.ResourceS.resource_rep(): 5} + ) + + _counts_from_compressed_res_op( + re.ResourceRZ.resource_rep(), + base_gate_counts, + gate_set=DefaultGateSet, + config=custom_config, + ) + expected_counts = defaultdict( + int, {re.ResourceT.resource_rep(): 3 + num_T_gates, re.ResourceS.resource_rep(): 5} + ) + + assert base_gate_counts == expected_counts + + def test_clean_gate_counts(self): + """Test that the function groups operations by name instead of compressed representation.""" + + gate_counts = defaultdict( + int, + { + re.ResourceQFT.resource_rep(5): 1, + re.ResourceHadamard.resource_rep(): 3, + re.ResourceCNOT.resource_rep(): 1, + re.ResourceQFT.resource_rep(3): 4, + }, + ) + + expected_clean_counts = defaultdict(int, {"CNOT": 1, "Hadamard": 3, "QFT": 5}) + + assert _clean_gate_counts(gate_counts) == expected_clean_counts + + @pytest.mark.parametrize( + "op, expected_resources", + ( + (re.ResourceHadamard(wires=0), re.Resources(1, 1, defaultdict(int, {"Hadamard": 1}))), + (re.ResourceRX(1.23, wires=1), re.Resources(1, 17, defaultdict(int, {"T": 17}))), + ( + re.ResourceQFT(wires=range(5)), + re.Resources(5, 541, defaultdict(int, {"Hadamard": 5, "CNOT": 26, "T": 510})), + ), + ), + ) + def test_resources_from_operation(self, op, expected_resources): """Test that we can extract the resources from an Operation.""" - - assert True - - def test_resources_from_qfunc(): - assert True - - def test_resources_from_tape(): - assert True - - def test_counts_from_compressed_res(): - assert True - - def test_clean_gate_counts(): - assert True - - def test_operations_to_compressed_reps(): - assert True - - def test_get_resources(): - assert True + computed_resources = resources_from_operation( + op + ) # add tests that don't use default gate_set and config + assert computed_resources == expected_resources + + @staticmethod + def my_qfunc(): + for w in range(2): + re.ResourceHadamard(w) + + re.ResourceCNOT([0, 1]) + re.ResourceRX(1.23, 0) + re.ResourceRY(-4.56, 1) + + re.ResourceQFT(wires=[0, 1, 2]) + return qml.expval(re.ResourceHadamard(2)) + + def test_resources_from_qfunc(self): + """Test the we can extract the resources from a quantum function.""" + expected_resources_standard = re.Resources( + num_wires=3, + num_gates=24, + gate_types=defaultdict( + int, {"Hadamard": 5, "CNOT": 7, "SWAP": 1, "RX": 1, "RY": 1, "RZ": 9} + ), + ) + + computed_resources = resources_from_qfunc(self.my_qfunc, gate_set=_StandardGateSet)() + assert computed_resources == expected_resources_standard + + expected_resources_custom = re.Resources( + num_wires=3, + num_gates=190, + gate_types=defaultdict(int, {"Hadamard": 5, "CNOT": 10, "T": 175}), + ) + + my_resource_config = copy(resource_config) + my_resource_config["error_rx"] = 10e-1 + my_resource_config["error_ry"] = 10e-2 + computed_resources = resources_from_qfunc( + self.my_qfunc, gate_set=DefaultGateSet, config=my_resource_config + )() + + assert computed_resources == expected_resources_custom + + my_tape = qml.tape.QuantumScript( + ops=[ + re.ResourceHadamard(0), + re.ResourceHadamard(1), + re.ResourceCNOT([0, 1]), + re.ResourceRX(1.23, 0), + re.ResourceRY(-4.56, 1), + re.ResourceQFT(wires=[0, 1, 2]), + ], + measurements=[qml.expval(re.ResourceHadamard(2))], + ) + + def test_resources_from_tape(self): + """Test that we can extract the resources from a quantum tape""" + expected_resources_standard = re.Resources( + num_wires=3, + num_gates=24, + gate_types=defaultdict( + int, {"Hadamard": 5, "CNOT": 7, "SWAP": 1, "RX": 1, "RY": 1, "RZ": 9} + ), + ) + + computed_resources = resources_from_tape(self.my_tape, gate_set=_StandardGateSet) + assert computed_resources == expected_resources_standard + + expected_resources_custom = re.Resources( + num_wires=3, + num_gates=190, + gate_types=defaultdict(int, {"Hadamard": 5, "CNOT": 10, "T": 175}), + ) + + my_resource_config = copy(resource_config) + my_resource_config["error_rx"] = 10e-1 + my_resource_config["error_ry"] = 10e-2 + computed_resources = resources_from_tape( + self.my_tape, gate_set=DefaultGateSet, config=my_resource_config + ) + + assert computed_resources == expected_resources_custom + + def test_get_resources(self): + """Test that we can dispatch between each of the implementations above""" + op = re.ResourceControlledPhaseShift(1.23, wires=[0, 1]) + tape = qml.tape.QuantumScript(ops=[op], measurements=[qml.expval(re.ResourceHadamard(0))]) + + def circuit(): + re.ResourceControlledPhaseShift(1.23, wires=[0, 1]) + return qml.expval(re.ResourceHadamard(0)) + + res_from_op = get_resources(op) + res_from_tape = get_resources(tape) + res_from_circuit = get_resources(circuit)() + + expected_resources = re.Resources( + num_wires=2, num_gates=53, gate_types=defaultdict(int, {"CNOT": 2, "T": 51}) + ) + + assert res_from_op == expected_resources + assert res_from_tape == expected_resources + assert res_from_circuit == expected_resources From d7bf9577e7cd7555eaa9c4ede24bdd2738bf7aa1 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 12 Nov 2024 12:46:24 -0500 Subject: [PATCH 127/335] Fixing docs --- .../labs/resource_estimation/__init__.py | 12 +++- .../resource_estimation/resource_tracking.py | 68 ++++++++++++++++++- .../test_resource_tracking.py | 2 + 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 21c31e986fe..ce63b77b88c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -32,11 +32,21 @@ ~Resources ~CompressedResourceOp +Tracking Resources +~~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~get_resources + ~DefaultGateSet + ~resource_config + """ from .resource_operator import ResourceOperator from .resource_container import CompressedResourceOp, Resources -from .resource_tracking import DefaultGateSet, get_resources +from .resource_tracking import DefaultGateSet, get_resources, resource_config from .ops import ( ResourceCNOT, diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 5be45741d84..aa90d89f4df 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -64,7 +64,7 @@ @singledispatch def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: - """Obtain the resources from a quantum circuit or operation in terms of the gates provided + r"""Obtain the resources from a quantum circuit or operation in terms of the gates provided in the gate_set. Args: @@ -77,6 +77,72 @@ def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_c Rasies: TypeError: "Could not obtain resources for obj of type (type(obj))". + + **Example** + + We can track the resources of a quantum workflow by passing the quantum function defining our workflow directly + into this function. + + .. code-block:: python + + import pennylane.labs.resource_estimation as re + + def my_circuit(): + for w in range(2): + re.ResourceHadamard(w) + + re.ResourceCNOT([0, 1]) + re.ResourceRX(1.23, 0) + re.ResourceRY(-4.56, 1) + + re.ResourceQFT(wires=[0, 1, 2]) + return qml.expval(re.ResourceHadamard(2)) + + Note that we are passing a python function NOT a :class:`~.QNode`. The resources for this workflow are then obtained by: + + >>> res = re.get_resources(my_circuit)() + >>> print(res) + wires: 3 + gates: 202 + gate_types: + {'Hadamard': 5, 'CNOT': 10, 'T': 187} + + .. details:: + :title: Usage Details + + Users can provide custom gatesets to track resources with. Consider :code:`my_circuit()` from above: + + >>> my_gateset = {"Hadamard", "RX", "RY", "QFT", "CNOT"} + >>> print(re.get_resources(my_circuit, gate_set = my_gateset)()) + wires: 3 + gates: 6 + gate_types: + {'Hadamard': 2, 'CNOT': 1, 'RX': 1, 'RY': 1, 'QFT': 1} + + We can also obtain resources for individual operations and quantum tapes in a similar manner: + + >>> op = re.ResourceRX(1.23, 0) + >>> print(re.get_resources(op)) + wires: 1 + gates: 17 + gate_types: + {'T': 17} + + Finally, we can modify the config values listed in the global :code:`labs.resource_estimation.resource_config` + dictionary to have finegrain control of how the resources are computed. + + >>> re.resource_config + {'error_rx': 0.01, 'error_ry': 0.01, 'error_rz': 0.01} + >>> + >>> my_config = copy.copy(re.resource_config) + >>> my_config["error_rx"] = 0.001 + >>> + >>> print(re.get_resources(op, config=my_config)) + wires: 1 + gates: 21 + gate_types: + {'T': 21} + """ raise TypeError( diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index e78a445537a..4ffd8af8ca8 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -35,6 +35,7 @@ class DummyOperation(qml.operation.Operation): + """Dummy class to test _operations_to_compressed_reps function.""" def __init__(self, wires=None): super().__init__(wires=wires) @@ -253,6 +254,7 @@ def test_resources_from_operation(self, op, expected_resources): @staticmethod def my_qfunc(): + """Dummy qfunc used to test resources_from_qfunc function.""" for w in range(2): re.ResourceHadamard(w) From c48a8fd4b33f1e8c81628ea9cab8d22a794df568 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 09:44:34 -0500 Subject: [PATCH 128/335] init file --- .../resource_estimation/ops/qubit/__init__.py | 21 +++++++++++++++++++ .../ops/qubit/parametric_ops_multi_qubit.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index a013cfb7d56..a6766e70cf4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -20,9 +20,30 @@ ResourceT, ) +from .parametric_ops_multi_qubit import ( + ResourceIsingXX, + ResourceIsingXY, + ResourceIsingYY, + ResourceIsingZZ, + ResourceMultiRZ, + ResourcePauliRot, + ResourcePSWAP, +) + from .parametric_ops_single_qubit import ( ResourceRot, ResourceRX, ResourceRY, ResourceRZ, ) + +from .qchem_ops import ( + ResourceDoubleExcitation, + ResourceDoubleExcitationMinus, + ResourceDoubleExcitationPlus, + ResourceFermionicSWAP, + ResourceOrbitalRotation, + ResourceSingleExcitation, + ResourceSingleExcitationMinus, + ResourceSingleExcitationPlus, +) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 635bbe217ca..048ac65789c 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -94,7 +94,7 @@ def resource_params(self): def resource_rep(cls, *args, **kwargs): return -class PSWAP(qml.PSWAP, re.ResourceOperator): +class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): """Resource class for PSWAP""" @staticmethod From ea9512ac41cc97ae20f4f94506da88974fbbffe6 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 10:40:26 -0500 Subject: [PATCH 129/335] PauliRot --- .../labs/resource_estimation/__init__.py | 1 + .../labs/resource_estimation/ops/__init__.py | 1 + .../ops/qubit/parametric_ops_multi_qubit.py | 36 ++++++++++++++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 7b3cad72064..263c1474d66 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -44,6 +44,7 @@ ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, + ResourceMultiRZ, ResourceRot, ResourceRX, ResourceRZ, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 0593aee9f87..fe93f8e9560 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -20,6 +20,7 @@ from .qubit import ( ResourceHadamard, + ResourceMultiRZ, ResourceRot, ResourceRX, ResourceRY, diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 048ac65789c..a7146e8a272 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -28,15 +28,41 @@ class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): """Resource class for PauliRot""" @staticmethod - def _resource_decomp(*args, **kwargs): - return + def _resource_decomp(active_wires, pauli_word, **kwargs): + if set(pauli_word) == {"I"}: + gp = re.ResourceGlobalPhase.resource_rep(**kwargs) + return {gp: 1} + + h = re.ResourceHadamard.resource_rep(**kwargs) + rx = re.ResourceRX.resource_rep(**kwargs) + multi_rz = re.ResourceMultiRZ.resource_rep(active_wires, **kwargs) + + h_count = 0 + rx_count = 0 + + for gate in pauli_word: + if gate == "X": + h_count += 2 + if gate == "Y": + rx_count += 2 + + gate_types = {} + gate_types[h] = h_count + gate_types[rx] = rx_count + gate_types[multi_rz] = 1 + + return gate_types def resource_params(self): - return + pauli_word = self.hyperparameters["pauli_word"] + return { + "active_wires": len(pauli_word.replace("I", "")), + "pauli_word": pauli_word, + } @classmethod - def resource_rep(cls, *args, **kwargs): - return + def resource_rep(cls, active_wires, pauli_word, **kwargs): + return re.CompressedResourceOp(cls, {"active_wires": active_wires, "pauli_word": pauli_word}) class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): """Resource class for IsingXX""" From c5d98e4b0eeff8b26ed11c9ec91b8c57802dc416 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 11:48:09 -0500 Subject: [PATCH 130/335] IsingXX --- .../ops/qubit/parametric_ops_multi_qubit.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index a7146e8a272..12f1004709d 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -69,14 +69,19 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - return + cnot = re.ResourceCNOT.resource_rep(**kwargs) + rx = re.ResourceRX.resource_rep(**kwargs) + + gate_types = {} + gate_types[cnot] = 2 + gate_types[rx] = 1 def resource_params(self): - return + return {} @classmethod def resource_rep(cls, *args, **kwargs): - return + return re.CompressedResourceOp(cls, {}) class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): """Resource class for IsingYY""" From 52a31bebf0bc27eadf3f63458c5d9a7e2ea42bf5 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 13:26:58 -0500 Subject: [PATCH 131/335] resource methods for multi qubit ops --- .../labs/resource_estimation/ops/__init__.py | 1 + .../resource_estimation/ops/qubit/__init__.py | 1 + .../ops/qubit/parametric_ops_multi_qubit.py | 150 +++++++++++++++--- 3 files changed, 129 insertions(+), 23 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index bb5f44912ae..d55f57f1d1a 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -21,6 +21,7 @@ from .qubit import ( ResourceHadamard, ResourceMultiRZ, + ResourcePhaseShift, ResourceRot, ResourceRX, ResourceRY, diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index a6766e70cf4..44e86917f5b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -31,6 +31,7 @@ ) from .parametric_ops_single_qubit import ( + ResourcePhaseShift, ResourceRot, ResourceRX, ResourceRY, diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 12f1004709d..f29773f47a0 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -4,12 +4,18 @@ #pylint: disable=arguments-differ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): - """Resource class for MultiRZ""" + r"""Resource class for MultiRZ + + Resources: + .. math:: + + MultiRZ(\theta) = \exp\left(-i \frac{\theta}{2} Z^{\otimes n}\right) + """ @staticmethod def _resource_decomp(num_wires, **kwargs): - cnot = re.CompressedResourceOp(re.ResourceCNOT, {}) - rz = re.CompressedResourceOp(re.ResourceRZ, {}) + cnot = re.CompressedResourceOp(re.ops.ResourceCNOT, {}) + rz = re.CompressedResourceOp(re.ops.ResourceRZ, {}) gate_types = {} gate_types[cnot] = 2*(num_wires-1) @@ -25,7 +31,13 @@ def resource_rep(cls, num_wires, **kwargs): return re.CompressedResourceOp(cls, {"num_wires": num_wires}) class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): - """Resource class for PauliRot""" + r"""Resource class for PauliRot + + Resources: + .. math:: + + RP(\theta, P) = \exp\left(-i \frac{\theta}{2} P\right) + """ @staticmethod def _resource_decomp(active_wires, pauli_word, **kwargs): @@ -65,7 +77,20 @@ def resource_rep(cls, active_wires, pauli_word, **kwargs): return re.CompressedResourceOp(cls, {"active_wires": active_wires, "pauli_word": pauli_word}) class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): - """Resource class for IsingXX""" + r"""Resource class for IsingXX + + Resources: + Ising XX coupling gate + + .. math:: XX(\phi) = \exp\left(-i \frac{\phi}{2} (X \otimes X)\right) = + \begin{bmatrix} = + \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\ + 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ + 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ + -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) + \end{bmatrix}. + """ + @staticmethod def _resource_decomp(*args, **kwargs): @@ -84,57 +109,136 @@ def resource_rep(cls, *args, **kwargs): return re.CompressedResourceOp(cls, {}) class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): - """Resource class for IsingYY""" + r"""Resource class for IsingYY + + Resources: + Ising YY coupling gate + + .. math:: \mathtt{YY}(\phi) = \exp\left(-i \frac{\phi}{2} (Y \otimes Y)\right) = + \begin{bmatrix} + \cos(\phi / 2) & 0 & 0 & i \sin(\phi / 2) \\ + 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ + 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ + i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): - return + cy = re.ops.ResourceCY.resource_rep(**kwargs) + ry = re.ops.ResourceRY.resource_rep(**kwargs) + + gate_types = {} + gate_types[cy] = 2 + gate_types[ry] = 1 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, *args, **kwargs): - return + return re.CompressedResourceOp(cls, {}) -class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): - """Resource class for IsingZZ""" +class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): + r"""Resource class for IsingXY + + Resources: + Ising (XX + YY) coupling gate + + .. math:: \mathtt{XY}(\phi) = \exp\left(i \frac{\theta}{4} (X \otimes X + Y \otimes Y)\right) = + \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & \cos(\phi / 2) & i \sin(\phi / 2) & 0 \\ + 0 & i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ + 0 & 0 & 0 & 1 + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): - return + h = re.ops.ResourceHadamard.resource_rep(**kwargs) + cy = re.ops.ResourceCY.resource_rep(**kwargs) + ry = re.ops.ResourceRY.resource_rep(**kwargs) + rx = re.ops.ResourceRX.resource_rep(**kwargs) + + gate_types = {} + gate_types[h] = 2 + gate_types[cy] = 2 + gate_types[ry] = 1 + gate_types[rx] = 1 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, *args, **kwargs): - return + return re.CompressedResourceOp(cls, {}) -class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): - """Resource class for IsingXY""" +class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): + r"""Resource class for IsingZZ + + Resources: + Ising ZZ coupling gate + + .. math:: ZZ(\phi) = \exp\left(-i \frac{\phi}{2} (Z \otimes Z)\right) = + \begin{bmatrix} + e^{-i \phi / 2} & 0 & 0 & 0 \\ + 0 & e^{i \phi / 2} & 0 & 0 \\ + 0 & 0 & e^{i \phi / 2} & 0 \\ + 0 & 0 & 0 & e^{-i \phi / 2} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): - return + cnot = re.ops.ResourceCNOT.resource_rep(**kwargs) + rz = re.ops.ResourceRZ.resource_rep(**kwargs) + + gate_types = {} + gate_types[cnot] = 2 + gate_types[rz] = 1 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, *args, **kwargs): - return + return re.CompressedResourceOp(cls, {}) class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): - """Resource class for PSWAP""" + r"""Resource class for PSWAP + + Resources: + .. math:: PSWAP(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 0 & e^{i \phi} & 0 \\ + 0 & e^{i \phi} & 0 & 0 \\ + 0 & 0 & 0 & 1 + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): - return + swap = re.ops.ResourceSWAP.resource_rep(**kwargs) + cnot = re.ops.ResourceCNOT.resource_rep(**kwargs) + phase = re.ops.ResourcePhaseShift.resource_rep(**kwargs) + + gate_types = {} + gate_types[swap] = 1 + gate_types[cnot] = 2 + gate_types[phase] = 1 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, *args, **kwargs): - return + return re.CompressedResourceOp(cls, {}) From c661275e4e8aa0ac1ebdce130487ff55ab34fe9f Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 13:53:26 -0500 Subject: [PATCH 132/335] rename file --- .../{test_resource_constructor.py => test_resource_operator.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pennylane/labs/tests/resource_estimation/{test_resource_constructor.py => test_resource_operator.py} (100%) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_operator.py similarity index 100% rename from pennylane/labs/tests/resource_estimation/test_resource_constructor.py rename to pennylane/labs/tests/resource_estimation/test_resource_operator.py From 9be5e9f480698b60edb55249b272ad727baba613 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 14:00:49 -0500 Subject: [PATCH 133/335] adding exception to init file --- pennylane/labs/resource_estimation/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 3883973a7d8..49b968fa2fb 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -34,7 +34,7 @@ """ -from .resource_operator import ResourceOperator +from .resource_operator import ResourceOperator, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources from .ops import ( From 936882dd8a36087ee60fb1e1beeef6891605a0a1 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 14:57:52 -0500 Subject: [PATCH 134/335] Apply suggestions from code review Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- pennylane/labs/resource_estimation/resource_container.py | 8 ++++---- pennylane/labs/resource_estimation/resource_operator.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 12d932f1786..b912791b5b9 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -20,12 +20,12 @@ class CompressedResourceOp: - r"""Instantiate the light weight class corressponding to the operator type and parameters. + r"""Instantiate the light weight class corresponding to the operator type and parameters. Args: - op_type: the class object for of an operation which inherits from '~.ResourceOperator' + op_type: the class object of an operation which inherits from '~.ResourceOperator' params (dict): a dictionary containing the minimal pairs of parameter names and values - required to compute the resources for the given operator! + required to compute the resources for the given operator .. details:: @@ -39,7 +39,7 @@ class CompressedResourceOp: """ def __init__(self, op_type, params: dict) -> None: - r"""Instantiate the light weight class corressponding to the operator type and parameters. + r"""Instantiate the light weight class corresponding to the operator type and parameters. Args: op_type: the class object for an operation which inherits from '~.ResourceOperator' diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index a639d3fefb5..4d5ffee8003 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -30,7 +30,7 @@ class ResourceOperator(ABC): **Example** A PennyLane Operator can be extended for resource estimation by creating a new class that - inherits from both the Operator and ``ResourceOperator``. Here is an example showing how to + inherits from both the ``Operator`` and ``ResourceOperator``. Here is an example showing how to extend ``qml.QFT`` for resource estimation. .. code-block:: python From 291b9abee38760e406b7bb4ce63ffb7bc92dc827 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 15:06:53 -0500 Subject: [PATCH 135/335] rename file --- .../templates/{resource_qft.py => subroutines.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pennylane/labs/resource_estimation/templates/{resource_qft.py => subroutines.py} (100%) diff --git a/pennylane/labs/resource_estimation/templates/resource_qft.py b/pennylane/labs/resource_estimation/templates/subroutines.py similarity index 100% rename from pennylane/labs/resource_estimation/templates/resource_qft.py rename to pennylane/labs/resource_estimation/templates/subroutines.py From 78ff9e26b22ab45c61d542518a9eb82bc35ea648 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 13 Nov 2024 16:30:39 -0500 Subject: [PATCH 136/335] fix init file --- pennylane/labs/resource_estimation/templates/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/templates/__init__.py b/pennylane/labs/resource_estimation/templates/__init__.py index e5426bb269c..93370d1f971 100644 --- a/pennylane/labs/resource_estimation/templates/__init__.py +++ b/pennylane/labs/resource_estimation/templates/__init__.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .resource_qft import ResourceQFT +from .subroutines import ResourceQFT From c9897029ecaf282685a7cf474dfd11895be01a6c Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:27:51 -0500 Subject: [PATCH 137/335] Fixed docs and updated tests --- .../resource_estimation/resource_container.py | 4 +- .../test_resource_container.py | 53 ++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index b912791b5b9..1b80b8caf87 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -23,7 +23,7 @@ class CompressedResourceOp: r"""Instantiate the light weight class corresponding to the operator type and parameters. Args: - op_type: the class object of an operation which inherits from '~.ResourceOperator' + op_type (Type): the class object of an operation which inherits from '~.ResourceOperator' params (dict): a dictionary containing the minimal pairs of parameter names and values required to compute the resources for the given operator @@ -42,7 +42,7 @@ def __init__(self, op_type, params: dict) -> None: r"""Instantiate the light weight class corresponding to the operator type and parameters. Args: - op_type: the class object for an operation which inherits from '~.ResourceOperator' + op_type (Type): the class object for an operation which inherits from '~.ResourceOperator' params (dict): a dictionary containing the minimal pairs of parameter names and values required to compute the resources for the given operator! diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index d2c7a6e5710..f35c3249a03 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -31,28 +31,45 @@ mul_in_parallel, mul_in_series, ) +from pennylane.labs.resource_estimation.resource_operator import ResourceOperator + + +class ResourceDummyX(ResourceOperator): + pass + + +class ResourceDummyQFT(ResourceOperator): + pass + + +class ResourceDummyQSVT(ResourceOperator): + pass + + +class ResourceDummyTrotterProduct(ResourceOperator): + pass class TestCompressedResourceOp: """Testing the methods and attributes of the CompressedResourceOp class""" - hamiltonian_arg = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) + test_hamiltonian = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) compressed_op_args_lst = ( - ("PauliX", qml.X, {"num_wires": 1}), - ("QFT", qml.QFT, {"num_wires": 5}), - ("QSVT", qml.QSVT, {"num_wires": 3, "num_angles": 5}), + ("DummyX", ResourceDummyX, {"num_wires": 1}), + ("DummyQFT", ResourceDummyQFT, {"num_wires": 5}), + ("DummyQSVT", ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}), ( - "TrotterProduct", - qml.TrotterProduct, - {"Hamiltonian": hamiltonian_arg, "num_steps": 5, "order": 2}, + "DummyTrotterProduct", + ResourceDummyTrotterProduct, + {"Hamiltonian": test_hamiltonian, "num_steps": 5, "order": 2}, ), ) compressed_op_reprs = ( - "PauliX(num_wires=1)", - "QFT(num_wires=5)", - "QSVT(num_wires=3, num_angles=5)", - "TrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", + "DummyX(num_wires=1)", + "DummyQFT(num_wires=5)", + "DummyQSVT(num_wires=3, num_angles=5)", + "DummyTrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", ) @pytest.mark.parametrize("name, op_type, parameters", compressed_op_args_lst) @@ -67,9 +84,9 @@ def test_init(self, name, op_type, parameters): def test_hash(self): """Test that the hash method behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) - CmprssedQSVT2 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) - Other = CompressedResourceOp(qml.QFT, {"num_wires": 3}) + CmprssedQSVT1 = CompressedResourceOp(ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}) + CmprssedQSVT2 = CompressedResourceOp(ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}) + Other = CompressedResourceOp(ResourceDummyQFT, {"num_wires": 3}) assert hash(CmprssedQSVT1) == hash(CmprssedQSVT1) # compare same object assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) # compare identical instance @@ -77,10 +94,10 @@ def test_hash(self): def test_equality(self): """Test that the equality methods behaves as expected""" - CmprssedQSVT1 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) - CmprssedQSVT2 = CompressedResourceOp(qml.QSVT, {"num_wires": 3, "num_angles": 5}) - CmprssedQSVT3 = CompressedResourceOp(qml.QSVT, {"num_angles": 5, "num_wires": 3}) - Other = CompressedResourceOp(qml.QFT, {"num_wires": 3}) + CmprssedQSVT1 = CompressedResourceOp(ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}) + CmprssedQSVT2 = CompressedResourceOp(ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}) + CmprssedQSVT3 = CompressedResourceOp(ResourceDummyQSVT, {"num_angles": 5, "num_wires": 3}) + Other = CompressedResourceOp(ResourceDummyQFT, {"num_wires": 3}) assert CmprssedQSVT1 == CmprssedQSVT2 # compare identical instance assert CmprssedQSVT1 == CmprssedQSVT3 # compare swapped parameters From 0fcab13d0d216c9438c3dead2417245a59de51ed Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:30:44 -0500 Subject: [PATCH 138/335] modify names --- .../tests/resource_estimation/test_resource_container.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index f35c3249a03..bf2eb77052f 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -54,7 +54,7 @@ class TestCompressedResourceOp: """Testing the methods and attributes of the CompressedResourceOp class""" test_hamiltonian = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) - compressed_op_args_lst = ( + compressed_ops_and_params_lst = ( ("DummyX", ResourceDummyX, {"num_wires": 1}), ("DummyQFT", ResourceDummyQFT, {"num_wires": 5}), ("DummyQSVT", ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}), @@ -72,7 +72,7 @@ class TestCompressedResourceOp: "DummyTrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", ) - @pytest.mark.parametrize("name, op_type, parameters", compressed_op_args_lst) + @pytest.mark.parametrize("name, op_type, parameters", compressed_ops_and_params_lst) def test_init(self, name, op_type, parameters): """Test that we can correctly instantiate CompressedResourceOp""" cr_op = CompressedResourceOp(op_type, parameters) @@ -103,7 +103,7 @@ def test_equality(self): assert CmprssedQSVT1 == CmprssedQSVT3 # compare swapped parameters assert CmprssedQSVT1 != Other - @pytest.mark.parametrize("args, repr", zip(compressed_op_args_lst, compressed_op_reprs)) + @pytest.mark.parametrize("args, repr", zip(compressed_ops_and_params_lst, compressed_op_reprs)) def test_repr(self, args, repr): """Test that the repr method behaves as expected.""" _, op_type, parameters = args From 30c6e22198749b124213e9719af212e0f6529fb2 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:35:06 -0500 Subject: [PATCH 139/335] Added changelog entry --- doc/releases/changelog-dev.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 97527df5f2a..80bf2981896 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -50,6 +50,11 @@ * Expand `ExecutionConfig.gradient_method` to store `TransformDispatcher` type. [(#6455)](https://github.com/PennyLaneAI/pennylane/pull/6455) +

Labs 🧪

+ +* Added base class `Resources`, `CompressedResourceOp`, `ResourceOperator` for advanced resource estimation. + [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428) +

Breaking changes 💔

* The `qml.shadows.shadow_expval` transform has been removed. Instead, please use the @@ -78,4 +83,5 @@ Astral Cai, Yushao Chen, Pietropaolo Frisoni, Andrija Paurevic, -Justin Pickering +Justin Pickering, +Jay Soni, \ No newline at end of file From cae6175995795a96977c4711ae010a054d1ca9d2 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:45:00 -0500 Subject: [PATCH 140/335] fixing codefactor --- pennylane/labs/.pylintrc | 2 +- .../labs/tests/resource_estimation/test_resource_container.py | 4 ++++ .../labs/tests/resource_estimation/test_resource_operator.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/.pylintrc b/pennylane/labs/.pylintrc index ed06299952c..f08eb3ea645 100644 --- a/pennylane/labs/.pylintrc +++ b/pennylane/labs/.pylintrc @@ -30,7 +30,7 @@ ignored-classes=numpy,scipy,autograd,toml,appdir,autograd.numpy,autograd.numpy.l # it should appear only once). # Cyclical import checks are disabled for now as they are frequently used in # the code base, but this can be removed in the future once cycles are resolved. -disable=line-too-long,invalid-name,too-many-lines,redefined-builtin,too-many-locals,duplicate-code,cyclic-import,import-error,bad-option-value +disable=line-too-long,invalid-name,too-many-lines,redefined-builtin,too-many-locals,duplicate-code,cyclic-import,import-error,bad-option-value,too-few-public-methods, [MISCELLANEOUS] diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index bf2eb77052f..6a1a851318b 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -35,18 +35,22 @@ class ResourceDummyX(ResourceOperator): + """Dummy testing class representing X gate""" pass class ResourceDummyQFT(ResourceOperator): + """Dummy testing class representing QFT gate""" pass class ResourceDummyQSVT(ResourceOperator): + """Dummy testing class representing QSVT gate""" pass class ResourceDummyTrotterProduct(ResourceOperator): + """Dummy testing class representing TrotterProduct gate""" pass diff --git a/pennylane/labs/tests/resource_estimation/test_resource_operator.py b/pennylane/labs/tests/resource_estimation/test_resource_operator.py index c4425549d63..8f0ec6044d0 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_operator.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_operator.py @@ -18,7 +18,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=abstract-class-instantiated,arguments-differ +# pylint: disable=abstract-class-instantiated,arguments-differ,missing-function-docstring def test_abstract_resource_decomp(): From 4c0ebe6637965f5e71f9bab73c1bc0acd6ecaa7e Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:46:37 -0500 Subject: [PATCH 141/335] Update pennylane/labs/.pylintrc --- pennylane/labs/.pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/.pylintrc b/pennylane/labs/.pylintrc index f08eb3ea645..51a87506549 100644 --- a/pennylane/labs/.pylintrc +++ b/pennylane/labs/.pylintrc @@ -30,7 +30,7 @@ ignored-classes=numpy,scipy,autograd,toml,appdir,autograd.numpy,autograd.numpy.l # it should appear only once). # Cyclical import checks are disabled for now as they are frequently used in # the code base, but this can be removed in the future once cycles are resolved. -disable=line-too-long,invalid-name,too-many-lines,redefined-builtin,too-many-locals,duplicate-code,cyclic-import,import-error,bad-option-value,too-few-public-methods, +disable=line-too-long,invalid-name,too-many-lines,redefined-builtin,too-many-locals,duplicate-code,cyclic-import,import-error,bad-option-value,too-few-public-methods [MISCELLANEOUS] From 843d2478b3986094bf00bf8663941afb3ad20d09 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:48:04 -0500 Subject: [PATCH 142/335] added pylint disable and format --- .../labs/tests/resource_estimation/test_resource_container.py | 4 ++++ .../labs/tests/resource_estimation/test_resource_operator.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index 6a1a851318b..97a9b0476db 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -36,21 +36,25 @@ class ResourceDummyX(ResourceOperator): """Dummy testing class representing X gate""" + pass class ResourceDummyQFT(ResourceOperator): """Dummy testing class representing QFT gate""" + pass class ResourceDummyQSVT(ResourceOperator): """Dummy testing class representing QSVT gate""" + pass class ResourceDummyTrotterProduct(ResourceOperator): """Dummy testing class representing TrotterProduct gate""" + pass diff --git a/pennylane/labs/tests/resource_estimation/test_resource_operator.py b/pennylane/labs/tests/resource_estimation/test_resource_operator.py index 8f0ec6044d0..feb3471acdb 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_operator.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_operator.py @@ -18,7 +18,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=abstract-class-instantiated,arguments-differ,missing-function-docstring +# pylint: disable=abstract-class-instantiated,arguments-differ,missing-function-docstring,too-few-public-methods def test_abstract_resource_decomp(): From 1459da0372af74e3bf9102cc77c52381c1294ed8 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 17:49:45 -0500 Subject: [PATCH 143/335] format --- .../labs/tests/resource_estimation/test_resource_container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index 97a9b0476db..b535d548afa 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -14,7 +14,7 @@ """ Test base Resource class and its associated methods """ -# pylint:disable=protected-access, no-self-use +# pylint:disable=protected-access, no-self-use, too-few-public-methods import copy from collections import defaultdict From 2a14d00197b42962c7a14957f116bab752ae08be Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 23:35:29 -0500 Subject: [PATCH 144/335] format --- .../tests/resource_estimation/test_resource_container.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index b535d548afa..328f208145e 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -37,26 +37,19 @@ class ResourceDummyX(ResourceOperator): """Dummy testing class representing X gate""" - pass class ResourceDummyQFT(ResourceOperator): """Dummy testing class representing QFT gate""" - pass - class ResourceDummyQSVT(ResourceOperator): """Dummy testing class representing QSVT gate""" - pass - class ResourceDummyTrotterProduct(ResourceOperator): """Dummy testing class representing TrotterProduct gate""" - pass - class TestCompressedResourceOp: """Testing the methods and attributes of the CompressedResourceOp class""" From ff03b78abe47d89ca47328ca7839985f8764bba2 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 23:42:04 -0500 Subject: [PATCH 145/335] format --- .../tests/resource_estimation/test_resource_container.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index b535d548afa..55831d75d00 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -37,26 +37,17 @@ class ResourceDummyX(ResourceOperator): """Dummy testing class representing X gate""" - pass - - class ResourceDummyQFT(ResourceOperator): """Dummy testing class representing QFT gate""" - pass - class ResourceDummyQSVT(ResourceOperator): """Dummy testing class representing QSVT gate""" - pass - class ResourceDummyTrotterProduct(ResourceOperator): """Dummy testing class representing TrotterProduct gate""" - pass - class TestCompressedResourceOp: """Testing the methods and attributes of the CompressedResourceOp class""" From 9dcbbad5bbec1454e3c79c533ed11cae3c29d6c1 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 23:43:21 -0500 Subject: [PATCH 146/335] lint --- .../labs/tests/resource_estimation/test_resource_container.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index 55831d75d00..616b095e768 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -37,6 +37,7 @@ class ResourceDummyX(ResourceOperator): """Dummy testing class representing X gate""" + class ResourceDummyQFT(ResourceOperator): """Dummy testing class representing QFT gate""" From 00c53a75804855241dc6385e68e84609d3dfe46e Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 14 Nov 2024 10:39:03 -0500 Subject: [PATCH 147/335] first draft --- .../labs/resource_estimation/__init__.py | 5 +- .../labs/resource_estimation/ops/__init__.py | 3 + .../ops/op_math/__init__.py | 1 + .../ops/op_math/symbolic.py | 41 +++ .../resource_estimation/resource_operator.py | 31 ++ .../resource_estimation/resource_tracking.py | 271 ++++++++++++++++++ 6 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 pennylane/labs/resource_estimation/ops/op_math/symbolic.py create mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 49b968fa2fb..c528bdacb9f 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -34,12 +34,15 @@ """ -from .resource_operator import ResourceOperator, ResourcesNotDefined +from .resource_operator import ResourceOperator, ResourcesNotDefined, ResourceSymbolicOperator from .resource_container import CompressedResourceOp, Resources from .ops import ( + ResourceAdjoint, ResourceCNOT, + ResourceControlled, ResourceControlledPhaseShift, + ResourcePow, ResourceHadamard, ResourceRZ, ResourceSWAP, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 8e1ecf7f583..430839bf303 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -21,6 +21,9 @@ ) from .op_math import ( + ResourceAdjoint, ResourceCNOT, + ResourceControlled, ResourceControlledPhaseShift, + ResourcePow, ) diff --git a/pennylane/labs/resource_estimation/ops/op_math/__init__.py b/pennylane/labs/resource_estimation/ops/op_math/__init__.py index b65c404b698..0859d77c070 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/__init__.py +++ b/pennylane/labs/resource_estimation/ops/op_math/__init__.py @@ -14,3 +14,4 @@ r"""This module contains experimental resource estimation functionality. """ from .controlled_ops import * +from .symbolic import * diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py new file mode 100644 index 00000000000..8de1ce9c311 --- /dev/null +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -0,0 +1,41 @@ +import pennylane as qml +import pennylane.labs.resource_estimation as re + +class ResourceAdjoint(qml.ops.Adjoint, re.ResourceSymbolicOperator): + """Resource class for Adjoint""" + + @staticmethod + def _resource_decomp(*args, **kwargs): + pass + + @classmethod + def resource_rep(cls, *args, **kwargs): + pass + +class ResourceControlled(qml.ops.Controlled, re.ResourceOperator): + """Resource class for Controlled""" + + @staticmethod + def _resource_decomp(*args, **kwargs): + pass + + def resource_params(self): + pass + + @classmethod + def resource_rep(cls, *args, **kwargs): + pass + +class ResourcePow(qml.ops.Pow, re.ResourceOperator): + """Resource class for Pow""" + + @staticmethod + def _resource_decomp(*args, **kwargs): + pass + + def resource_params(self): + pass + + @classmethod + def resource_rep(cls, *args, **kwargs): + pass diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 71da9cf83e0..dc416235e80 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -95,5 +95,36 @@ def resource_rep_from_op(self) -> CompressedResourceOp: """Returns a compressed representation directly from the operator""" return self.__class__.resource_rep(**self.resource_params()) + @classmethod + def adjoint_resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + """Returns a compressed representation of the adjoint of the operator""" + raise CompressedRepNotDefined + + @classmethod + def controlled_resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + """Returns a compressed representation of the controlled version of the operator""" + raise CompressedRepNotDefined + + @classmethod + def pow_resource_rep(cls, exponent, *args, **kwargs) -> CompressedResourceOp: + """Returns a compressed representation of the operator raised to a power""" + raise CompressedRepNotDefined + +class ResourceSymbolicOperator(ResourceOperator): + def __init__(self, base=None, id=None): + if not isinstance(base, ResourceOperator): + raise TypeError(f"base must be a subclass of ResourceOperator, got type {type(base)}.") + + super().__init__(base=base, id=id) + + def resource_params(self): + return { + "base_class": type(self.base), + "base_params": self.base.resource_params() + } + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" + +class CompressedRepNotDefined(Exception): + """Exception to be raised when a compressed representation is not defined""" diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py new file mode 100644 index 00000000000..fcabaa0f440 --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -0,0 +1,271 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Core resource tracking logic.""" +from collections import defaultdict +from collections.abc import Callable +from functools import singledispatch, wraps +from typing import Dict, Iterable, List, Set + +import pennylane as qml +from pennylane.measurements import MeasurementProcess +from pennylane.operation import DecompositionUndefinedError, Operation +from pennylane.queuing import AnnotatedQueue +from pennylane.tape import QuantumScript +from pennylane.wires import Wires + +from .resource_container import CompressedResourceOp, Resources +from .resource_operator import ResourceOperator, ResourceOperatorNotImplemented + +# pylint: disable=dangerous-default-value,protected-access + +_StandardGateSet = { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "SWAP", + "CNOT", + "S", + "T", + "Toffoli", + "RX", + "RY", + "RZ", + "PhaseShift", +} + + +DefaultGateSet = { + "Hadamard", + "CNOT", + "S", + "T", + "Toffoli", +} + + +resource_config = { + "error_rx": 10e-3, + "error_ry": 10e-3, + "error_rz": 10e-3, +} + + +@singledispatch +def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: + """Obtain the resources from a quantum circuit or operation in terms of the gates provided + in the gate_set. + + Args: + obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. + gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. + config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. + + Returns: + Resources: The total resources of the quantum circuit. + + Rasies: + TypeError: "Could not obtain resources for obj of type (type(obj)). + """ + + raise TypeError( + f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" + ) + + +@get_resources.register +def resources_from_operation( + obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from an operation""" + + if not isinstance(obj, ResourceOperator): + obj = _op_to_resource_op(obj) + + cp_rep = obj.resource_rep_from_op() + gate_counts_dict = defaultdict(int) + _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) + + num_wires = len(obj.wires) + num_gates = sum(gate_counts_dict.values()) + + return Resources(gate_types=gate_counts_dict, num_gates=num_gates, num_wires=num_wires) + + +@get_resources.register +def resources_from_qfunc( + obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum function which queues operations!""" + + @wraps(obj) + def wrapper(*args, **kwargs): + with AnnotatedQueue() as q: + obj(*args, **kwargs) + + operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + num_wires = len(Wires.shared_wires(tuple(op.wires for op in operations))) + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + return wrapper + + +@get_resources.register +def resources_from_tape( + obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum tape""" + num_wires = obj.num_wires + operations = obj.operations + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + +def _counts_from_compressed_res_op( + cp_rep: CompressedResourceOp, + gate_counts_dict, + gate_set: Set, + scalar: int = 1, + config: Dict = resource_config, +) -> None: + """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. + + Args: + cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from + gate_counts_dict (_type_): base dictionary to modify with the resource counts + gate_set (Set): the set of operations to track resources with respect too + scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. + config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. + """ + ## If op in gate_set add to resources + if cp_rep._name in gate_set: + gate_counts_dict[cp_rep] += scalar + return + + ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources + resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) + + for sub_cp_rep, counts in resource_decomp.items(): + _counts_from_compressed_res_op( + sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config + ) + + return + + +@qml.QueuingManager.stop_recording() +def _op_to_resource_op(op: Operation) -> ResourceOperator: + """Map a PL Operator to its corresponding Resource Operator + + Args: + op Operation: A PennyLane operator + + Returns: + ResourceOperator: A ResourceOperator instantiated with op's parameters and hyperparameters. + + """ + import pennylane.labs.resource_estimation as re # pylint: disable=import-outside-toplevel + + name = "Resource" + op._name + + try: + cls = vars(re)[name] + except KeyError as exc: + raise ResourceOperatorNotImplemented( + f"No resource operator for PennyLane operator {op._name} has been implemented." + ) from exc + + return cls._unflatten(*op._flatten()) + + +def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: + """Map resources with gate_types made from CompressedResourceOps + into one which tracks just strings of operations! + + Args: + gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops + + Returns: + Dict[str, int]: gate counts in terms of names of operations + """ + clean_gate_counts = defaultdict(int) + + for cmp_res_op, counts in gate_counts.items(): + clean_gate_counts[cmp_res_op._name] += counts + + return clean_gate_counts + + +@qml.QueuingManager.stop_recording() +def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: + """Convert the sequence of operations to a list of compressed resource ops. + + Args: + ops (Iterable[Operation]): set of operations to convert. + + Returns: + List[CompressedResourceOp]: set of converted compressed resource ops. + """ + cmp_rep_ops = [] + for op in ops: + if isinstance(op, ResourceOperator): + cmp_rep_ops.append(op.resource_rep_from_op()) + + else: + try: + cmp_rep_ops.append(_op_to_resource_op(op).resource_rep_from_op()) + + except ResourceOperatorNotImplemented as exc: + try: + decomp = op.decomposition() + cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) + except DecompositionUndefinedError: + raise ResourceOperatorNotImplemented(f"No resource operator defined for {op._name}, but {op._name} has no decomposition.") from exc + + + return cmp_rep_ops From c7958963528c7f3f89847fa8a1de114267d702ac Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 14 Nov 2024 11:54:51 -0500 Subject: [PATCH 148/335] Code review comments + changelog --- doc/releases/changelog-dev.md | 4 ++++ .../labs/resource_estimation/resource_operator.py | 1 + .../labs/resource_estimation/resource_tracking.py | 11 ++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index ac75b84327f..e0686fcd670 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -62,6 +62,10 @@ * Added base class `Resources`, `CompressedResourceOp`, `ResourceOperator` for advanced resource estimation. [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428) +* Added `get_resources()` functionality which allows users to extract resources from a quantum function, tape or + resource operation. Additionally added some standard gatesets `DefaultGateSet` to track resources with respect to. + [(#6500)](https://github.com/PennyLaneAI/pennylane/pull/6500) +

Breaking changes 💔

* Top level access to `Device`, `QubitDevice`, and `QutritDevice` have been removed. Instead, they diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 71da9cf83e0..1e3bfa1acf3 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -95,5 +95,6 @@ def resource_rep_from_op(self) -> CompressedResourceOp: """Returns a compressed representation directly from the operator""" return self.__class__.resource_rep(**self.resource_params()) + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index aa90d89f4df..b6905f79796 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -15,10 +15,9 @@ from collections import defaultdict from collections.abc import Callable from functools import singledispatch, wraps -from typing import Dict, Iterable, List, Set +from typing import Dict, Iterable, List, Set, Union import pennylane as qml -from pennylane.measurements import MeasurementProcess from pennylane.operation import Operation from pennylane.queuing import AnnotatedQueue from pennylane.tape import QuantumScript @@ -63,7 +62,9 @@ @singledispatch -def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: +def get_resources( + obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Union[Resources, Callable]: r"""Obtain the resources from a quantum circuit or operation in terms of the gates provided in the gate_set. @@ -181,7 +182,7 @@ def wrapper(*args, **kwargs): with AnnotatedQueue() as q: obj(*args, **kwargs) - operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) + operations = tuple(op for op in q.queue if isinstance(op, Operation)) compressed_res_ops_lst = _operations_to_compressed_reps(operations) initial_gate_set = set.union(gate_set, _StandardGateSet) @@ -247,7 +248,7 @@ def _counts_from_compressed_res_op( Args: cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from gate_counts_dict (Dict): base dictionary to modify with the resource counts - gate_set (Set): the set of operations to track resources with respect too + gate_set (Set): the set of operations to track resources with respect to scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. """ From 5573f6d36127afb99cc6a99f4b90138c1495b126 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 14 Nov 2024 13:12:29 -0500 Subject: [PATCH 149/335] some review changes --- .../ops/qubit/non_parametric_ops.py | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 2ef80b4bfdf..0a5f69f4abf 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -39,9 +39,9 @@ class ResourceS(qml.S, re.ResourceOperator): """Resource class for S""" @staticmethod - def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - t = ResourceT.resource_rep() + t = ResourceT.resource_rep(**kwargs) gate_types[t] = 2 return gate_types @@ -86,3 +86,64 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + +class ResourceX(qml.X, re.ResourceOperator): + """Resource class for X""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + s = re.ResourceS.resource_rep(**kwargs) + h = re.ResourceHadamard.resource_rep(**kwargs) + + gate_types = {} + gate_types[s] = 2 + gate_types[h] = 2 + + return gate_types + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + +class ResourceY(qml.Y, re.ResourceOperator): + """Resource class for Y""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + s = re.ResourceS.resource_rep(**kwargs) + h = re.ResourceHadamard.resource_rep(**kwargs) + + gate_types = {} + gate_types[s] = 6 + gate_types[h] = 2 + + return gate_types + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) + +class ResourceZ(qml.Z, re.ResourceOperator): + """Resource class for Z""" + + @staticmethod + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + s = re.ResourceS.resource_rep(**kwargs) + + gate_types = {} + gate_types[s] = 2 + + return gate_types + + def resource_params(self) -> dict: + return {} + + @classmethod + def resource_rep(cls) -> re.CompressedResourceOp: + return re.CompressedResourceOp(cls, {}) From 6743f027c3846985f157ea893aef7c6d3c39e668 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 13 Nov 2024 14:59:22 -0500 Subject: [PATCH 150/335] adding more resources --- .../ops/op_math/controlled_ops.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 168a7144750..a7d7cfec7b2 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -220,13 +220,28 @@ class ResourceToffoli(qml.Toffoli, re.ResourceOperator): """Resource class for Toffoli Resources: - The resources are obtained from the paper `Novel constructions for the fault-tolerant - Toffoli gate `_. We summarize the cost as: + The resources are obtained from (in figure 1.) the paper `Novel constructions for the fault-tolerant + Toffoli gate `_. """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - raise re.ResourcesNotDefined + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + t = re.ResourceT.resource_rep() + h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep() + cz = re.ResourceCZ.resource_rep() + + + gate_types[cnot] = 9 + gate_types[h] = 3 + gate_types[s] = 1 + gate_types[cz] = 1 + gate_types[t] = 4 + + return gate_types @staticmethod def textbook_resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: From 1754eb3e8e7d98a1f978ada950d7c90b9849c0e1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 14 Nov 2024 13:40:58 -0500 Subject: [PATCH 151/335] more review changes --- .../ops/qubit/non_parametric_ops.py | 3 +++ .../ops/qubit/parametric_ops_single_qubit.py | 11 ++++++++++- .../labs/resource_estimation/resource_operator.py | 1 + .../labs/resource_estimation/templates/subroutines.py | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 0a5f69f4abf..9fc87012092 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -87,6 +87,7 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + class ResourceX(qml.X, re.ResourceOperator): """Resource class for X""" @@ -108,6 +109,7 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + class ResourceY(qml.Y, re.ResourceOperator): """Resource class for Y""" @@ -129,6 +131,7 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + class ResourceZ(qml.Z, re.ResourceOperator): """Resource class for Z""" diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index e4ded60e1c8..142ec597a37 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -33,7 +33,16 @@ def _rotation_resources(epsilon=10e-3): class ResourcePhaseShift(qml.PhaseShift, re.ResourceOperator): - """Resource class for PhaseShift""" + r"""Resource class for PhaseShift + + Resources: + The resources are defined from the following identity: + + .. math:: R_\phi(\phi) = e^{i\phi/2}R_z(\phi) = \begin{bmatrix} + 1 & 0 \\ + 0 & e^{i\phi} + \end{bmatrix}. + """ @staticmethod def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 71da9cf83e0..1e3bfa1acf3 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -95,5 +95,6 @@ def resource_rep_from_op(self) -> CompressedResourceOp: """Returns a compressed representation directly from the operator""" return self.__class__.resource_rep(**self.resource_params()) + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 0985814f032..7681d634685 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -17,9 +17,9 @@ import pennylane as qml from pennylane.labs.resource_estimation import ( CompressedResourceOp, - ResourceOperator, ResourceControlledPhaseShift, ResourceHadamard, + ResourceOperator, ResourceSWAP, ) From 5ef66c6086a4788cecbe9dd00026d1268201c1ee Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 14 Nov 2024 13:43:56 -0500 Subject: [PATCH 152/335] docstring --- .../labs/resource_estimation/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 49b968fa2fb..f8b3dca5f0c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -32,6 +32,25 @@ ~Resources ~CompressedResourceOp +Operators +~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~ResourceCNOT + ~ResourceControlledPhaseShift + ~ResourceHadamard + ~ResourceRZ + ~ResourceT + +Templates +~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~ResourceQFT """ from .resource_operator import ResourceOperator, ResourcesNotDefined From f44253140a45cc5a12590ce8b211f922dd06aef4 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 14 Nov 2024 14:19:47 -0500 Subject: [PATCH 153/335] review comments --- .../labs/resource_estimation/__init__.py | 9 +++- .../ops/qubit/test_non_parametric_ops.py | 24 ++++++++--- .../qubit/test_parametric_ops_single_qubit.py | 38 +++++++++++++---- .../resource_estimation/ops/test_identity.py | 41 ++++++++++++++++++- 4 files changed, 97 insertions(+), 15 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 4d4deac32e6..fc0619faa2d 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -40,8 +40,15 @@ ~ResourceCNOT ~ResourceControlledPhaseShift + ~ResourceGlobalPhase ~ResourceHadamard + ~ResourceIdentity + ~ResourceRot + ~ResourceRX + ~ResourceRY ~ResourceRZ + ~ResourceS + ~ResourceSWAP ~ResourceT Templates @@ -64,8 +71,8 @@ ResourceIdentity, ResourceRot, ResourceRX, - ResourceRZ, ResourceRY, + ResourceRZ, ResourceS, ResourceSWAP, ResourceT, diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 880cf3c9e17..b59c519d71d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -66,10 +66,12 @@ def test_resources_from_rep(self): """Test that the resources can be computed from the compressed representation""" op = re.ResourceSWAP([0, 1]) - cnot = re.ResourceCNOT.resource_rep() - expected = {cnot: 3} + expected = {re.ResourceCNOT.resource_rep(): 3} - assert op.resources(**re.ResourceSWAP.resource_rep().params) == expected + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected class TestS: @@ -81,16 +83,26 @@ def test_resources(self): expected = {re.CompressedResourceOp(re.ResourceT, {}): 2} assert op.resources() == expected + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceS(0) + assert op.resource_params() == {} + def test_resource_rep(self): """Test that the compressed representation is correct""" expected = re.CompressedResourceOp(re.ResourceS, {}) assert re.ResourceS.resource_rep() == expected def test_resources_from_rep(self): - """Test that the compressed representation yields the correct resources""" + """Test that the resources can be computed from the compressed representation""" + op = re.ResourceS(0) - expected = {re.CompressedResourceOp(re.ResourceT, {}): 2} - assert op.resources(**re.ResourceS.resource_rep().params) == expected + expected = {re.ResourceT.resource_rep(): 2} + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected class TestT: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 100195bbcc3..9f621ce5f4e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -22,7 +22,7 @@ _rotation_resources, ) -# pylint: disable=no-self-use +# pylint: disable=no-self-use,use-implicit-booleaness-not-comparison @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) @@ -39,9 +39,11 @@ def test_rotation_resources(epsilon): class TestPauliRotation: """Test ResourceRX, ResourceRY, and ResourceRZ""" - params = list(zip([re.ResourceRX, re.ResourceRY, re.ResourceRZ], [10e-3, 10e-4, 10e-5])) + params_classes = [re.ResourceRX, re.ResourceRY, re.ResourceRZ] + params_errors = [10e-3, 10e-4, 10e-5] - @pytest.mark.parametrize("resource_class, epsilon", params) + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) def test_resources(self, resource_class, epsilon): """Test the resources method""" @@ -50,14 +52,16 @@ def test_resources(self, resource_class, epsilon): op = resource_class(1.24, wires=0) assert op.resources(config) == _rotation_resources(epsilon=epsilon) - @pytest.mark.parametrize("resource_class, epsilon", params) + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) def test_resource_rep(self, resource_class, epsilon): # pylint: disable=unused-argument """Test the compact representation""" op = resource_class(1.24, wires=0) expected = re.CompressedResourceOp(resource_class, {}) assert op.resource_rep() == expected - @pytest.mark.parametrize("resource_class, epsilon", params) + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) def test_resources_from_rep(self, resource_class, epsilon): """Test the resources can be obtained from the compact representation""" @@ -65,7 +69,18 @@ def test_resources_from_rep(self, resource_class, epsilon): config = {label: epsilon} op = resource_class(1.24, wires=0) expected = _rotation_resources(epsilon=epsilon) - assert resource_class.resources(config, **op.resource_rep().params) == expected + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params, config=config) == expected + + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + def test_resource_params(self, resource_class, epsilon): # pylint: disable=unused-argument + """Test that the resource params are correct""" + op = resource_class(1.24, wires=0) + assert op.resource_params() == {} class TestRot: @@ -94,4 +109,13 @@ def test_resources_from_rep(self): ry = re.CompressedResourceOp(re.ResourceRY, {}) rz = re.CompressedResourceOp(re.ResourceRZ, {}) expected = {rx: 1, ry: 1, rz: 1} - assert re.ResourceRot.resources(**op.resource_rep().params) == expected + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) + assert op.resource_params() == {} diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py index 4f4c0630320..c1cf7d04528 100644 --- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -32,7 +32,46 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(re.ResourceIdentity, {}) assert re.ResourceIdentity.resource_rep() == expected + def test_resource_params(self): + """Test the resource params are correct""" + op = re.ResourceIdentity(0) + assert op.resource_params() == {} + def test_resources_from_rep(self): """Test that the resources can be computed from the compressed representation""" op = re.ResourceIdentity() - assert op.resources(**re.ResourceIdentity.resource_rep().params) == {} + expected = {} + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + +class TestGlobalPhase: + """Test ResourceGlobalPhase""" + + def test_resources(self): + """ResourceGlobalPhase should have empty resources""" + op = re.ResourceGlobalPhase(0.1, wires=0) + assert op.resources() == {} + + def test_resource_rep(self): + """Test the compressed representation""" + expected = re.CompressedResourceOp(re.ResourceGlobalPhase, {}) + assert re.ResourceGlobalPhase.resource_rep() == expected + + def test_resource_params(self): + """Test the resource params are correct""" + op = re.ResourceGlobalPhase(0.1, wires=0) + assert op.resource_params() == {} + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation""" + op = re.ResourceGlobalPhase(0.1, wires=0) + expected = {} + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected From 6b5319d4a0c5e0d20481dcc82ff09bfed0f5f4e6 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 14 Nov 2024 15:26:57 -0500 Subject: [PATCH 154/335] review comments --- .../labs/resource_estimation/ops/__init__.py | 2 +- .../ops/qubit/non_parametric_ops.py | 38 ++++++++++++++++++- .../ops/qubit/parametric_ops_single_qubit.py | 1 + .../resource_estimation/templates/__init__.py | 2 +- .../templates/subroutines.py | 2 +- 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 8e1ecf7f583..509a0a0230a 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""This module contains experimental resource estimation functionality. """ +r"""This module contains resource operators for PennyLane Operators""" from .qubit import ( ResourceHadamard, diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 2992f3a34ba..795e1305255 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -36,7 +36,43 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceSWAP(qml.SWAP, re.ResourceOperator): - """Resource class for SWAP""" + r"""Resource class for the SWAP gate. + + Resources: + The resources come from the following identity expressing SWAP as the product of three CNOT gates: + + .. math:: + + SWAP = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 0 & 1 & 0\\ + 0 & 1 & 0 & 0\\ + 0 & 0 & 0 & 1 + \end{bmatrix}. + = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 1 & 0 & 0\\ + 0 & 0 & 0 & 1\\ + 0 & 0 & 1 & 0 + \end{bmatrix} + + \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 1\\ + 0 & 0 & 1 & 0\\ + 0 & 1 & 0 & 0 + \end{bmatrix} + + \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 1 & 0 & 0\\ + 0 & 0 & 0 & 1\\ + 0 & 0 & 1 & 0 + \end{bmatrix}. + + 0: ─╭●─╭X─╭●─┤ + 1: ─╰X─╰●─╰X─┤ + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 69d7d0ddf71..5305a7b9805 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -23,6 +23,7 @@ def _rotation_resources(epsilon=10e-3): + """An estimate on the number of T gates needed to implement a Pauli rotation. The estimate is taken from https://arxiv.org/abs/1404.5320.""" gate_types = {} num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) diff --git a/pennylane/labs/resource_estimation/templates/__init__.py b/pennylane/labs/resource_estimation/templates/__init__.py index 93370d1f971..328ab7a10bf 100644 --- a/pennylane/labs/resource_estimation/templates/__init__.py +++ b/pennylane/labs/resource_estimation/templates/__init__.py @@ -11,5 +11,5 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""This module contains experimental resource estimation functionality. """ +r"""This module contains resource operators for PennyLane templates. """ from .subroutines import ResourceQFT diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 0985814f032..1eab218ddd8 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""Resource operator for the QFT template.""" +r"""Resource operators for PennyLane subroutine templates.""" from typing import Dict import pennylane as qml From 98f268d2d46ad183e4fc2103107a23cc2715128f Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 11:22:18 -0500 Subject: [PATCH 155/335] some review changes --- doc/releases/changelog-dev.md | 5 ++++- .../ops/qubit/parametric_ops_single_qubit.py | 7 ++++++- .../ops/op_math/test_controlled_ops.py | 7 ++++++- .../qubit/test_parametric_ops_single_qubit.py | 7 ++++++- .../test_resource_operator.py | 20 +++++++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index ac75b84327f..3486d24efae 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -62,6 +62,9 @@ * Added base class `Resources`, `CompressedResourceOp`, `ResourceOperator` for advanced resource estimation. [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428) +* Added `ResourceOperator` classes for QFT and all operators in QFT's decomposition. + [(#6447)](https://github.com/PennyLaneAI/pennylane/pull/6447) +

Breaking changes 💔

* Top level access to `Device`, `QubitDevice`, and `QutritDevice` have been removed. Instead, they @@ -137,4 +140,4 @@ Austin Huang, Christina Lee, Andrija Paurevic, Justin Pickering, -Jay Soni, \ No newline at end of file +Jay Soni, diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 5305a7b9805..8f6c95ba577 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -34,7 +34,12 @@ def _rotation_resources(epsilon=10e-3): class ResourceRZ(qml.RZ, re.ResourceOperator): - """Resource class for RZ""" + r"""Resource class for RZ + + Resources: + The resources are estimated by approximating the gate with a series of T gates. + The estimate is taken from https://arxiv.org/abs/1404.5320. + """ @staticmethod def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 2397173f814..d8495f45d71 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -18,7 +18,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=no-self-use +# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison class TestControlledPhaseShift: @@ -97,3 +97,8 @@ def test_resource_rep(self): op = re.ResourceCNOT([0, 1]) expected = re.CompressedResourceOp(re.ResourceCNOT, {}) assert op.resource_rep() == expected + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceCNOT([0, 1]) + assert op.resource_params() == {} diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index df666ea965f..7c9f7ebd07a 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -22,7 +22,7 @@ _rotation_resources, ) -# pylint: disable=no-self-use +# pylint: disable=no-self-use, use-implicit-booleaness-not-comparison @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) @@ -53,6 +53,11 @@ def test_resource_rep(self): assert op.resource_rep() == expected + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceRZ(1.24, wires=0) + assert op.resource_params() == {} + @pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) def test_resources_from_rep(self, epsilon): """Test the resources can be obtained from the compact representation""" diff --git a/pennylane/labs/tests/resource_estimation/test_resource_operator.py b/pennylane/labs/tests/resource_estimation/test_resource_operator.py index feb3471acdb..18ffa0c98d9 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_operator.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_operator.py @@ -101,3 +101,23 @@ def _resource_decomp(): dummy = DummyClass() DummyClass.set_resources(lambda _: 5) assert DummyClass.resources(10) == 5 + +def test_resource_rep_from_op(): + """Test that the resource_rep_from_op method is the composition of resource_params and resource_rep""" + + class DummyClass(re.ResourceQFT, re.ResourceOperator): + """Dummy class for testing""" + + @staticmethod + def _resource_decomp(): + return + + def resource_params(self): + return {"foo": 1, "bar": 2} + + @classmethod + def resource_rep(cls, foo, bar): + return re.CompressedResourceOp(cls, {"foo": foo, "bar": bar}) + + op = DummyClass() + assert op.resource_rep_from_op() == op.__class__.resource_rep(**op.resource_params()) From 96b4fa757ee3b292c5d2f673733d66a7884ea16c Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 11:22:30 -0500 Subject: [PATCH 156/335] formatting --- pennylane/labs/resource_estimation/resource_operator.py | 1 + pennylane/labs/resource_estimation/templates/subroutines.py | 2 +- .../labs/tests/resource_estimation/test_resource_operator.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 71da9cf83e0..1e3bfa1acf3 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -95,5 +95,6 @@ def resource_rep_from_op(self) -> CompressedResourceOp: """Returns a compressed representation directly from the operator""" return self.__class__.resource_rep(**self.resource_params()) + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 1eab218ddd8..46a762b7776 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -17,9 +17,9 @@ import pennylane as qml from pennylane.labs.resource_estimation import ( CompressedResourceOp, - ResourceOperator, ResourceControlledPhaseShift, ResourceHadamard, + ResourceOperator, ResourceSWAP, ) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_operator.py b/pennylane/labs/tests/resource_estimation/test_resource_operator.py index 18ffa0c98d9..f88026f86b7 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_operator.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_operator.py @@ -102,6 +102,7 @@ def _resource_decomp(): DummyClass.set_resources(lambda _: 5) assert DummyClass.resources(10) == 5 + def test_resource_rep_from_op(): """Test that the resource_rep_from_op method is the composition of resource_params and resource_rep""" From 9a9584d68a8b5af946a0f2ffc843303778016470 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 11:39:58 -0500 Subject: [PATCH 157/335] formatting --- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 795e1305255..630d1867173 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -55,14 +55,14 @@ class ResourceSWAP(qml.SWAP, re.ResourceOperator): 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0 \end{bmatrix} - + \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ 0 & 1 & 0 & 0 \end{bmatrix} - + \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0\\ From 532dcb88b7053eb11fddcd7d0369cdeae12f03c8 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 13:07:31 -0500 Subject: [PATCH 158/335] update init --- pennylane/labs/resource_estimation/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index f8b3dca5f0c..58d847fae23 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -42,6 +42,7 @@ ~ResourceControlledPhaseShift ~ResourceHadamard ~ResourceRZ + ~ResourceSWAP ~ResourceT Templates @@ -51,6 +52,14 @@ :toctree: api ~ResourceQFT + +Exceptions +~~~~~~~~~~ + +..autosummary:: + :toctree: api + + ~ResourcesNotDefined """ from .resource_operator import ResourceOperator, ResourcesNotDefined From 2b90ba7325bae4cb46c847994a57086e28ba855e Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 13:48:38 -0500 Subject: [PATCH 159/335] docstrings and stuff --- .../labs/resource_estimation/__init__.py | 3 +- .../ops/qubit/non_parametric_ops.py | 48 +++++++++---------- .../templates/subroutines.py | 7 ++- .../ops/op_math/test_controlled_ops.py | 11 ++--- .../ops/qubit/test_non_parametric_ops.py | 6 ++- .../templates/test_resource_qft.py | 2 +- 6 files changed, 41 insertions(+), 36 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 58d847fae23..be6db245e56 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -56,10 +56,11 @@ Exceptions ~~~~~~~~~~ -..autosummary:: +.. autosummary:: :toctree: api ~ResourcesNotDefined + """ from .resource_operator import ResourceOperator, ResourcesNotDefined diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 630d1867173..0e77b6da83e 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -43,35 +43,31 @@ class ResourceSWAP(qml.SWAP, re.ResourceOperator): .. math:: - SWAP = \begin{bmatrix} + SWAP = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 0 & 1 & 0\\ + 0 & 1 & 0 & 0\\ + 0 & 0 & 0 & 1 + \end{bmatrix} + = \begin{bmatrix} 1 & 0 & 0 & 0 \\ + 0 & 1 & 0 & 0\\ + 0 & 0 & 0 & 1\\ + 0 & 0 & 1 & 0 + \end{bmatrix} + \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 1\\ 0 & 0 & 1 & 0\\ + 0 & 1 & 0 & 0 + \end{bmatrix} + \begin{bmatrix} + 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0\\ - 0 & 0 & 0 & 1 - \end{bmatrix}. - = \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & 1 & 0 & 0\\ - 0 & 0 & 0 & 1\\ - 0 & 0 & 1 & 0 - \end{bmatrix} - - \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & 0 & 0 & 1\\ - 0 & 0 & 1 & 0\\ - 0 & 1 & 0 & 0 - \end{bmatrix} - - \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & 1 & 0 & 0\\ - 0 & 0 & 0 & 1\\ - 0 & 0 & 1 & 0 - \end{bmatrix}. - - 0: ─╭●─╭X─╭●─┤ - 1: ─╰X─╰●─╰X─┤ + 0 & 0 & 0 & 1\\ + 0 & 0 & 1 & 0 + \end{bmatrix}. + """ @staticmethod diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 46a762b7776..4dfa5747c28 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -27,7 +27,12 @@ class ResourceQFT(qml.QFT, ResourceOperator): - """Resource class for QFT""" + """Resource class for QFT + + Resources: + The resources are obtained from the textbook decomposition of QFT. See + chapter 5 of Quantum Computing and Quantum Information by Nielsen and Chuang for more details. + """ @staticmethod def _resource_decomp(num_wires, **kwargs) -> Dict[CompressedResourceOp, int]: diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index d8495f45d71..07d95ef8c9e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -75,12 +75,11 @@ def test_resources_from_rep(self, phi, wires): re.CompressedResourceOp(re.ResourceRZ, {}): 3, } - assert ( - op.resources( - **re.ResourceControlledPhaseShift.resource_rep(**op.resource_params()).params - ) - == expected - ) + op_compressed_rep = op.resource_rep_from_op() + op_resource_params = op_compressed_rep.params + op_compressed_rep_type = op_compressed_rep.op_type + + assert op_compressed_rep_type.resources(**op_resource_params) == expected class TestCNOT: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 95f43f37686..3ebc3a0ed9c 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -69,7 +69,11 @@ def test_resources_from_rep(self): cnot = re.ResourceCNOT.resource_rep() expected = {cnot: 3} - assert op.resources(**re.ResourceSWAP.resource_rep().params) == expected + op_compressed_rep = op.resource_rep_from_op() + op_resource_params = op_compressed_rep.params + op_compressed_rep_type = op_compressed_rep.op_type + + assert op_compressed_rep_type.resources(**op_resource_params) == expected class TestT: diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index fbd90f6ff47..761d30e5f3b 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -75,7 +75,7 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph expected = {hadamard: num_hadamard, swap: num_swap, ctrl_phase_shift: num_ctrl_phase_shift} rep = re.ResourceQFT.resource_rep(num_wires) - actual = re.ResourceQFT.resources(**rep.params) + actual = rep.op_type.resources(**rep.params) assert actual == expected From 7ca1498e8b30345f95aaac6c062317428710b20e Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 13:51:03 -0500 Subject: [PATCH 160/335] remove unnecessary file --- .../resource_estimation/resource_tracking.py | 271 ------------------ 1 file changed, 271 deletions(-) delete mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py deleted file mode 100644 index fcabaa0f440..00000000000 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -r"""Core resource tracking logic.""" -from collections import defaultdict -from collections.abc import Callable -from functools import singledispatch, wraps -from typing import Dict, Iterable, List, Set - -import pennylane as qml -from pennylane.measurements import MeasurementProcess -from pennylane.operation import DecompositionUndefinedError, Operation -from pennylane.queuing import AnnotatedQueue -from pennylane.tape import QuantumScript -from pennylane.wires import Wires - -from .resource_container import CompressedResourceOp, Resources -from .resource_operator import ResourceOperator, ResourceOperatorNotImplemented - -# pylint: disable=dangerous-default-value,protected-access - -_StandardGateSet = { - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "SWAP", - "CNOT", - "S", - "T", - "Toffoli", - "RX", - "RY", - "RZ", - "PhaseShift", -} - - -DefaultGateSet = { - "Hadamard", - "CNOT", - "S", - "T", - "Toffoli", -} - - -resource_config = { - "error_rx": 10e-3, - "error_ry": 10e-3, - "error_rz": 10e-3, -} - - -@singledispatch -def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: - """Obtain the resources from a quantum circuit or operation in terms of the gates provided - in the gate_set. - - Args: - obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. - gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. - config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. - - Returns: - Resources: The total resources of the quantum circuit. - - Rasies: - TypeError: "Could not obtain resources for obj of type (type(obj)). - """ - - raise TypeError( - f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" - ) - - -@get_resources.register -def resources_from_operation( - obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from an operation""" - - if not isinstance(obj, ResourceOperator): - obj = _op_to_resource_op(obj) - - cp_rep = obj.resource_rep_from_op() - gate_counts_dict = defaultdict(int) - _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) - - num_wires = len(obj.wires) - num_gates = sum(gate_counts_dict.values()) - - return Resources(gate_types=gate_counts_dict, num_gates=num_gates, num_wires=num_wires) - - -@get_resources.register -def resources_from_qfunc( - obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from a quantum function which queues operations!""" - - @wraps(obj) - def wrapper(*args, **kwargs): - with AnnotatedQueue() as q: - obj(*args, **kwargs) - - operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) - compressed_res_ops_lst = _operations_to_compressed_reps(operations) - - gate_counts_dict = defaultdict(int) - for cmp_rep_op in compressed_res_ops_lst: - _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config - ) - - # Validation: - condensed_gate_counts = defaultdict(int) - for sub_cmp_rep, counts in gate_counts_dict.items(): - _counts_from_compressed_res_op( - sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config - ) - - clean_gate_counts = _clean_gate_counts(condensed_gate_counts) - num_gates = sum(clean_gate_counts.values()) - num_wires = len(Wires.shared_wires(tuple(op.wires for op in operations))) - return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) - - return wrapper - - -@get_resources.register -def resources_from_tape( - obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from a quantum tape""" - num_wires = obj.num_wires - operations = obj.operations - compressed_res_ops_lst = _operations_to_compressed_reps(operations) - - gate_counts_dict = defaultdict(int) - for cmp_rep_op in compressed_res_ops_lst: - _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config - ) - - # Validation: - condensed_gate_counts = defaultdict(int) - for sub_cmp_rep, counts in gate_counts_dict.items(): - _counts_from_compressed_res_op( - sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config - ) - - clean_gate_counts = _clean_gate_counts(condensed_gate_counts) - num_gates = sum(clean_gate_counts.values()) - - return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) - - -def _counts_from_compressed_res_op( - cp_rep: CompressedResourceOp, - gate_counts_dict, - gate_set: Set, - scalar: int = 1, - config: Dict = resource_config, -) -> None: - """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. - - Args: - cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from - gate_counts_dict (_type_): base dictionary to modify with the resource counts - gate_set (Set): the set of operations to track resources with respect too - scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. - config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. - """ - ## If op in gate_set add to resources - if cp_rep._name in gate_set: - gate_counts_dict[cp_rep] += scalar - return - - ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources - resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) - - for sub_cp_rep, counts in resource_decomp.items(): - _counts_from_compressed_res_op( - sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config - ) - - return - - -@qml.QueuingManager.stop_recording() -def _op_to_resource_op(op: Operation) -> ResourceOperator: - """Map a PL Operator to its corresponding Resource Operator - - Args: - op Operation: A PennyLane operator - - Returns: - ResourceOperator: A ResourceOperator instantiated with op's parameters and hyperparameters. - - """ - import pennylane.labs.resource_estimation as re # pylint: disable=import-outside-toplevel - - name = "Resource" + op._name - - try: - cls = vars(re)[name] - except KeyError as exc: - raise ResourceOperatorNotImplemented( - f"No resource operator for PennyLane operator {op._name} has been implemented." - ) from exc - - return cls._unflatten(*op._flatten()) - - -def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: - """Map resources with gate_types made from CompressedResourceOps - into one which tracks just strings of operations! - - Args: - gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops - - Returns: - Dict[str, int]: gate counts in terms of names of operations - """ - clean_gate_counts = defaultdict(int) - - for cmp_res_op, counts in gate_counts.items(): - clean_gate_counts[cmp_res_op._name] += counts - - return clean_gate_counts - - -@qml.QueuingManager.stop_recording() -def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: - """Convert the sequence of operations to a list of compressed resource ops. - - Args: - ops (Iterable[Operation]): set of operations to convert. - - Returns: - List[CompressedResourceOp]: set of converted compressed resource ops. - """ - cmp_rep_ops = [] - for op in ops: - if isinstance(op, ResourceOperator): - cmp_rep_ops.append(op.resource_rep_from_op()) - - else: - try: - cmp_rep_ops.append(_op_to_resource_op(op).resource_rep_from_op()) - - except ResourceOperatorNotImplemented as exc: - try: - decomp = op.decomposition() - cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) - except DecompositionUndefinedError: - raise ResourceOperatorNotImplemented(f"No resource operator defined for {op._name}, but {op._name} has no decomposition.") from exc - - - return cmp_rep_ops From 5a2475dc1e26c0895540d5c9e57c5273406c6e68 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 15:28:33 -0500 Subject: [PATCH 161/335] final review comments --- .../ops/op_math/controlled_ops.py | 20 ++++++++++++++++++- .../qubit/test_parametric_ops_single_qubit.py | 10 ++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 87a729a2c3a..405af0f1193 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -21,7 +21,25 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): - """Resource class for ControlledPhaseShift""" + r"""Resource class for ControlledPhaseShift + + Resources: + The resources come from the following identity expressing Controlled Phase Shift + as a product of Phase Shifts and CNOTs. + + + .. math:: + + CR_\phi(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 1 & 0 & 0 \\ + 0 & 0 & 1 & 0 \\ + 0 & 0 & 0 & e^{i\phi} + \end{bmatrix} = + (R_\phi(\phi/2) \otimes I) \cdot CNOT \cdot (I \otimes R_\phi(-\phi/2)) \cdot CNOT \cdot (I \otimes R_\phi(\phi/2)) + + + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 7c9f7ebd07a..792d3b97026 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -14,7 +14,6 @@ """ Tests for parametric single qubit resource operators. """ -import numpy as np import pytest import pennylane.labs.resource_estimation as re @@ -24,15 +23,14 @@ # pylint: disable=no-self-use, use-implicit-booleaness-not-comparison - -@pytest.mark.parametrize("epsilon", [10e-3, 10e-4, 10e-5]) -def test_rotation_resources(epsilon): +params = list(zip([10e-3, 10e-4, 10e-5], [17, 21, 24])) +@pytest.mark.parametrize("epsilon, expected", params) +def test_rotation_resources(epsilon, expected): """Test the hardcoded resources used for RX, RY, RZ""" gate_types = {} - num_gates = round(1.149 * np.log2(1 / epsilon) + 9.2) t = re.CompressedResourceOp(re.ResourceT, {}) - gate_types[t] = num_gates + gate_types[t] = expected assert gate_types == _rotation_resources(epsilon=epsilon) From c60aec0389fdd5536c34bab1bc545cde9d6b18b2 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 15:59:21 -0500 Subject: [PATCH 162/335] formatting --- .../ops/qubit/test_parametric_ops_single_qubit.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 792d3b97026..957282cae1e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -24,6 +24,8 @@ # pylint: disable=no-self-use, use-implicit-booleaness-not-comparison params = list(zip([10e-3, 10e-4, 10e-5], [17, 21, 24])) + + @pytest.mark.parametrize("epsilon, expected", params) def test_rotation_resources(epsilon, expected): """Test the hardcoded resources used for RX, RY, RZ""" From 43a41a436680c472ec09fbb65a25d6ba6cd9a008 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 15 Nov 2024 17:08:51 -0500 Subject: [PATCH 163/335] symbolic class --- .../labs/resource_estimation/resource_operator.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index dc416235e80..3462ac19d0c 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -20,6 +20,8 @@ if TYPE_CHECKING: from pennylane.labs.resource_estimation import CompressedResourceOp +import pennylane.labs.resource_estimation as re + class ResourceOperator(ABC): r"""This is an abstract class that defines the methods a PennyLane Operator @@ -111,18 +113,19 @@ def pow_resource_rep(cls, exponent, *args, **kwargs) -> CompressedResourceOp: raise CompressedRepNotDefined class ResourceSymbolicOperator(ResourceOperator): - def __init__(self, base=None, id=None): - if not isinstance(base, ResourceOperator): - raise TypeError(f"base must be a subclass of ResourceOperator, got type {type(base)}.") - - super().__init__(base=base, id=id) + """Base class for resources of symbolic operators""" + #pylint: disable=no-member def resource_params(self): return { "base_class": type(self.base), "base_params": self.base.resource_params() } + @classmethod + def resource_rep(cls, base_class, base_params, **kwargs): + return re.CompressedResourceOp(cls, {"base_class": base_class, "base_params": base_params}) + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" From 8d8ea68d4203feaece9002b9f78a6661cd1e5907 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 08:23:09 -0500 Subject: [PATCH 164/335] ResourceConstructor -> ResourceOperator --- .../ops/qubit/qchem_ops.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 7a75fb11554..dd0d31f6650 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -1,7 +1,9 @@ import pennylane as qml -import pennylane.labs.resource_estimation.resource_constructor as rc +from pennylane.labs.resource_estimation import ResourceOperator + +class ResourceSingleExcitation(qml.SingleExcitation, ResourceOperator): + """Resource Operator for Single Excitation""" -class ResourceSingleExcitation(qml.SingleExcitation, rc.ResourceConstructor): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -13,7 +15,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, rc.ResourceConstructor): +class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -25,7 +27,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, rc.ResourceConstructor): +class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -37,7 +39,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceDoubleExcitation(qml.DoubleExcitation, rc.ResourceConstructor): +class ResourceDoubleExcitation(qml.DoubleExcitation, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -49,7 +51,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, rc.ResourceConstructor): +class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -61,7 +63,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, rc.ResourceConstructor): +class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -73,7 +75,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceOrbitalRotation(qml.OrbitalRotation, rc.ResourceConstructor): +class ResourceOrbitalRotation(qml.OrbitalRotation, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -85,7 +87,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceFermionicSWAP(qml.FermionicSWAP, rc.ResourceConstructor): +class ResourceFermionicSWAP(qml.FermionicSWAP, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return From 275b4a685b823cc378c73e9c3f0afdc6160b950e Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 08:33:48 -0500 Subject: [PATCH 165/335] remove test_resource_constructor --- .../test_resource_constructor.py | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 pennylane/labs/tests/resource_estimation/test_resource_constructor.py diff --git a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py b/pennylane/labs/tests/resource_estimation/test_resource_constructor.py deleted file mode 100644 index c4425549d63..00000000000 --- a/pennylane/labs/tests/resource_estimation/test_resource_constructor.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Test the abstract ResourceOperator class -""" -import pytest - -import pennylane.labs.resource_estimation as re - -# pylint: disable=abstract-class-instantiated,arguments-differ - - -def test_abstract_resource_decomp(): - """Test that the _resource_decomp method is abstract.""" - - class DummyClass(re.ResourceOperator): - """Dummy class for testing""" - - def resource_params(self): - return - - @staticmethod - def resource_rep(): - return - - with pytest.raises( - TypeError, - match="Can't instantiate abstract class DummyClass with abstract method _resource_decomp", - ): - DummyClass() - - -def test_abstract_resource_params(): - """Test that the resource_params method is abstract""" - - class DummyClass(re.ResourceOperator): - """Dummy class for testing""" - - @staticmethod - def _resource_decomp(): - return - - def resource_rep(self): - return - - with pytest.raises( - TypeError, - match="Can't instantiate abstract class DummyClass with abstract method resource_params", - ): - DummyClass() - - -def test_abstract_resource_rep(): - """Test that the resource_rep method is abstract""" - - class DummyClass(re.ResourceOperator): - """Dummy class for testing""" - - @staticmethod - def _resource_decomp(): - return - - def resource_params(self): - return - - with pytest.raises( - TypeError, - match="Can't instantiate abstract class DummyClass with abstract method resource_rep", - ): - DummyClass() - - -def test_set_resources(): - """Test that the resources method can be overriden""" - - class DummyClass(re.ResourceOperator): - """Dummy class for testing""" - - def resource_params(self): - return - - @staticmethod - def resource_rep(): - return - - @staticmethod - def _resource_decomp(): - return - - dummy = DummyClass() - DummyClass.set_resources(lambda _: 5) - assert DummyClass.resources(10) == 5 From 6be66a0b83af5407d760025457352a9a1b9a5ed0 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 08:39:51 -0500 Subject: [PATCH 166/335] update docstring --- .../labs/resource_estimation/resource_operator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 4d5ffee8003..76917dbf9fd 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -44,9 +44,9 @@ class ResourceQFT(qml.QFT, ResourceOperator): def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: gate_types = {} - hadamard = CompressedResourceOp(ResourceHadamard, {}) - swap = CompressedResourceOp(ResourceSWAP, {}) - ctrl_phase_shift = CompressedResourceOp(ResourceControlledPhaseShift, {}) + hadamard = ResourceHadamard.resource_rep() + swap = ResourceSWAP.resource_rep() + ctrl_phase_shift = ResourceControlledPhaseShift.resource_rep() gate_types[hadamard] = num_wires gate_types[swap] = num_wires // 2 @@ -54,8 +54,8 @@ def _resource_decomp(num_wires) -> Dict[CompressedResourceOp, int]: return gate_types - def resource_params(self) -> dict: - return {"num_wires": len(self.wires)} + def resource_params(self, num_wires) -> dict: + return {"num_wires": num_wires} @classmethod def resource_rep(cls, num_wires) -> CompressedResourceOp: @@ -66,7 +66,7 @@ def resource_rep(cls, num_wires) -> CompressedResourceOp: @staticmethod @abstractmethod def _resource_decomp(*args, **kwargs) -> Dict[CompressedResourceOp, int]: - """Returns the Resource object. This method is only to be used inside + """Returns a dictionary to be used for internal tracking of resources. This method is only to be used inside the methods of classes inheriting from ResourceOperator.""" @classmethod From fb96e14ec05a7e9216d0899bbd162fb3bf7bd8f8 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 09:32:41 -0500 Subject: [PATCH 167/335] adding more controlled resources --- .../ops/op_math/controlled_ops.py | 125 ++++++++++++++++-- pennylane/ops/op_math/controlled_ops.py | 10 ++ 2 files changed, 123 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index a7d7cfec7b2..6b433f265b7 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -221,7 +221,20 @@ class ResourceToffoli(qml.Toffoli, re.ResourceOperator): Resources: The resources are obtained from (in figure 1.) the paper `Novel constructions for the fault-tolerant - Toffoli gate `_. + Toffoli gate `_. + + The circuit which applies the Toffoli gate on target wire 'target' with control wires ('c1', 'c2') is + given by: + + .. code-block:: bash + + c1: ─╭●────╭X──T†────────╭X────╭●───────────────╭●─┤ + c2: ─│──╭X─│──╭●───T†─╭●─│──╭X─│────────────────╰Z─┤ + aux1: ─╰X─│──│──╰X───T──╰X─│──│──╰X────────────────║─┤ + aux2: ──H─╰●─╰●──T─────────╰●─╰●──H──S─╭●──H──┤↗├──║─┤ + target: ─────────────────────────────────╰X──────║───║─┤ + ╚═══╝ + """ @staticmethod @@ -281,14 +294,25 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): - """Resource class for MultiControlledX""" + """Resource class for MultiControlledX + + Resources: + The resources are obtained from (table 3.) the paper `Polylogarithmic-depth controlled-NOT gates + without ancilla qubits `_. The resources are + estimated using the following formulas. + + If the number of control qubits is 0 + + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: - return {} + num_control = len(self.hyperparameters["control_wires"]) + num_work_wires = len(self.hyperparameters["work_wires"]) + return {"num_ctrl_wires": num_control, "num_work_wires": num_work_wires} @classmethod def resource_rep(cls) -> re.CompressedResourceOp: @@ -296,11 +320,36 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRX(qml.CRX, re.ResourceOperator): - """Resource class for CRX""" + """Resource class for CRX + + Resources: + + The resources are derived from the following identities: + + .. math:: + + \begin{align} + \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}, \\ + \hat{X} &= \hat{H} \dot \hat{Z} \dot \hat{H} + \end{align} + + The expression for controlled-RZ gates is used as defined in (figure 1b.) the paper + `T-count and T-depth of any multi-qubit unitary `_. + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - raise re.ResourcesNotDefined + gate_types = {} + + h = re.ResourceHadamard.resource_rep() + rz = re.ResourceRZ.resource_rep() + cnot = re.ResourceCNOT.resource_rep() + + gate_types[cnot] = 2 + gate_types[rz] = 2 + gate_types[h] = 2 + + return gate_types def resource_params(self) -> dict: return {} @@ -311,11 +360,27 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRY(qml.CRY, re.ResourceOperator): - """Resource class for CRY""" + """Resource class for CRY + + Resources: + The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. The resources are derived with the following identity: + + .. math:: \hat{RY}(- \theta) = \hat{X} \dot \hat{RY}(\theta) \dot \hat{X}. + + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - raise re.ResourcesNotDefined + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + ry = re.ResourceRY.resource_rep() + + gate_types[cnot] = 2 + gate_types[ry] = 2 + + return gate_types def resource_params(self) -> dict: return {} @@ -326,11 +391,27 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRZ(qml.CRZ, re.ResourceOperator): - """Resource class for CRZ""" + """Resource class for CRZ + + Resources: + The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. The resources are derived from the following identity: + + .. math:: \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}. + + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - raise re.ResourcesNotDefined + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() + + gate_types[cnot] = 2 + gate_types[rz] = 2 + + return gate_types def resource_params(self) -> dict: return {} @@ -341,11 +422,26 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRot(qml.CRot, re.ResourceOperator): - """Resource class for CRot""" + """Resource class for CRot + + Resources: + TODO: Add a source for resources! + + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - raise re.ResourcesNotDefined + gate_types = {} + + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRZ.resource_rep() + + gate_types[cnot] = 2 + gate_types[rz] = 3 + gate_types[ry] = 2 + + return gate_types def resource_params(self) -> dict: return {} @@ -356,7 +452,12 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): - """Resource class for ControlledPhaseShift""" + """Resource class for ControlledPhaseShift + + Resources: + TODO: Add a source for resources + + """ @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/ops/op_math/controlled_ops.py b/pennylane/ops/op_math/controlled_ops.py index 3bcf4d98a0c..3bb1df60bf2 100644 --- a/pennylane/ops/op_math/controlled_ops.py +++ b/pennylane/ops/op_math/controlled_ops.py @@ -1381,6 +1381,16 @@ def compute_decomposition(phi, wires): # pylint: disable=arguments-differ """ pi_half = qml.math.ones_like(phi) * (np.pi / 2) + + # return [ + # qml.Hadamard(wires[1]), + # qml.RZ(phi/2, wires=wires[1]), + # qml.CNOT(wires), + # qml.RZ(phi/2, wires=wires[1]), + # qml.CNOT(wires), + # qml.Hadamard(wires[1]), + # ] + return [ qml.RZ(pi_half, wires=wires[1]), qml.RY(phi / 2, wires=wires[1]), From 15138ee145763bf930b07e4601623b37b27eda07 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 10:26:10 -0500 Subject: [PATCH 168/335] modify docs --- doc/index.rst | 1 - pennylane/labs/__init__.py | 6 +++--- pennylane/labs/resource_estimation/__init__.py | 18 ------------------ 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index e2e1706ca62..b554310934a 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -202,7 +202,6 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve code/qml_gradients code/qml_kernels code/qml_labs - code/qml_labs_resource_estimation code/qml_logging code/qml_math code/qml_noise diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index eee151ac661..7940f5e7e25 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -24,8 +24,8 @@ .. currentmodule:: pennylane.labs.resource_estimation -Resource Estimation -~~~~~~~~~~~~~~~~~~~ +Resource Estimation Base Classes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: :toctree: api @@ -42,4 +42,4 @@ ResourceOperator, ) -__all__ = ["Resources", "CompressedResourceOp"] +__all__ = ["Resources", "CompressedResourceOp", "ResourceOperator"] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 5a657aeefbf..6cbe29d0b2a 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -14,24 +14,6 @@ r""" As part of the labs module, this module contains experimental features for resource estimation. - -.. warning:: - - This module is experimental. Frequent changes will occur, - with no guarantees of stability or backwards compatibility. - -.. currentmodule:: pennylane.labs.resource_estimation - -Base Objects -~~~~~~~~~~~~ - -.. autosummary:: - :toctree: api - - ~ResourceOperator - ~Resources - ~CompressedResourceOp - """ from .resource_operator import ResourceOperator From da59a91d560e695efd47a73c6c1fcc1898a818a9 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 11:07:35 -0500 Subject: [PATCH 169/335] codecov --- doc/code/qml_labs_resource_estimation.rst | 6 ------ pennylane/labs/__init__.py | 4 +--- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 doc/code/qml_labs_resource_estimation.rst diff --git a/doc/code/qml_labs_resource_estimation.rst b/doc/code/qml_labs_resource_estimation.rst deleted file mode 100644 index 910b22edb12..00000000000 --- a/doc/code/qml_labs_resource_estimation.rst +++ /dev/null @@ -1,6 +0,0 @@ -qml.labs.resource_estimation -============================ - -.. currentmodule:: pennylane.labs.resource_estimation - -.. automodule:: pennylane.labs.resource_estimation diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index ac591bb7097..1c7f2749c4b 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -46,14 +46,12 @@ """ +from pennylane.labs import dla from .resource_estimation import ( Resources, CompressedResourceOp, ResourceOperator, ) -from pennylane.labs import dla - __all__ = ["Resources", "CompressedResourceOp", "ResourceOperator"] - From c6787feb180d7c4d2ceb26b00e002202a9b2fd9d Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 11:42:16 -0500 Subject: [PATCH 170/335] ci From 6da8258fa65da41fb0bfe07d0e73bb7e8b1dd608 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 12:15:44 -0500 Subject: [PATCH 171/335] format --- pennylane/labs/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 9974122f6d9..ea9cdd5164b 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -61,8 +61,8 @@ .. autosummary:: :toctree: api - ~ResourcesNotDefined - + ~ResourcesNotDefined + .. currentmodule:: pennylane.labs Modules From baf1946b1d24ffa75db5bfb5ccaa8823a66f4392 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 12:17:07 -0500 Subject: [PATCH 172/335] format --- pennylane/labs/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index f7dae1baf9d..d790d2008c3 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -67,8 +67,8 @@ .. autosummary:: :toctree: api - ~ResourcesNotDefined - + ~ResourcesNotDefined + .. currentmodule:: pennylane.labs Modules From 23866523a9f08b29dde8c23d5387d2c9033478f7 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 12:21:00 -0500 Subject: [PATCH 173/335] docs for X,Y,Z --- pennylane/labs/__init__.py | 9 +++++++++ pennylane/labs/resource_estimation/__init__.py | 3 +++ pennylane/labs/resource_estimation/ops/__init__.py | 3 +++ pennylane/labs/resource_estimation/ops/qubit/__init__.py | 3 +++ 4 files changed, 18 insertions(+) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index d790d2008c3..e3d7da64c3a 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -52,6 +52,9 @@ ~ResourceS ~ResourceSWAP ~ResourceT + ~ResourceX + ~ResourceY + ~ResourceZ Templates ~~~~~~~~~ @@ -100,6 +103,9 @@ ResourceRY, ResourceS, ResourceGlobalPhase, + ResourceX, + ResourceY, + ResourceZ, ) @@ -121,4 +127,7 @@ "ResourceRY", "ResourceS", "ResourceGlobalPhase", + "ResourceX", + "ResourceY", + "ResourceZ", ] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index ca2271a084f..32b9bcc7a05 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -32,6 +32,9 @@ ResourceS, ResourceSWAP, ResourceT, + ResourceX, + ResourceY, + ResourceZ, ) from .templates import ( diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index ed3b4a79325..0b5f06d15e1 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -27,6 +27,9 @@ ResourceS, ResourceSWAP, ResourceT, + ResourceX, + ResourceY, + ResourceZ, ) from .op_math import ( diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index a013cfb7d56..8ee2922e014 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -18,6 +18,9 @@ ResourceS, ResourceSWAP, ResourceT, + ResourceX, + ResourceY, + ResourceZ, ) from .parametric_ops_single_qubit import ( From 35b94c517af23168a2fc4abb184fa78015b9ae98 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 12:56:08 -0500 Subject: [PATCH 174/335] adjoints and pows work now --- .../labs/resource_estimation/__init__.py | 2 +- .../ops/op_math/controlled_ops.py | 4 + .../ops/op_math/symbolic.py | 102 +++++- .../ops/qubit/non_parametric_ops.py | 8 + .../resource_estimation/resource_operator.py | 28 +- .../resource_estimation/resource_tracking.py | 316 ++++++++++++++++++ 6 files changed, 425 insertions(+), 35 deletions(-) create mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 1668e7e0c6f..d3f31c5396a 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -63,7 +63,7 @@ """ -from .resource_operator import ResourceOperator, ResourcesNotDefined, ResourceSymbolicOperator +from .resource_operator import ResourceOperator, ResourcesNotDefined from .resource_container import CompressedResourceOp, Resources from .ops import ( diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 405af0f1193..25d04ef4251 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -60,6 +60,10 @@ def resource_params(self): def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs): + return {cls.resource_rep(): 1} + class ResourceCNOT(qml.CNOT, re.ResourceOperator): """Resource class for CNOT""" diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index b55a7adf1ff..ca274a3ea69 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -1,33 +1,111 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for controlled operations.""" +from collections import defaultdict + import pennylane as qml import pennylane.labs.resource_estimation as re +from pennylane.labs.resource_estimation.resource_container import _combine_dict, _scale_dict from pennylane.ops.op_math.adjoint import AdjointOperation +from pennylane.ops.op_math.pow import PowOperation + +# pylint: disable=too-many-ancestors,arguments-differ -#pylint: disable=too-many-ancestors -class ResourceAdjoint(AdjointOperation, re.ResourceSymbolicOperator): +class ResourceAdjoint(AdjointOperation, re.ResourceOperator): """Resource class for Adjoint""" @staticmethod - def _resource_decomp(*args, **kwargs, base_class, base_params): + def _resource_decomp(base_class, base_params, **kwargs): try: - return base_class.adjoint_resource_rep() + return base_class.adjoint_resource_decomp(**base_params) + except re.ResourcesNotDefined: + gate_types = defaultdict(int) + decomp = base_class.resources(**base_params) + for gate, count in decomp.items(): + resources = gate.op_type.adjoint_resource_decomp(**gate.params) + _scale_dict(resources, count, in_place=True) + _combine_dict(gate_types, resources, in_place=True) + + return gate_types + + def resource_params(self): + return {"base_class": type(self.base), "base_params": self.base.resource_params()} + + @classmethod + def resource_rep(cls, base_class, base_params, **kwargs): + name = f"Adjoint({base_class.__name__})".replace("Resource", "") + return re.CompressedResourceOp( + cls, {"base_class": base_class, "base_params": base_params}, name=name + ) + -class ResourceControlled(qml.ops.Controlled, re.ResourceSymbolicOperator): +class ResourceControlled(qml.ops.Controlled, re.ResourceOperator): """Resource class for Controlled""" @staticmethod - def _resource_decomp(*args, **kwargs): - pass + def _resource_decomp(base_class, base_params, **kwargs): + try: + return base_class.controlled_resource_decomp(**base_params) + except re.ResourcesNotDefined: + gate_types = defaultdict(int) + decomp = base_class.resources(**base_params) + for gate, count in decomp.items(): + resources = gate.op_type.controlled_resource_decomp(**gate.params) + _scale_dict(resources, count, in_place=True) + _combine_dict(gate_types, resources, in_place=True) + + return gate_types def resource_params(self): - pass + return {"base_class": type(self.base), "base_params": self.base.resource_params()} + + @classmethod + def resource_rep(cls, base_class, base_params, **kwargs): + name = f"Controlled({base_class.__name__})".replace("Resource", "") + return re.CompressedResourceOp( + cls, {"base_class": base_class, "base_params": base_params}, name=name + ) -class ResourcePow(qml.ops.Pow, re.ResourceSymbolicOperator): + +class ResourcePow(PowOperation, re.ResourceOperator): """Resource class for Pow""" @staticmethod - def _resource_decomp(*args, **kwargs): - pass + def _resource_decomp(base_class, z, base_params, **kwargs): + try: + return base_class.pow_resource_decomp(z, **base_params) + except re.ResourcesNotDefined: + pass + + try: + return _scale_dict(base_class.resources(**base_params), z) + except re.ResourcesNotDefined: + pass + + return {base_class.resource_rep(): z} def resource_params(self): - pass + return { + "base_class": type(self.base), + "z": self.z, + "base_params": self.base.resource_params(), + } + + @classmethod + def resource_rep(cls, base_class, z, base_params, **kwargs): + name = f"{base_class.__name__}**{z}".replace("Resource", "") + return re.CompressedResourceOp( + cls, {"base_class": base_class, "z": z, "base_params": base_params}, name=name + ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 0e77b6da83e..4e3a6b3236c 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -34,6 +34,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceSWAP(qml.SWAP, re.ResourceOperator): r"""Resource class for the SWAP gate. @@ -85,6 +89,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs): + return {cls.resource_rep(): 1} + class ResourceT(qml.T, re.ResourceOperator): """Resource class for T""" diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 3698169cc8e..5a58a49ecb2 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -98,36 +98,20 @@ def resource_rep_from_op(self) -> CompressedResourceOp: return self.__class__.resource_rep(**self.resource_params()) @classmethod - def adjoint_resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + def adjoint_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the adjoint of the operator""" - raise CompressedRepNotDefined + raise ResourcesNotDefined @classmethod - def controlled_resource_rep(cls, *args, **kwargs) -> CompressedResourceOp: + def controlled_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the controlled version of the operator""" - raise CompressedRepNotDefined + raise ResourcesNotDefined @classmethod - def pow_resource_rep(cls, exponent, *args, **kwargs) -> CompressedResourceOp: + def pow_resource_decomp(cls, z, *args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the operator raised to a power""" - raise CompressedRepNotDefined + raise ResourcesNotDefined -class ResourceSymbolicOperator(ResourceOperator): - """Base class for resources of symbolic operators""" - - #pylint: disable=no-member - def resource_params(self): - return { - "base_class": type(self.base), - "base_params": self.base.resource_params() - } - - @classmethod - def resource_rep(cls, base_class, base_params, **kwargs): - return re.CompressedResourceOp(cls, {"base_class": base_class, "base_params": base_params}) class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" - -class CompressedRepNotDefined(Exception): - """Exception to be raised when a compressed representation is not defined""" diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py new file mode 100644 index 00000000000..aa90d89f4df --- /dev/null +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -0,0 +1,316 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Core resource tracking logic.""" +from collections import defaultdict +from collections.abc import Callable +from functools import singledispatch, wraps +from typing import Dict, Iterable, List, Set + +import pennylane as qml +from pennylane.measurements import MeasurementProcess +from pennylane.operation import Operation +from pennylane.queuing import AnnotatedQueue +from pennylane.tape import QuantumScript +from pennylane.wires import Wires + +from .resource_container import CompressedResourceOp, Resources +from .resource_operator import ResourceOperator + +# pylint: disable=dangerous-default-value,protected-access + +_StandardGateSet = { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "SWAP", + "CNOT", + "S", + "T", + "Toffoli", + "RX", + "RY", + "RZ", + "PhaseShift", +} + + +DefaultGateSet = { + "Hadamard", + "CNOT", + "S", + "T", + "Toffoli", +} + + +resource_config = { + "error_rx": 10e-3, + "error_ry": 10e-3, + "error_rz": 10e-3, +} + + +@singledispatch +def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: + r"""Obtain the resources from a quantum circuit or operation in terms of the gates provided + in the gate_set. + + Args: + obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. + gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. + config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. + + Returns: + Resources: The total resources of the quantum circuit. + + Rasies: + TypeError: "Could not obtain resources for obj of type (type(obj))". + + **Example** + + We can track the resources of a quantum workflow by passing the quantum function defining our workflow directly + into this function. + + .. code-block:: python + + import pennylane.labs.resource_estimation as re + + def my_circuit(): + for w in range(2): + re.ResourceHadamard(w) + + re.ResourceCNOT([0, 1]) + re.ResourceRX(1.23, 0) + re.ResourceRY(-4.56, 1) + + re.ResourceQFT(wires=[0, 1, 2]) + return qml.expval(re.ResourceHadamard(2)) + + Note that we are passing a python function NOT a :class:`~.QNode`. The resources for this workflow are then obtained by: + + >>> res = re.get_resources(my_circuit)() + >>> print(res) + wires: 3 + gates: 202 + gate_types: + {'Hadamard': 5, 'CNOT': 10, 'T': 187} + + .. details:: + :title: Usage Details + + Users can provide custom gatesets to track resources with. Consider :code:`my_circuit()` from above: + + >>> my_gateset = {"Hadamard", "RX", "RY", "QFT", "CNOT"} + >>> print(re.get_resources(my_circuit, gate_set = my_gateset)()) + wires: 3 + gates: 6 + gate_types: + {'Hadamard': 2, 'CNOT': 1, 'RX': 1, 'RY': 1, 'QFT': 1} + + We can also obtain resources for individual operations and quantum tapes in a similar manner: + + >>> op = re.ResourceRX(1.23, 0) + >>> print(re.get_resources(op)) + wires: 1 + gates: 17 + gate_types: + {'T': 17} + + Finally, we can modify the config values listed in the global :code:`labs.resource_estimation.resource_config` + dictionary to have finegrain control of how the resources are computed. + + >>> re.resource_config + {'error_rx': 0.01, 'error_ry': 0.01, 'error_rz': 0.01} + >>> + >>> my_config = copy.copy(re.resource_config) + >>> my_config["error_rx"] = 0.001 + >>> + >>> print(re.get_resources(op, config=my_config)) + wires: 1 + gates: 21 + gate_types: + {'T': 21} + + """ + + raise TypeError( + f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" + ) + + +@get_resources.register +def resources_from_operation( + obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from an operation""" + + if isinstance(obj, ResourceOperator): + cp_rep = obj.resource_rep_from_op() + + gate_counts_dict = defaultdict(int) + _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) + gate_types = _clean_gate_counts(gate_counts_dict) + + n_gates = sum(gate_types.values()) + return Resources(num_wires=len(obj.wires), num_gates=n_gates, gate_types=gate_types) + + res = Resources() # TODO: Add implementation here! + return res + + +@get_resources.register +def resources_from_qfunc( + obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Callable: + """Get resources from a quantum function which queues operations!""" + + @wraps(obj) + def wrapper(*args, **kwargs): + with AnnotatedQueue() as q: + obj(*args, **kwargs) + + operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + initial_gate_set = set.union(gate_set, _StandardGateSet) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=initial_gate_set, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + num_wires = len(Wires.all_wires(tuple(op.wires for op in operations))) + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + return wrapper + + +@get_resources.register +def resources_from_tape( + obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config +) -> Resources: + """Get resources from a quantum tape""" + num_wires = obj.num_wires + operations = obj.operations + compressed_res_ops_lst = _operations_to_compressed_reps(operations) + + gate_counts_dict = defaultdict(int) + for cmp_rep_op in compressed_res_ops_lst: + _counts_from_compressed_res_op( + cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config + ) + + # Validation: + condensed_gate_counts = defaultdict(int) + for sub_cmp_rep, counts in gate_counts_dict.items(): + _counts_from_compressed_res_op( + sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config + ) + + clean_gate_counts = _clean_gate_counts(condensed_gate_counts) + num_gates = sum(clean_gate_counts.values()) + + return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) + + +def _counts_from_compressed_res_op( + cp_rep: CompressedResourceOp, + gate_counts_dict, + gate_set: Set, + scalar: int = 1, + config: Dict = resource_config, +) -> None: + """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. + + Args: + cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from + gate_counts_dict (Dict): base dictionary to modify with the resource counts + gate_set (Set): the set of operations to track resources with respect too + scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. + config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. + """ + ## If op in gate_set add to resources + if cp_rep._name in gate_set: + gate_counts_dict[cp_rep] += scalar + return + + ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources + resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) + + for sub_cp_rep, counts in resource_decomp.items(): + _counts_from_compressed_res_op( + sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config + ) + + return + + +def _temp_map_func(op: Operation) -> ResourceOperator: + """Temp map function""" + raise NotImplementedError + + +def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: + """Map resources with gate_types made from CompressedResourceOps + into one which tracks just strings of operations! + + Args: + gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops + + Returns: + Dict[str, int]: gate counts in terms of names of operations + """ + clean_gate_counts = defaultdict(int) + + for cmp_res_op, counts in gate_counts.items(): + clean_gate_counts[cmp_res_op._name] += counts + + return clean_gate_counts + + +@qml.QueuingManager.stop_recording() +def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: + """Convert the sequence of operations to a list of compressed resource ops. + + Args: + ops (Iterable[Operation]): set of operations to convert. + + Returns: + List[CompressedResourceOp]: set of converted compressed resource ops. + """ + cmp_rep_ops = [] + for op in ops: + if isinstance(op, ResourceOperator): + cmp_rep_ops.append(op.resource_rep_from_op()) + + else: + try: + cmp_rep_ops.append(_temp_map_func(op).resource_rep_from_op()) + + except NotImplementedError: + decomp = op.decomposition() + cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) + + return cmp_rep_ops From 73cf68b2f9e183bceef41cc0bddefaf113a66679 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 13:17:12 -0500 Subject: [PATCH 175/335] inerhting from ControlledOp --- pennylane/labs/resource_estimation/ops/op_math/symbolic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index ca274a3ea69..b17efb0a3cb 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -11,13 +11,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r"""Resource operators for controlled operations.""" +r"""Resource operators for symbolic operations.""" from collections import defaultdict -import pennylane as qml import pennylane.labs.resource_estimation as re from pennylane.labs.resource_estimation.resource_container import _combine_dict, _scale_dict from pennylane.ops.op_math.adjoint import AdjointOperation +from pennylane.ops.op_math.controlled import ControlledOp from pennylane.ops.op_math.pow import PowOperation # pylint: disable=too-many-ancestors,arguments-differ @@ -51,7 +51,7 @@ def resource_rep(cls, base_class, base_params, **kwargs): ) -class ResourceControlled(qml.ops.Controlled, re.ResourceOperator): +class ResourceControlled(ControlledOp, re.ResourceOperator): """Resource class for Controlled""" @staticmethod From 30e30f70ae7263e1acae7b6aabaf604e12845442 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 13:17:55 -0500 Subject: [PATCH 176/335] remove unnecessary file --- .../resource_estimation/resource_tracking.py | 316 ------------------ 1 file changed, 316 deletions(-) delete mode 100644 pennylane/labs/resource_estimation/resource_tracking.py diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py deleted file mode 100644 index aa90d89f4df..00000000000 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ /dev/null @@ -1,316 +0,0 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -r"""Core resource tracking logic.""" -from collections import defaultdict -from collections.abc import Callable -from functools import singledispatch, wraps -from typing import Dict, Iterable, List, Set - -import pennylane as qml -from pennylane.measurements import MeasurementProcess -from pennylane.operation import Operation -from pennylane.queuing import AnnotatedQueue -from pennylane.tape import QuantumScript -from pennylane.wires import Wires - -from .resource_container import CompressedResourceOp, Resources -from .resource_operator import ResourceOperator - -# pylint: disable=dangerous-default-value,protected-access - -_StandardGateSet = { - "PauliX", - "PauliY", - "PauliZ", - "Hadamard", - "SWAP", - "CNOT", - "S", - "T", - "Toffoli", - "RX", - "RY", - "RZ", - "PhaseShift", -} - - -DefaultGateSet = { - "Hadamard", - "CNOT", - "S", - "T", - "Toffoli", -} - - -resource_config = { - "error_rx": 10e-3, - "error_ry": 10e-3, - "error_rz": 10e-3, -} - - -@singledispatch -def get_resources(obj, gate_set: Set = DefaultGateSet, config: Dict = resource_config) -> Resources: - r"""Obtain the resources from a quantum circuit or operation in terms of the gates provided - in the gate_set. - - Args: - obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. - gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. - config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. - - Returns: - Resources: The total resources of the quantum circuit. - - Rasies: - TypeError: "Could not obtain resources for obj of type (type(obj))". - - **Example** - - We can track the resources of a quantum workflow by passing the quantum function defining our workflow directly - into this function. - - .. code-block:: python - - import pennylane.labs.resource_estimation as re - - def my_circuit(): - for w in range(2): - re.ResourceHadamard(w) - - re.ResourceCNOT([0, 1]) - re.ResourceRX(1.23, 0) - re.ResourceRY(-4.56, 1) - - re.ResourceQFT(wires=[0, 1, 2]) - return qml.expval(re.ResourceHadamard(2)) - - Note that we are passing a python function NOT a :class:`~.QNode`. The resources for this workflow are then obtained by: - - >>> res = re.get_resources(my_circuit)() - >>> print(res) - wires: 3 - gates: 202 - gate_types: - {'Hadamard': 5, 'CNOT': 10, 'T': 187} - - .. details:: - :title: Usage Details - - Users can provide custom gatesets to track resources with. Consider :code:`my_circuit()` from above: - - >>> my_gateset = {"Hadamard", "RX", "RY", "QFT", "CNOT"} - >>> print(re.get_resources(my_circuit, gate_set = my_gateset)()) - wires: 3 - gates: 6 - gate_types: - {'Hadamard': 2, 'CNOT': 1, 'RX': 1, 'RY': 1, 'QFT': 1} - - We can also obtain resources for individual operations and quantum tapes in a similar manner: - - >>> op = re.ResourceRX(1.23, 0) - >>> print(re.get_resources(op)) - wires: 1 - gates: 17 - gate_types: - {'T': 17} - - Finally, we can modify the config values listed in the global :code:`labs.resource_estimation.resource_config` - dictionary to have finegrain control of how the resources are computed. - - >>> re.resource_config - {'error_rx': 0.01, 'error_ry': 0.01, 'error_rz': 0.01} - >>> - >>> my_config = copy.copy(re.resource_config) - >>> my_config["error_rx"] = 0.001 - >>> - >>> print(re.get_resources(op, config=my_config)) - wires: 1 - gates: 21 - gate_types: - {'T': 21} - - """ - - raise TypeError( - f"Could not obtain resources for obj of type {type(obj)}. obj must be one of Operation, Callable or QuantumScript" - ) - - -@get_resources.register -def resources_from_operation( - obj: Operation, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from an operation""" - - if isinstance(obj, ResourceOperator): - cp_rep = obj.resource_rep_from_op() - - gate_counts_dict = defaultdict(int) - _counts_from_compressed_res_op(cp_rep, gate_counts_dict, gate_set=gate_set, config=config) - gate_types = _clean_gate_counts(gate_counts_dict) - - n_gates = sum(gate_types.values()) - return Resources(num_wires=len(obj.wires), num_gates=n_gates, gate_types=gate_types) - - res = Resources() # TODO: Add implementation here! - return res - - -@get_resources.register -def resources_from_qfunc( - obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Callable: - """Get resources from a quantum function which queues operations!""" - - @wraps(obj) - def wrapper(*args, **kwargs): - with AnnotatedQueue() as q: - obj(*args, **kwargs) - - operations = tuple(op for op in q.queue if not isinstance(op, MeasurementProcess)) - compressed_res_ops_lst = _operations_to_compressed_reps(operations) - - initial_gate_set = set.union(gate_set, _StandardGateSet) - - gate_counts_dict = defaultdict(int) - for cmp_rep_op in compressed_res_ops_lst: - _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=initial_gate_set, config=config - ) - - # Validation: - condensed_gate_counts = defaultdict(int) - for sub_cmp_rep, counts in gate_counts_dict.items(): - _counts_from_compressed_res_op( - sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config - ) - - clean_gate_counts = _clean_gate_counts(condensed_gate_counts) - num_gates = sum(clean_gate_counts.values()) - num_wires = len(Wires.all_wires(tuple(op.wires for op in operations))) - return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) - - return wrapper - - -@get_resources.register -def resources_from_tape( - obj: QuantumScript, gate_set: Set = DefaultGateSet, config: Dict = resource_config -) -> Resources: - """Get resources from a quantum tape""" - num_wires = obj.num_wires - operations = obj.operations - compressed_res_ops_lst = _operations_to_compressed_reps(operations) - - gate_counts_dict = defaultdict(int) - for cmp_rep_op in compressed_res_ops_lst: - _counts_from_compressed_res_op( - cmp_rep_op, gate_counts_dict, gate_set=_StandardGateSet, config=config - ) - - # Validation: - condensed_gate_counts = defaultdict(int) - for sub_cmp_rep, counts in gate_counts_dict.items(): - _counts_from_compressed_res_op( - sub_cmp_rep, condensed_gate_counts, scalar=counts, gate_set=gate_set, config=config - ) - - clean_gate_counts = _clean_gate_counts(condensed_gate_counts) - num_gates = sum(clean_gate_counts.values()) - - return Resources(num_wires=num_wires, num_gates=num_gates, gate_types=clean_gate_counts) - - -def _counts_from_compressed_res_op( - cp_rep: CompressedResourceOp, - gate_counts_dict, - gate_set: Set, - scalar: int = 1, - config: Dict = resource_config, -) -> None: - """Modifies the `gate_counts_dict` argument by adding the (scaled) resources of the operation provided. - - Args: - cp_rep (CompressedResourceOp): operation in compressed representation to extract resources from - gate_counts_dict (Dict): base dictionary to modify with the resource counts - gate_set (Set): the set of operations to track resources with respect too - scalar (int, optional): optional scalar to multiply the counts. Defaults to 1. - config (Dict, optional): additional parameters to specify the resources from an operator. Defaults to resource_config. - """ - ## If op in gate_set add to resources - if cp_rep._name in gate_set: - gate_counts_dict[cp_rep] += scalar - return - - ## Else decompose cp_rep using its resource decomp [cp_rep --> dict[cp_rep: counts]] and extract resources - resource_decomp = cp_rep.op_type.resources(config=config, **cp_rep.params) - - for sub_cp_rep, counts in resource_decomp.items(): - _counts_from_compressed_res_op( - sub_cp_rep, gate_counts_dict, scalar=scalar * counts, gate_set=gate_set, config=config - ) - - return - - -def _temp_map_func(op: Operation) -> ResourceOperator: - """Temp map function""" - raise NotImplementedError - - -def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: - """Map resources with gate_types made from CompressedResourceOps - into one which tracks just strings of operations! - - Args: - gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops - - Returns: - Dict[str, int]: gate counts in terms of names of operations - """ - clean_gate_counts = defaultdict(int) - - for cmp_res_op, counts in gate_counts.items(): - clean_gate_counts[cmp_res_op._name] += counts - - return clean_gate_counts - - -@qml.QueuingManager.stop_recording() -def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedResourceOp]: - """Convert the sequence of operations to a list of compressed resource ops. - - Args: - ops (Iterable[Operation]): set of operations to convert. - - Returns: - List[CompressedResourceOp]: set of converted compressed resource ops. - """ - cmp_rep_ops = [] - for op in ops: - if isinstance(op, ResourceOperator): - cmp_rep_ops.append(op.resource_rep_from_op()) - - else: - try: - cmp_rep_ops.append(_temp_map_func(op).resource_rep_from_op()) - - except NotImplementedError: - decomp = op.decomposition() - cmp_rep_ops.extend(_operations_to_compressed_reps(decomp)) - - return cmp_rep_ops From 7cdc9ddcadcae2b84ba7d6dde05e088177eca4be Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 13:18:36 -0500 Subject: [PATCH 177/335] supporting symbolic ops in compressedresourceop --- .../labs/resource_estimation/resource_container.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 1b80b8caf87..eab4f81284d 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -38,7 +38,7 @@ class CompressedResourceOp: Hadamard(num_wires=1) """ - def __init__(self, op_type, params: dict) -> None: + def __init__(self, op_type, params: dict, name=None) -> None: r"""Instantiate the light weight class corresponding to the operator type and parameters. Args: @@ -59,10 +59,10 @@ def __init__(self, op_type, params: dict) -> None: if not issubclass(op_type, ResourceOperator): raise TypeError(f"op_type must be a subclass of ResourceOperator. Got {op_type}.") - self._name = (op_type.__name__).replace("Resource", "") self.op_type = op_type self.params = params - self._hashable_params = tuple(params.items()) + self._hashable_params = _make_hashable(params) + self._name = name or self.op_type.__name__.replace("Resource", "") def __hash__(self) -> int: return hash((self._name, self._hashable_params)) @@ -257,3 +257,10 @@ def _scale_dict(dict1: defaultdict, scalar: int, in_place=False): combined_dict[k] *= scalar return combined_dict + + +def _make_hashable(d: dict) -> tuple: + if isinstance(d, dict): + return tuple((name, _make_hashable(value)) for name, value in d.items()) + + return d From d9de72000f40f3c0c60cf0ad4cd410b0dadaa568 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 13:22:47 -0500 Subject: [PATCH 178/335] removing trailing whitespace --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 405af0f1193..bfe6cd97849 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -22,13 +22,13 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for ControlledPhaseShift - + Resources: The resources come from the following identity expressing Controlled Phase Shift as a product of Phase Shifts and CNOTs. - .. math:: + .. math:: CR_\phi(\phi) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ @@ -36,7 +36,7 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & e^{i\phi} \end{bmatrix} = - (R_\phi(\phi/2) \otimes I) \cdot CNOT \cdot (I \otimes R_\phi(-\phi/2)) \cdot CNOT \cdot (I \otimes R_\phi(\phi/2)) + (R_\phi(\phi/2) \otimes I) \cdot CNOT \cdot (I \otimes R_\phi(-\phi/2)) \cdot CNOT \cdot (I \otimes R_\phi(\phi/2)) """ From 6d38a1e40865f2db6bce3c7bcf59fc5379e4dd08 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 13:25:04 -0500 Subject: [PATCH 179/335] ignore arguments-differ pylint warning --- pennylane/labs/resource_estimation/ops/identity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 57f81fa78cd..cd7f23042f5 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -17,7 +17,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -# pylint: disable=no-self-use,too-many-ancestors +# pylint: disable=arguments-differ,no-self-use,too-many-ancestors class ResourceIdentity(qml.Identity, re.ResourceOperator): From 2f9c965d6d7aad2ed8773c819d88ffd791d4f7a4 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 13:38:13 -0500 Subject: [PATCH 180/335] formatting --- pennylane/labs/resource_estimation/resource_operator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 5a58a49ecb2..0220a2846e0 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -20,8 +20,6 @@ if TYPE_CHECKING: from pennylane.labs.resource_estimation import CompressedResourceOp -import pennylane.labs.resource_estimation as re - class ResourceOperator(ABC): r"""This is an abstract class that defines the methods a PennyLane Operator From cb312b3709338506bc947933d4584c2f9094171d Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 14:16:55 -0500 Subject: [PATCH 181/335] tests for symbolic ops --- .../ops/op_math/test_symbolic.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py new file mode 100644 index 00000000000..2d3e0834835 --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -0,0 +1,84 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for symbolic resource operators. +""" + +import pennylane.labs.resource_estimation as re + +# pylint: disable=protected-access + + +class TestResourceAdjoint: + """Tests for ResourceAdjoint""" + + def test_resource_params(self): + """Test that the resources are correct""" + + base = re.ResourceQFT(wires=[0, 1, 2]) + op = re.ResourceAdjoint(base=base) + assert op.resource_params() == { + "base_class": re.ResourceQFT, + "base_params": base.resource_params(), + } + + def test_name(self): + """Test that the name of the compressed representation is correct""" + + base = re.ResourceQFT(wires=[0, 1, 2]) + op = re.ResourceAdjoint(base=base) + assert op.resource_rep_from_op()._name == "Adjoint(QFT)" + + +class TestResourceControlled: + """Tests for ResourceControlled""" + + def test_resource_params(self): + """Test that the resources are correct""" + + base = re.ResourceQFT(wires=[0, 1, 2]) + op = re.ResourceControlled(base=base, control_wires=[3]) + assert op.resource_params() == { + "base_class": re.ResourceQFT, + "base_params": base.resource_params(), + } + + def test_name(self): + """Test that the name of the compressed representation is correct""" + + base = re.ResourceQFT(wires=[0, 1, 2]) + op = re.ResourceControlled(base=base, control_wires=[3]) + assert op.resource_rep_from_op()._name == "Controlled(QFT)" + + +class TestResourcePow: + """Tests for ResourcePow""" + + def test_resource_params(self): + """Test that the resources are correct""" + + base = re.ResourceQFT(wires=[0, 1, 2]) + op = re.ResourcePow(base=base, z=5) + assert op.resource_params() == { + "base_class": re.ResourceQFT, + "z": 5, + "base_params": base.resource_params(), + } + + def test_name(self): + """Test that the name of the compressed representation is correct""" + + base = re.ResourceQFT(wires=[0, 1, 2]) + op = re.ResourcePow(base=base, z=5) + assert op.resource_rep_from_op()._name == "QFT**5" From 426fa5687be7c08ce74b0699a8914c209d7e5d7e Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 14:46:10 -0500 Subject: [PATCH 182/335] disable pylint warning --- .../labs/tests/resource_estimation/ops/op_math/test_symbolic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index 2d3e0834835..3704bf55865 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -17,7 +17,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=protected-access +# pylint: disable=protected-access,no-self-use class TestResourceAdjoint: From ec91220f884aa2047eedae84e0b8c98ac9658a43 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 14:51:31 -0500 Subject: [PATCH 183/335] update changelog --- doc/releases/changelog-dev.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 7b0be7afbf7..6e5b38704c7 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -182,6 +182,7 @@ Pietropaolo Frisoni, Austin Huang, Korbinian Kottmann, Christina Lee, +William Maxwell, Andrija Paurevic, Justin Pickering, Jay Soni, From 388d4ecb89a10b4c6239f6eb17783ae09c80c34d Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 14:54:09 -0500 Subject: [PATCH 184/335] update docstrings --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 4 ++-- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 4 ++-- pennylane/labs/resource_estimation/templates/subroutines.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index bfe6cd97849..f9f3fddc338 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -21,7 +21,7 @@ class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): - r"""Resource class for ControlledPhaseShift + r"""Resource class for the ControlledPhaseShift gate. Resources: The resources come from the following identity expressing Controlled Phase Shift @@ -62,7 +62,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCNOT(qml.CNOT, re.ResourceOperator): - """Resource class for CNOT""" + """Resource class for the CNOT gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 0e77b6da83e..e3eb9fd450e 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -21,7 +21,7 @@ class ResourceHadamard(qml.Hadamard, re.ResourceOperator): - """Resource class for Hadamard""" + """Resource class for the Hadamard gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: @@ -87,7 +87,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceT(qml.T, re.ResourceOperator): - """Resource class for T""" + """Resource class for the T gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 4dfa5747c28..4e1cd79beb7 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -27,7 +27,7 @@ class ResourceQFT(qml.QFT, ResourceOperator): - """Resource class for QFT + """Resource class for QFT. Resources: The resources are obtained from the textbook decomposition of QFT. See From dcc2f6b1ebc2c278f8300021baf66bf34935d749 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 18 Nov 2024 14:56:37 -0500 Subject: [PATCH 185/335] adding type annotations --- .../ops/qubit/parametric_ops_single_qubit.py | 4 ++-- .../labs/resource_estimation/templates/subroutines.py | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 8f6c95ba577..b22c5b7ce86 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -34,7 +34,7 @@ def _rotation_resources(epsilon=10e-3): class ResourceRZ(qml.RZ, re.ResourceOperator): - r"""Resource class for RZ + r"""Resource class for the RZ gate. Resources: The resources are estimated by approximating the gate with a series of T gates. @@ -45,7 +45,7 @@ class ResourceRZ(qml.RZ, re.ResourceOperator): def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rz"]) - def resource_params(self): + def resource_params(self) -> dict: return {} @classmethod diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 4e1cd79beb7..7f135898ed7 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -36,12 +36,6 @@ class ResourceQFT(qml.QFT, ResourceOperator): @staticmethod def _resource_decomp(num_wires, **kwargs) -> Dict[CompressedResourceOp, int]: - if not isinstance(num_wires, int): - raise TypeError("num_wires must be an int.") - - if num_wires < 1: - raise ValueError("num_wires must be greater than 0.") - gate_types = {} hadamard = ResourceHadamard.resource_rep() @@ -54,7 +48,7 @@ def _resource_decomp(num_wires, **kwargs) -> Dict[CompressedResourceOp, int]: return gate_types - def resource_params(self): + def resource_params(self) -> dict: return {"num_wires": len(self.wires)} @classmethod From 73c3cb03d10765cca1d76b2f37cb7cf20716d4ea Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 16:28:46 -0500 Subject: [PATCH 186/335] Apply suggestions from code review Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- .../labs/resource_estimation/resource_container.py | 2 +- .../labs/resource_estimation/resource_operator.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index 1b80b8caf87..23f8b4cc6ba 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -44,7 +44,7 @@ def __init__(self, op_type, params: dict) -> None: Args: op_type (Type): the class object for an operation which inherits from '~.ResourceOperator' params (dict): a dictionary containing the minimal pairs of parameter names and values - required to compute the resources for the given operator! + required to compute the resources for the given operator .. details:: diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 76917dbf9fd..1d74d0a95b9 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -22,7 +22,7 @@ class ResourceOperator(ABC): - r"""This is an abstract class that defines the methods a PennyLane Operator + r"""Abstract class that defines the methods a PennyLane Operator must implement in order to be used for resource estimation. .. details:: @@ -61,6 +61,15 @@ def resource_params(self, num_wires) -> dict: def resource_rep(cls, num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} return CompressedResourceOp(cls, params) + + Which can be instantiated as a normal operation, but now contains the resources: + + .. code-block:: bash + + >>> op = ResourceQFT(range(3)) + >>> op.resources(**op.resource_params()) + {Hadamard(): 3, SWAP(): 1, ControlledPhaseShift(): 3} + """ @staticmethod From 47e311882ca30cd1bea55626567fb0b8f06fb81f Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 16:37:34 -0500 Subject: [PATCH 187/335] fixing docs again --- pennylane/labs/__init__.py | 19 ++----------------- .../labs/resource_estimation/__init__.py | 18 ++++++++++++++++++ .../resource_estimation/resource_operator.py | 10 +++++----- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 1c7f2749c4b..69a41e878bb 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -17,23 +17,6 @@ This module contains experimental features enabling advanced quantum computing research. -.. warning:: - - This module is experimental. Frequent changes will occur, - with no guarantees of stability or backwards compatibility. - -.. currentmodule:: pennylane.labs.resource_estimation - -Resource Estimation Base Classes: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autosummary:: - :toctree: api - - ~Resources - ~CompressedResourceOp - ~ResourceOperator - .. currentmodule:: pennylane.labs Modules @@ -43,10 +26,12 @@ :toctree: api dla + resource_estimation """ from pennylane.labs import dla +from pennylane.labs import resource_estimation from .resource_estimation import ( Resources, CompressedResourceOp, diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 6cbe29d0b2a..c7ff3f8b952 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -14,6 +14,24 @@ r""" As part of the labs module, this module contains experimental features for resource estimation. + +.. warning:: + + This module is experimental. Frequent changes will occur, + with no guarantees of stability or backwards compatibility. + +.. currentmodule:: pennylane.labs.resource_estimation + +Resource Estimation Base Classes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autosummary:: + :toctree: api + + ~Resources + ~CompressedResourceOp + ~ResourceOperator + """ from .resource_operator import ResourceOperator diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 1d74d0a95b9..ec4e9dc6ec6 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -61,15 +61,15 @@ def resource_params(self, num_wires) -> dict: def resource_rep(cls, num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} return CompressedResourceOp(cls, params) - - Which can be instantiated as a normal operation, but now contains the resources: - + + Which can be instantiated as a normal operation, but now contains the resources: + .. code-block:: bash - + >>> op = ResourceQFT(range(3)) >>> op.resources(**op.resource_params()) {Hadamard(): 3, SWAP(): 1, ControlledPhaseShift(): 3} - + """ @staticmethod From 3d1df6333e15e0e8b8b7ebe8074feaa8e8d302f5 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 18 Nov 2024 19:04:50 -0500 Subject: [PATCH 188/335] format --- pennylane/labs/resource_estimation/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 55b6029bda3..1cd91dcd0bf 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -67,7 +67,7 @@ .. autosummary:: :toctree: api - + ~get_resources ~DefaultGateSet ~resource_config From 27e79509d7c61de1be56af006bfc43c336a236f2 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 19 Nov 2024 10:06:26 -0500 Subject: [PATCH 189/335] fixing init files --- pennylane/labs/__init__.py | 29 +++++-------------- .../labs/resource_estimation/__init__.py | 5 +++- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 2660e3ed3df..92eb83a2c99 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -14,36 +14,23 @@ r""" .. currentmodule:: pennylane -This module contains experimental features enabling +This module module contains experimental features enabling advanced quantum computing research. +.. warning:: + + This module is experimental. Frequent changes will occur, + with no guarantees of stability or backwards compatibility. + .. currentmodule:: pennylane.labs Modules -~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: :toctree: api - dla - resource_estimation """ -from pennylane.labs import dla -from pennylane.labs import resource_estimation - - -__all__ = [ - "Resources", - "CompressedResourceOp", - "ResourceOperator", - "ResourcesNotDefined", - "ResourceCNOT", - "ResourceControlledPhaseShift", - "ResourceHadamard", - "ResourceRZ", - "ResourceSWAP", - "ResourceT", - "ResourceQFT", -] +__all__ = [] diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index e2ef1b7065a..331d29d1630 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -38,9 +38,12 @@ .. autosummary:: :toctree: api + ~ResourceAdjoint ~ResourceCNOT + ~ResourceControlled ~ResourceControlledPhaseShift ~ResourceHadamard + ~ResourcePow ~ResourceRZ ~ResourceSWAP ~ResourceT @@ -70,8 +73,8 @@ ResourceCNOT, ResourceControlled, ResourceControlledPhaseShift, - ResourcePow, ResourceHadamard, + ResourcePow, ResourceRZ, ResourceSWAP, ResourceT, From d72838b28a1cbf9cff167463c1bd52b45e906a62 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 19 Nov 2024 10:08:04 -0500 Subject: [PATCH 190/335] fixing init again --- pennylane/labs/__init__.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py index 92eb83a2c99..2a0289f74c4 100644 --- a/pennylane/labs/__init__.py +++ b/pennylane/labs/__init__.py @@ -14,23 +14,24 @@ r""" .. currentmodule:: pennylane -This module module contains experimental features enabling +This module contains experimental features enabling advanced quantum computing research. -.. warning:: - - This module is experimental. Frequent changes will occur, - with no guarantees of stability or backwards compatibility. - .. currentmodule:: pennylane.labs Modules -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~ .. autosummary:: :toctree: api + dla + resource_estimation """ +from pennylane.labs import dla +from pennylane.labs import resource_estimation + + __all__ = [] From 1720416051205c6f0e57d6c3243364bbd971fb8c Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 19 Nov 2024 10:09:19 -0500 Subject: [PATCH 191/335] codefactor --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index d343bd358c2..1e8c40c49e8 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -63,7 +63,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls, **kwargs): return {cls.resource_rep(): 1} - + class ResourceCNOT(qml.CNOT, re.ResourceOperator): """Resource class for the CNOT gate.""" From 940287dfa308621fdd1cc13dcd0fe04605ce2f57 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 19 Nov 2024 10:35:14 -0500 Subject: [PATCH 192/335] formatting --- pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 1e8c40c49e8..686ffb4fead 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -64,6 +64,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(cls, **kwargs): return {cls.resource_rep(): 1} + class ResourceCNOT(qml.CNOT, re.ResourceOperator): """Resource class for the CNOT gate.""" From a9c6fcd49cc924cadd1a71c350b8939541cbdd79 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 19 Nov 2024 10:58:53 -0500 Subject: [PATCH 193/335] reverting changes --- .../templates/test_resource_qft.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 761d30e5f3b..8bb4b1ef0ef 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -78,15 +78,3 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph actual = rep.op_type.resources(**rep.params) assert actual == expected - - @pytest.mark.parametrize("num_wires", [2.5, -0.5]) - def test_type_error(self, num_wires): - """Test that resources correctly raises a TypeError""" - with pytest.raises(TypeError, match="num_wires must be an int."): - re.ResourceQFT.resources(num_wires) - - @pytest.mark.parametrize("num_wires", [0, -1]) - def test_value_error(self, num_wires): - """Test that resources correctly raises a ValueError""" - with pytest.raises(ValueError, match="num_wires must be greater than 0."): - re.ResourceQFT.resources(num_wires) From 90ebacb807bc0b06e2daeb2366a7729f0d319f3e Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 19 Nov 2024 11:46:51 -0500 Subject: [PATCH 194/335] Apply suggestions from code review --- doc/releases/changelog-dev.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 06aa8a3f08c..107a294a7a9 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -69,14 +69,12 @@ * Added base class `Resources`, `CompressedResourceOp`, `ResourceOperator` for advanced resource estimation. [(#6428)](https://github.com/PennyLaneAI/pennylane/pull/6428) -<<<<<<< HEAD * Added `get_resources()` functionality which allows users to extract resources from a quantum function, tape or resource operation. Additionally added some standard gatesets `DefaultGateSet` to track resources with respect to. [(#6500)](https://github.com/PennyLaneAI/pennylane/pull/6500) -======= + * Added `ResourceOperator` classes for QFT and all operators in QFT's decomposition. [(#6447)](https://github.com/PennyLaneAI/pennylane/pull/6447) ->>>>>>> resource_single_qubit

Breaking changes 💔

From 52f3ebcf5acdfb464df24e4c6f3d0af142d2e28c Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 19 Nov 2024 15:09:35 -0500 Subject: [PATCH 195/335] adjoint/pow methods for QFT dependencies --- .../ops/op_math/controlled_ops.py | 8 ++++++++ .../ops/qubit/non_parametric_ops.py | 16 +++++++++++++++- .../ops/qubit/parametric_ops_single_qubit.py | 8 ++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 686ffb4fead..4ed1983c46e 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -78,3 +78,11 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(**kwargs) + + @classmethod + def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 4cf867da6a7..64ee57fdd32 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -90,9 +90,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, **kwargs): + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} + @classmethod + def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceT(qml.T, re.ResourceOperator): """Resource class for the T gate.""" @@ -107,3 +111,13 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + """Resources obtained from the identity T^8 = I.""" + return {cls.resource_rep(): 7} + + @classmethod + def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + """Resources obtained from the identity T^8 = I.""" + return {cls.resource_rep(): z % 8} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index b22c5b7ce86..62197a6a7ef 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -51,3 +51,11 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls, config): + return cls.resources(config) + + @classmethod + def pow_resource_decomp(cls, z, config): + return cls.resources(config) From 4eebd8a8227634b1917b5d266852253fcda91c6b Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 10:32:01 -0500 Subject: [PATCH 196/335] update controlled --- .../labs/resource_estimation/ops/op_math/symbolic.py | 10 +++++----- .../labs/resource_estimation/resource_operator.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index b17efb0a3cb..fe8e4e5bc46 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -55,9 +55,9 @@ class ResourceControlled(ControlledOp, re.ResourceOperator): """Resource class for Controlled""" @staticmethod - def _resource_decomp(base_class, base_params, **kwargs): + def _resource_decomp(base_class, base_params, num_ctrl_wires, **kwargs): try: - return base_class.controlled_resource_decomp(**base_params) + return base_class.controlled_resource_decomp(num_ctrl_wires, **base_params) except re.ResourcesNotDefined: gate_types = defaultdict(int) decomp = base_class.resources(**base_params) @@ -72,10 +72,10 @@ def resource_params(self): return {"base_class": type(self.base), "base_params": self.base.resource_params()} @classmethod - def resource_rep(cls, base_class, base_params, **kwargs): - name = f"Controlled({base_class.__name__})".replace("Resource", "") + def resource_rep(cls, base_class, base_params, num_ctrl_wires, **kwargs): + name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( - cls, {"base_class": base_class, "base_params": base_params}, name=name + cls, {"base_class": base_class, "base_params": base_params, "num_ctrl_wires": num_ctrl_wires}, name=name ) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 8cf2aec989e..8a68bcf4fe6 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -110,7 +110,7 @@ def adjoint_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, raise ResourcesNotDefined @classmethod - def controlled_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, int]: + def controlled_resource_decomp(cls, num_ctrl_wires, *args, **kwargs) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the controlled version of the operator""" raise ResourcesNotDefined From 37bb4227d0337839baf12b29c7f9c20b57a17f37 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 11:21:01 -0500 Subject: [PATCH 197/335] soran's comments --- .../ops/qubit/non_parametric_ops.py | 8 ++++---- .../ops/qubit/parametric_ops_single_qubit.py | 20 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 8eff6b8dbff..7a3511022d4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -36,7 +36,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceS(qml.S, re.ResourceOperator): - """Resource class for S""" + """Resource class for the S gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: @@ -121,7 +121,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceX(qml.X, re.ResourceOperator): - """Resource class for X""" + """Resource class for the X gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: @@ -143,7 +143,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceY(qml.Y, re.ResourceOperator): - """Resource class for Y""" + """Resource class for the Y gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: @@ -165,7 +165,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceZ(qml.Z, re.ResourceOperator): - """Resource class for Z""" + """Resource class for the Z gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 5816f0ee410..572c12f9ebd 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -34,15 +34,15 @@ def _rotation_resources(epsilon=10e-3): class ResourcePhaseShift(qml.PhaseShift, re.ResourceOperator): - r"""Resource class for PhaseShift + r""" + Resource class for the PhaseShift gate. - Resources: - The resources are defined from the following identity: + The resources are defined from the following identity: - .. math:: R_\phi(\phi) = e^{i\phi/2}R_z(\phi) = \begin{bmatrix} - 1 & 0 \\ - 0 & e^{i\phi} - \end{bmatrix}. + .. math:: R_\phi(\phi) = e^{i\phi/2}R_z(\phi) = \begin{bmatrix} + 1 & 0 \\ + 0 & e^{i\phi} + \end{bmatrix}. """ @staticmethod @@ -64,7 +64,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceRX(qml.RX, re.ResourceOperator): - """Resource class for RX""" + """Resource class for the RX gate.""" @staticmethod def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: @@ -79,7 +79,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceRY(qml.RY, re.ResourceOperator): - """Resource class for RY""" + """Resource class for the RY gate.""" @staticmethod def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: @@ -114,7 +114,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceRot(qml.Rot, re.ResourceOperator): - """Resource class for Rot""" + """Resource class for the Rot gate.""" @staticmethod def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: From a2786df68c8a9c5f5780170a3e8b5065647cafa7 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 11:32:54 -0500 Subject: [PATCH 198/335] adding phase shift to docs --- pennylane/labs/resource_estimation/__init__.py | 2 ++ .../labs/resource_estimation/ops/__init__.py | 1 + .../labs/resource_estimation/ops/identity.py | 4 ++-- .../resource_estimation/ops/qubit/__init__.py | 18 ++---------------- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 4241466a311..3c7f2a97861 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -43,6 +43,7 @@ ~ResourceGlobalPhase ~ResourceHadamard ~ResourceIdentity + ~ResourcePhaseShift ~ResourceRot ~ResourceRX ~ResourceRY @@ -80,6 +81,7 @@ ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, + ResourcePhaseShift, ResourceRot, ResourceRX, ResourceRY, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 0b5f06d15e1..23d45a734f5 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -24,6 +24,7 @@ ResourceRX, ResourceRY, ResourceRZ, + ResourcePhaseShift, ResourceS, ResourceSWAP, ResourceT, diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index cd7f23042f5..7f9b59729db 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -21,7 +21,7 @@ class ResourceIdentity(qml.Identity, re.ResourceOperator): - """Resource class for Identity""" + """Resource class for the Identity gate.""" @staticmethod def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: @@ -36,7 +36,7 @@ def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): - """Resource class for GlobalPhase""" + """Resource class for the GlobalPhase gate.""" @staticmethod def _resource_decomp(*args, **kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index 8ee2922e014..429671eae0b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -13,19 +13,5 @@ # limitations under the License. r"""This module contains experimental resource estimation functionality. """ -from .non_parametric_ops import ( - ResourceHadamard, - ResourceS, - ResourceSWAP, - ResourceT, - ResourceX, - ResourceY, - ResourceZ, -) - -from .parametric_ops_single_qubit import ( - ResourceRot, - ResourceRX, - ResourceRY, - ResourceRZ, -) +from .non_parametric_ops import * +from .parametric_ops_single_qubit import * From 6323074fd92c8ae11a076be072062f571284281e Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 11:50:35 -0500 Subject: [PATCH 199/335] fix controlled to pass unit test --- pennylane/labs/resource_estimation/ops/op_math/symbolic.py | 2 +- .../tests/resource_estimation/ops/op_math/test_symbolic.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index fe8e4e5bc46..c4471ac0ddc 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -69,7 +69,7 @@ def _resource_decomp(base_class, base_params, num_ctrl_wires, **kwargs): return gate_types def resource_params(self): - return {"base_class": type(self.base), "base_params": self.base.resource_params()} + return {"base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires)} @classmethod def resource_rep(cls, base_class, base_params, num_ctrl_wires, **kwargs): diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index 3704bf55865..eac3387f93e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -52,6 +52,7 @@ def test_resource_params(self): assert op.resource_params() == { "base_class": re.ResourceQFT, "base_params": base.resource_params(), + "num_ctrl_wires": 1, } def test_name(self): @@ -59,7 +60,7 @@ def test_name(self): base = re.ResourceQFT(wires=[0, 1, 2]) op = re.ResourceControlled(base=base, control_wires=[3]) - assert op.resource_rep_from_op()._name == "Controlled(QFT)" + assert op.resource_rep_from_op()._name == "Controlled(QFT, wires=1)" class TestResourcePow: From 4d43c39cb54c672e70f9302c2add7207512affce Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 13:47:03 -0500 Subject: [PATCH 200/335] formatting --- .../resource_estimation/ops/op_math/symbolic.py | 14 ++++++++++++-- .../labs/resource_estimation/resource_operator.py | 4 +++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index c4471ac0ddc..6abd7517a8d 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -69,13 +69,23 @@ def _resource_decomp(base_class, base_params, num_ctrl_wires, **kwargs): return gate_types def resource_params(self): - return {"base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires)} + return { + "base_class": type(self.base), + "base_params": self.base.resource_params(), + "num_ctrl_wires": len(self.control_wires), + } @classmethod def resource_rep(cls, base_class, base_params, num_ctrl_wires, **kwargs): name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( - cls, {"base_class": base_class, "base_params": base_params, "num_ctrl_wires": num_ctrl_wires}, name=name + cls, + { + "base_class": base_class, + "base_params": base_params, + "num_ctrl_wires": num_ctrl_wires, + }, + name=name, ) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 8a68bcf4fe6..80a3f795c25 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -110,7 +110,9 @@ def adjoint_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, raise ResourcesNotDefined @classmethod - def controlled_resource_decomp(cls, num_ctrl_wires, *args, **kwargs) -> Dict[CompressedResourceOp, int]: + def controlled_resource_decomp( + cls, num_ctrl_wires, *args, **kwargs + ) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the controlled version of the operator""" raise ResourcesNotDefined From 2d153dbfc8fbe3e6c0f80b559e5302a36aaafc98 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 14:18:51 -0500 Subject: [PATCH 201/335] add CRY to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 4241466a311..dd2c9d6490c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -40,6 +40,7 @@ ~ResourceCNOT ~ResourceControlledPhaseShift + ~ResourceCRY ~ResourceGlobalPhase ~ResourceHadamard ~ResourceIdentity @@ -77,6 +78,7 @@ from .ops import ( ResourceCNOT, ResourceControlledPhaseShift, + ResourceCRY, ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, From cbc2fec452ebaa247d58c2202935ef405617fbae Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 15:20:32 -0500 Subject: [PATCH 202/335] adding resource methods --- .../labs/resource_estimation/__init__.py | 23 +++ .../labs/resource_estimation/ops/__init__.py | 14 ++ .../ops/qubit/parametric_ops_multi_qubit.py | 16 +- .../ops/qubit/qchem_ops.py | 147 ++++++++++++++---- 4 files changed, 167 insertions(+), 33 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index f71336601e0..27c8ab862c1 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -40,15 +40,24 @@ ~ResourceCNOT ~ResourceControlledPhaseShift + ~ResourceDoubleExcitation, + ~ResourceDoubleExcitationMinus, + ~ResourceDoubleExcitationPlus, ~ResourceCRY + ~ResourceFermionicSWAP ~ResourceGlobalPhase ~ResourceHadamard ~ResourceIdentity + ~ResourceMultiRZ + ~ResourceOrbitalRotation ~ResourceRot ~ResourceRX ~ResourceRY ~ResourceRZ ~ResourceS + ~ResourceSingleExcitation + ~ResourceSingleExcitationMinus + ~ResourceSingleExcitationPlus, ~ResourceSWAP ~ResourceT ~ResourceX @@ -79,15 +88,29 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceCRY, + ResourceDoubleExcitation, + ResourceDoubleExcitationMinus, + ResourceDoubleExcitationPlus, + ResourceFermionicSWAP, ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, + ResourceIsingXX, + ResourceIsingXY, + ResourceIsingYY, + ResourceIsingZZ, ResourceMultiRZ, + ResourceOrbitalRotation, + ResourcePauliRot, + ResourcePSWAP, ResourceRot, ResourceRX, ResourceRY, ResourceRZ, ResourceS, + ResourceSingleExcitation, + ResourceSingleExcitationMinus, + ResourceSingleExcitationPlus, ResourceSWAP, ResourceT, ResourceX, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index e806150d54e..76ff9937337 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -19,14 +19,28 @@ ) from .qubit import ( + ResourceDoubleExcitation, + ResourceDoubleExcitationMinus, + ResourceDoubleExcitationPlus, + ResourceFermionicSWAP, ResourceHadamard, + ResourceIsingXX, + ResourceIsingXY, + ResourceIsingYY, + ResourceIsingZZ, ResourceMultiRZ, + ResourceOrbitalRotation, + ResourcePauliRot, ResourcePhaseShift, + ResourcePSWAP, ResourceRot, ResourceRX, ResourceRY, ResourceRZ, ResourceS, + ResourceSingleExcitation, + ResourceSingleExcitationMinus, + ResourceSingleExcitationPlus, ResourceSWAP, ResourceT, ResourceX, diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index f29773f47a0..5fdf66470bc 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -1,7 +1,8 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -#pylint: disable=arguments-differ +# pylint: disable=arguments-differ + class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): r"""Resource class for MultiRZ @@ -18,7 +19,7 @@ def _resource_decomp(num_wires, **kwargs): rz = re.CompressedResourceOp(re.ops.ResourceRZ, {}) gate_types = {} - gate_types[cnot] = 2*(num_wires-1) + gate_types[cnot] = 2 * (num_wires - 1) gate_types[rz] = 1 return gate_types @@ -30,6 +31,7 @@ def resource_params(self): def resource_rep(cls, num_wires, **kwargs): return re.CompressedResourceOp(cls, {"num_wires": num_wires}) + class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): r"""Resource class for PauliRot @@ -74,7 +76,10 @@ def resource_params(self): @classmethod def resource_rep(cls, active_wires, pauli_word, **kwargs): - return re.CompressedResourceOp(cls, {"active_wires": active_wires, "pauli_word": pauli_word}) + return re.CompressedResourceOp( + cls, {"active_wires": active_wires, "pauli_word": pauli_word} + ) + class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): r"""Resource class for IsingXX @@ -91,7 +96,6 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): \end{bmatrix}. """ - @staticmethod def _resource_decomp(*args, **kwargs): cnot = re.ResourceCNOT.resource_rep(**kwargs) @@ -108,6 +112,7 @@ def resource_params(self): def resource_rep(cls, *args, **kwargs): return re.CompressedResourceOp(cls, {}) + class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): r"""Resource class for IsingYY @@ -141,6 +146,7 @@ def resource_params(self): def resource_rep(cls, *args, **kwargs): return re.CompressedResourceOp(cls, {}) + class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): r"""Resource class for IsingXY @@ -178,6 +184,7 @@ def resource_params(self): def resource_rep(cls, *args, **kwargs): return re.CompressedResourceOp(cls, {}) + class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): r"""Resource class for IsingZZ @@ -211,6 +218,7 @@ def resource_params(self): def resource_rep(cls, *args, **kwargs): return re.CompressedResourceOp(cls, {}) + class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): r"""Resource class for PSWAP diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index dd0d31f6650..cc95f898b0f 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -1,57 +1,119 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for qchem operations.""" import pennylane as qml -from pennylane.labs.resource_estimation import ResourceOperator +import pennylane.labs.resource_estimation as re -class ResourceSingleExcitation(qml.SingleExcitation, ResourceOperator): - """Resource Operator for Single Excitation""" +# pylint: disable=arguments-differ + + +class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): + """Resource class for the SingleExcitation gate.""" @staticmethod def _resource_decomp(*args, **kwargs): - return + """TODO: implement in resource_symbolic_ops branch""" + raise re.ResourcesNotDefined def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) + + +class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperator): + """Resource class for the SingleExcitationMinus gate.""" -class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - return + x = re.ResourceX.resource_rep(**kwargs) + ctrl_phase_shift = re.ResourceControlledPhaseShift.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep(**kwargs) + cry = re.ResourceCRY.resource_rep(**kwargs) + + gate_types = {} + gate_types[x] = 4 + gate_types[ctrl_phase_shift] = 2 + gate_types[cnot] = 2 + gate_types[cry] = 1 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) + + +class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator): + """Resource class for the SingleExcitationPlus gate.""" -class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - return + x = re.ResourceX.resource_rep(**kwargs) + ctrl_phase_shift = re.ResourceControlledPhaseShift.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep(**kwargs) + cry = re.ResourceCRY.resource_rep(**kwargs) + + gate_types = {} + gate_types[x] = 4 + gate_types[ctrl_phase_shift] = 2 + gate_types[cnot] = 2 + gate_types[cry] = 1 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) + + +class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): + """Resource class for the DoubleExcitation gate.""" -class ResourceDoubleExcitation(qml.DoubleExcitation, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - return + """See https://arxiv.org/abs/2104.05695""" + h = re.ResourceHadamard.resource_rep(**kwargs) + ry = re.ResourceX.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep(**kwargs) + + gate_types = {} + gate_types[h] = 6 + gate_types[ry] = 8 + gate_types[cnot] = 14 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) + + +class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator): + """Resource class for the DoubleExcitationMinus gate.""" -class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): return @@ -63,7 +125,10 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, ResourceOperator): + +class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator): + """Resource class for the DoubleExcitationPlus gate.""" + @staticmethod def _resource_decomp(*args, **kwargs): return @@ -75,26 +140,50 @@ def resource_params(self): def resource_rep(cls, **kwargs): return -class ResourceOrbitalRotation(qml.OrbitalRotation, ResourceOperator): + +class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): + """Resource class for the OrbitalRotation gate.""" + @staticmethod - def _resource_decomp(*args, **kwargs): - return + def _resource_decomp(**kwargs): + fermionic_swap = re.ResourceFermionicSWAP.resource_rep(**kwargs) + single_excitation = re.ResourceSingleExcitation.resource_rep(**kwargs) + + gate_types = {} + gate_types[fermionic_swap] = 2 + gate_types[single_excitation] = 2 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) + + +class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): + """Resource class for the FermionicSWAP gate.""" -class ResourceFermionicSWAP(qml.FermionicSWAP, ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - return + h = re.ResourceHadamard.resource_rep(**kwargs) + multi_rz = re.ResourceMultiRZ.resource_rep(num_wires=2, **kwargs) + rx = re.ResourceRX.resource_rep(**kwargs) + rz = re.ResourceRZ.resource_rep(**kwargs) + + gate_types = {} + gate_types[h] = 4 + gate_types[multi_rz] = 2 + gate_types[rx] = 4 + gate_types[rz] = 2 + + return gate_types def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) From 93c86e98fc6f80bd528e72e67bdd0e7286a7f1c7 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 16:09:16 -0500 Subject: [PATCH 203/335] raise resources not implemented --- .../labs/resource_estimation/ops/qubit/qchem_ops.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index cc95f898b0f..81f8e311047 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -116,14 +116,14 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat @staticmethod def _resource_decomp(*args, **kwargs): - return + """TODO: implement in resource_symbolic_op branch""" def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator): @@ -131,14 +131,15 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator @staticmethod def _resource_decomp(*args, **kwargs): - return + """TODO: implement in resource_symbolic_op branch""" + raise re.ResourcesNotDefined def resource_params(self): - return + return {} @classmethod def resource_rep(cls, **kwargs): - return + return re.CompressedResourceOp(cls, {}) class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): From 905c210985beb8d8ba72f56164d9ea48b6eaa7d6 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 16:31:01 -0500 Subject: [PATCH 204/335] revert changes from merge conflict --- pennylane/math/multi_dispatch.py | 35 ----------------------------- pennylane/workflow/qnode.py | 10 +++------ tests/capture/test_capture_qnode.py | 6 ----- 3 files changed, 3 insertions(+), 48 deletions(-) diff --git a/pennylane/math/multi_dispatch.py b/pennylane/math/multi_dispatch.py index 61cf3b3d969..585e3927198 100644 --- a/pennylane/math/multi_dispatch.py +++ b/pennylane/math/multi_dispatch.py @@ -23,8 +23,6 @@ from autoray import numpy as np from numpy import ndarray -import pennylane as qml - from . import single_dispatch # pylint:disable=unused-import from .utils import cast, cast_like, get_interface, requires_grad @@ -1006,39 +1004,6 @@ def detach(tensor, like=None): return tensor -def jax_argnums_to_tape_trainable(qnode, argnums, program, args, kwargs): - """This functions gets the tape parameters from the QNode construction given some argnums (only for Jax). - The tape parameters are transformed to JVPTracer if they are from argnums. This function imitates the behaviour - of Jax in order to mark trainable parameters. - - Args: - qnode(qml.QNode): the quantum node. - argnums(int, list[int]): the parameters that we want to set as trainable (on the QNode level). - program(qml.transforms.core.TransformProgram): the transform program to be applied on the tape. - - - Return: - list[float, jax.JVPTracer]: List of parameters where the trainable one are `JVPTracer`. - """ - import jax - - with jax.core.new_main(jax.interpreters.ad.JVPTrace) as main: - trace = jax.interpreters.ad.JVPTrace(main, 0) - - args_jvp = [ - ( - jax.interpreters.ad.JVPTracer(trace, arg, jax.numpy.zeros(arg.shape)) - if i in argnums - else arg - ) - for i, arg in enumerate(args) - ] - - tape = qml.workflow.construct_tape(qnode, level=0)(*args_jvp, **kwargs) - tapes, _ = program((tape,)) - del trace - return tuple(tape.get_parameters(trainable_only=False) for tape in tapes) - @multi_dispatch(tensor_list=[1]) def set_index(array, idx, val, like=None): """Set the value at a specified index in an array. diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py index cab8782fcf4..3c5c5469c96 100644 --- a/pennylane/workflow/qnode.py +++ b/pennylane/workflow/qnode.py @@ -991,17 +991,13 @@ def _execution_component(self, args: tuple, kwargs: dict) -> qml.typing.Result: full_transform_program = qml.transforms.core.TransformProgram(self.transform_program) inner_transform_program = qml.transforms.core.TransformProgram() - # Add the gradient expand to the program if necessary - if getattr(gradient_fn, "expand_transform", False): + if getattr(config.gradient_method, "expand_transform", False): full_transform_program.add_transform( - qml.transform(gradient_fn.expand_transform), - **gradient_kwargs, + qml.transform(config.gradient_method.expand_transform), + **config.gradient_keyword_arguments, ) - config = _make_execution_config(self, gradient_fn, mcm_config) - device_transform_program, config = self.device.preprocess(execution_config=config) - if config.use_device_gradient: full_transform_program += device_transform_program else: diff --git a/tests/capture/test_capture_qnode.py b/tests/capture/test_capture_qnode.py index 683012ac985..41b1854956f 100644 --- a/tests/capture/test_capture_qnode.py +++ b/tests/capture/test_capture_qnode.py @@ -29,12 +29,6 @@ # must be below jax importorskip from pennylane.capture.primitives import qnode_prim # pylint: disable=wrong-import-position -@pytest.fixture(autouse=True) -def enable_disable_plxpr(): - """Enable and disable the PennyLane JAX capture context around each test.""" - qml.capture.enable() - yield - qml.capture.disable() def get_qnode_output_eqns(jaxpr): """Extracts equations related to QNode outputs in the given JAX expression (jaxpr). From 86dd9e5d65d8c40d0933a264b107a6ee3d54dfb6 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 16:31:35 -0500 Subject: [PATCH 205/335] more merge conflict issues --- doc/releases/changelog-dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 9bef4594a49..49e413da003 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -82,6 +82,10 @@

Breaking changes 💔

+* `qml.math.jax_argnums_to_tape_trainable` is moved and made private to avoid a qnode dependency + in the math module. + [(#6609)](https://github.com/PennyLaneAI/pennylane/pull/6609) + * Gradient transforms are now applied after the user's transform program. [(#6590)](https://github.com/PennyLaneAI/pennylane/pull/6590) From a616fa5b518b286995c2be88ccbe2dc98faf26e2 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 16:32:46 -0500 Subject: [PATCH 206/335] final revert --- pennylane/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/_version.py b/pennylane/_version.py index 56ef428f264..3b74a713655 100644 --- a/pennylane/_version.py +++ b/pennylane/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev17" \ No newline at end of file +__version__ = "0.40.0-dev18" From be1498f3292861acfb9a8857c7b6277bb475de49 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 16:57:16 -0500 Subject: [PATCH 207/335] formatting --- pennylane/labs/resource_estimation/ops/__init__.py | 1 - .../labs/resource_estimation/ops/op_math/controlled_ops.py | 6 +++--- pennylane/labs/resource_estimation/ops/qubit/__init__.py | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 186c5d6f5b2..76ff9937337 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -37,7 +37,6 @@ ResourceRX, ResourceRY, ResourceRZ, - ResourcePhaseShift, ResourceS, ResourceSingleExcitation, ResourceSingleExcitationMinus, diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 6bfb47aadb2..499fd935fbc 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -320,7 +320,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRX(qml.CRX, re.ResourceOperator): - """Resource class for CRX + r"""Resource class for CRX Resources: @@ -360,7 +360,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRY(qml.CRY, re.ResourceOperator): - """Resource class for CRY + r"""Resource class for CRY Resources: The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit @@ -391,7 +391,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRZ(qml.CRZ, re.ResourceOperator): - """Resource class for CRZ + r"""Resource class for CRZ Resources: The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit diff --git a/pennylane/labs/resource_estimation/ops/qubit/__init__.py b/pennylane/labs/resource_estimation/ops/qubit/__init__.py index 429671eae0b..5741c2007c8 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/__init__.py +++ b/pennylane/labs/resource_estimation/ops/qubit/__init__.py @@ -14,4 +14,6 @@ r"""This module contains experimental resource estimation functionality. """ from .non_parametric_ops import * +from .parametric_ops_multi_qubit import * from .parametric_ops_single_qubit import * +from .qchem_ops import * From dcea65b0a0ce037ee50c403250762ff8668bc8d2 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 20 Nov 2024 17:05:14 -0500 Subject: [PATCH 208/335] formatting --- .../labs/resource_estimation/__init__.py | 4 +++ .../ops/op_math/controlled_ops.py | 34 +++++++++---------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index f7d39c24e9c..ed59378d9cb 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -41,6 +41,7 @@ ~ResourceCNOT ~ResourceControlledPhaseShift ~ResourceCRY + ~ResourceCZ ~ResourceGlobalPhase ~ResourceHadamard ~ResourceIdentity @@ -52,6 +53,7 @@ ~ResourceS ~ResourceSWAP ~ResourceT + ~ResourceToffoli ~ResourceX ~ResourceY ~ResourceZ @@ -80,6 +82,7 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceCRY, + ResourceCZ, ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, @@ -91,6 +94,7 @@ ResourceS, ResourceSWAP, ResourceT, + ResourceToffoli, ResourceX, ResourceY, ResourceZ, diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 6bfb47aadb2..56f8b008219 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -22,19 +22,19 @@ class ResourceCH(qml.CH, re.ResourceOperator): r"""Resource class for CH gate. - + Resources: The resources are derived from the following identities: - - .. math:: - + + .. math:: + \begin{align} \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \dot \hat{Z} \dot \hat{R}_{y}(\frac{-\pi}{4}), \\ \hat{Z} &= \hat{H} \dot \hat{X} \dot \hat{H} \end{align} - - We can control on the Pauli-X gate to obtain our controlled Hadamard gate. + + We can control on the Pauli-X gate to obtain our controlled Hadamard gate. """ @@ -218,7 +218,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceToffoli(qml.Toffoli, re.ResourceOperator): - """Resource class for Toffoli + r"""Resource class for Toffoli Resources: The resources are obtained from (in figure 1.) the paper `Novel constructions for the fault-tolerant @@ -258,7 +258,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: @staticmethod def textbook_resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - """Resources for the Toffoli gate + r"""Resources for the Toffoli gate Resources: The resources are taken (figure 4.9) from the textbook `Quantum Computation and Quantum Information @@ -294,7 +294,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): - """Resource class for MultiControlledX + r"""Resource class for MultiControlledX Resources: The resources are obtained from (table 3.) the paper `Polylogarithmic-depth controlled-NOT gates @@ -320,21 +320,21 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRX(qml.CRX, re.ResourceOperator): - """Resource class for CRX + r"""Resource class for CRX Resources: The resources are derived from the following identities: - .. math:: - + .. math:: + \begin{align} \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}, \\ \hat{X} &= \hat{H} \dot \hat{Z} \dot \hat{H} \end{align} - The expression for controlled-RZ gates is used as defined in (figure 1b.) the paper - `T-count and T-depth of any multi-qubit unitary `_. + The expression for controlled-RZ gates is used as defined in (figure 1b.) the paper + `T-count and T-depth of any multi-qubit unitary `_. """ @staticmethod @@ -360,7 +360,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRY(qml.CRY, re.ResourceOperator): - """Resource class for CRY + r"""Resource class for CRY Resources: The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit @@ -391,7 +391,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRZ(qml.CRZ, re.ResourceOperator): - """Resource class for CRZ + r"""Resource class for CRZ Resources: The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit @@ -422,7 +422,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceCRot(qml.CRot, re.ResourceOperator): - """Resource class for CRot + r"""Resource class for CRot Resources: TODO: Add a source for resources! From 4f182e35788b66b95208052833dcb983503ece28 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 08:07:31 -0500 Subject: [PATCH 209/335] unused argument --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 56f8b008219..0b3bfcb21bb 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -257,7 +257,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: return gate_types @staticmethod - def textbook_resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + def textbook_resource_decomp() -> Dict[re.CompressedResourceOp, int]: r"""Resources for the Toffoli gate Resources: From 9e42ab8d543b496673991f5c075da301939d6536 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 08:13:40 -0500 Subject: [PATCH 210/335] fixing merge error --- pennylane/labs/resource_estimation/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 1e4e3353c0f..ac8aae59a8c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -91,14 +91,11 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceCRY, -<<<<<<< HEAD + ResourceCZ, ResourceDoubleExcitation, ResourceDoubleExcitationMinus, ResourceDoubleExcitationPlus, ResourceFermionicSWAP, -======= - ResourceCZ, ->>>>>>> dcea65b0a0ce037ee50c403250762ff8668bc8d2 ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, From e5628138eea23f9d7f1bd322650c99b3029f09e5 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 08:24:01 -0500 Subject: [PATCH 211/335] docstring --- .../ops/qubit/parametric_ops_multi_qubit.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 5fdf66470bc..158280f533f 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -1,3 +1,17 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +r"""Resource operators for parametric multi qubit operations.""" import pennylane as qml import pennylane.labs.resource_estimation as re From 7ad4e2a51c8382cf9faebf1818b04725cb17fbff Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 08:26:27 -0500 Subject: [PATCH 212/335] formatting and syntax error --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 3 ++- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index bfd3191e30d..13b8f67c0ff 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -202,7 +202,7 @@ def adjoint_resource_decomp(cls, **kwargs): class ResourceCNOT(qml.CNOT, re.ResourceOperator): - """Resource class for the CNOT gate.""" + """Resource class for the CNOT gate. Resources: There is no further decomposition provided for this gate. @@ -454,6 +454,7 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for the ControlledPhaseShift gate. diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 5960bf28dfa..56fd48bbe0b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -141,6 +141,7 @@ def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: """Resources obtained from the identity T^8 = I.""" return {cls.resource_rep(): z % 8} + class ResourceX(qml.X, re.ResourceOperator): """Resource class for the X gate.""" @@ -202,4 +203,4 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: - return re.CompressedResourceOp(cls, {}) \ No newline at end of file + return re.CompressedResourceOp(cls, {}) From 02658cb813b5b38e8d593472c6731247e4c0713e Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 09:43:20 -0500 Subject: [PATCH 213/335] double excitation --- .../ops/op_math/symbolic.py | 4 ++- .../ops/qubit/qchem_ops.py | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 6abd7517a8d..45105512e4d 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -73,10 +73,11 @@ def resource_params(self): "base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires), + "num_zeros": len([val for val in self.control_values if not val]) } @classmethod - def resource_rep(cls, base_class, base_params, num_ctrl_wires, **kwargs): + def resource_rep(cls, base_class, base_params, num_ctrl_wires, num_zeros, **kwargs): name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( cls, @@ -84,6 +85,7 @@ def resource_rep(cls, base_class, base_params, num_ctrl_wires, **kwargs): "base_class": base_class, "base_params": base_params, "num_ctrl_wires": num_ctrl_wires, + "num_zeros": num_zeros, }, name=name, ) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 81f8e311047..cd42e35d728 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -116,7 +116,18 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" + phase = re.ResourceGlobalPhase.resource_rep() + double = re.ResourceDoubleExcitation.resource_rep() + ctrl_z = re.ResourceControlled(re.ResourceZ, {}, 3, 1) + ctrl_phase = re.ResourceControlled(re.ResourcePhaseShift, {}, 3, 1) + + gate_types = {} + gate_types[phase] = 1 + gate_types[double] = 1 + gate_types[ctrl_z] = 2 + gate_types[ctrl_phase] = 2 + + return gate_types def resource_params(self): return {} @@ -131,8 +142,18 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" - raise re.ResourcesNotDefined + phase = re.ResourceGlobalPhase.resource_rep() + double = re.ResourceDoubleExcitation.resource_rep() + ctrl_z = re.ResourceControlled(re.ResourceZ, {}, 3, 1) + ctrl_phase = re.ResourceControlled(re.ResourcePhaseShift, {}, 3, 1) + + gate_types = {} + gate_types[phase] = 1 + gate_types[double] = 1 + gate_types[ctrl_z] = 2 + gate_types[ctrl_phase] = 2 + + return gate_types def resource_params(self): return {} From ceab03ce162605762d586d9723f166d2e8055564 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 13:00:24 -0500 Subject: [PATCH 214/335] add ResourceCY to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index ed59378d9cb..d969eb02556 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -41,6 +41,7 @@ ~ResourceCNOT ~ResourceControlledPhaseShift ~ResourceCRY + ~ResourceCY ~ResourceCZ ~ResourceGlobalPhase ~ResourceHadamard @@ -82,6 +83,7 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceCRY, + ResourceCY, ResourceCZ, ResourceGlobalPhase, ResourceHadamard, From c532beaebd2a5c52406ecfd3a3b441f79a1d00ff Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 13:12:16 -0500 Subject: [PATCH 215/335] tests for parametric_ops_multi_qubit --- .../ops/qubit/parametric_ops_multi_qubit.py | 36 +-- .../qubit/test_parametric_ops_multi_qubit.py | 286 ++++++++++++++++++ 2 files changed, 305 insertions(+), 17 deletions(-) create mode 100644 pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 158280f533f..2e2d62e4ae1 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -29,8 +29,8 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): @staticmethod def _resource_decomp(num_wires, **kwargs): - cnot = re.CompressedResourceOp(re.ops.ResourceCNOT, {}) - rz = re.CompressedResourceOp(re.ops.ResourceRZ, {}) + cnot = re.CompressedResourceOp(re.ResourceCNOT, {}) + rz = re.CompressedResourceOp(re.ResourceRZ, {}) gate_types = {} gate_types[cnot] = 2 * (num_wires - 1) @@ -56,11 +56,13 @@ class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): """ @staticmethod - def _resource_decomp(active_wires, pauli_word, **kwargs): + def _resource_decomp(pauli_word, **kwargs): if set(pauli_word) == {"I"}: gp = re.ResourceGlobalPhase.resource_rep(**kwargs) return {gp: 1} + active_wires = len(pauli_word.replace("I", "")) + h = re.ResourceHadamard.resource_rep(**kwargs) rx = re.ResourceRX.resource_rep(**kwargs) multi_rz = re.ResourceMultiRZ.resource_rep(active_wires, **kwargs) @@ -82,16 +84,14 @@ def _resource_decomp(active_wires, pauli_word, **kwargs): return gate_types def resource_params(self): - pauli_word = self.hyperparameters["pauli_word"] return { - "active_wires": len(pauli_word.replace("I", "")), - "pauli_word": pauli_word, + "pauli_word": self.hyperparameters["pauli_word"], } @classmethod - def resource_rep(cls, active_wires, pauli_word, **kwargs): + def resource_rep(cls, pauli_word, **kwargs): return re.CompressedResourceOp( - cls, {"active_wires": active_wires, "pauli_word": pauli_word} + cls, {"pauli_word": pauli_word} ) @@ -119,6 +119,8 @@ def _resource_decomp(*args, **kwargs): gate_types[cnot] = 2 gate_types[rx] = 1 + return gate_types + def resource_params(self): return {} @@ -178,10 +180,10 @@ class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - h = re.ops.ResourceHadamard.resource_rep(**kwargs) - cy = re.ops.ResourceCY.resource_rep(**kwargs) - ry = re.ops.ResourceRY.resource_rep(**kwargs) - rx = re.ops.ResourceRX.resource_rep(**kwargs) + h = re.ResourceHadamard.resource_rep(**kwargs) + cy = re.ResourceCY.resource_rep(**kwargs) + ry = re.ResourceRY.resource_rep(**kwargs) + rx = re.ResourceRX.resource_rep(**kwargs) gate_types = {} gate_types[h] = 2 @@ -216,8 +218,8 @@ class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - cnot = re.ops.ResourceCNOT.resource_rep(**kwargs) - rz = re.ops.ResourceRZ.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep(**kwargs) + rz = re.ResourceRZ.resource_rep(**kwargs) gate_types = {} gate_types[cnot] = 2 @@ -247,9 +249,9 @@ class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - swap = re.ops.ResourceSWAP.resource_rep(**kwargs) - cnot = re.ops.ResourceCNOT.resource_rep(**kwargs) - phase = re.ops.ResourcePhaseShift.resource_rep(**kwargs) + swap = re.ResourceSWAP.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep(**kwargs) + phase = re.ResourcePhaseShift.resource_rep(**kwargs) gate_types = {} gate_types[swap] = 1 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py new file mode 100644 index 00000000000..3280f4be01b --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py @@ -0,0 +1,286 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for parametric multi qubit resource operators.""" + +import pytest + +import pennylane.labs.resource_estimation as re + +#pylint: disable=use-implicit-booleaness-not-comparison + +class TestMultiRZ: + """Test the ResourceMultiRZ class.""" + + @pytest.mark.parametrize("num_wires", range(1, 10)) + def test_resource_params(self, num_wires): + """Test that the resource params are correct.""" + op = re.ResourceMultiRZ(0.5, range(num_wires)) + assert op.resource_params() == {"num_wires": num_wires} + + @pytest.mark.parametrize("num_wires", range(1, 10)) + def test_resource_rep(self, num_wires): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceMultiRZ, {"num_wires": num_wires}) + assert re.ResourceMultiRZ.resource_rep(num_wires) == expected + + @pytest.mark.parametrize("num_wires", range(1, 10)) + def test_resources(self, num_wires): + """Test that the resources are correct.""" + expected = { + re.ResourceCNOT.resource_rep(): 2 * (num_wires - 1), + re.ResourceRZ.resource_rep(): 1, + } + assert re.ResourceMultiRZ.resources(num_wires) == expected + + @pytest.mark.parametrize("num_wires", range(1, 10)) + def test_resources_from_rep(self, num_wires): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourceMultiRZ(0.5, wires=range(num_wires)) + expected = { + re.ResourceCNOT.resource_rep(): 2 * (num_wires - 1), + re.ResourceRZ.resource_rep(): 1, + } + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + +class TestPauliRot: + """Test the ResourcePauliRot class.""" + + pauli_words = ("I", "XYZ", "XXX", "XIYIZIX", "III") + + @pytest.mark.parametrize("pauli_word", pauli_words) + def test_resource_params(self, pauli_word): + """Test that the resource params are correct.""" + op = re.ResourcePauliRot(theta=0.5, pauli_word=pauli_word, wires=range(len(pauli_word))) + assert op.resource_params() == {"pauli_word": pauli_word} + + @pytest.mark.parametrize("pauli_word", pauli_words) + def test_resource_rep(self, pauli_word): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourcePauliRot, {"pauli_word": pauli_word}) + assert re.ResourcePauliRot.resource_rep(pauli_word) == expected + + expected_h_count = (0, 2, 6, 4, 0) + expected_rx_count = (0, 2, 0, 2, 0) + params = zip(pauli_words, expected_h_count, expected_rx_count) + + @pytest.mark.parametrize("pauli_word, expected_h_count, expected_rx_count", params) + def test_resources(self, pauli_word, expected_h_count, expected_rx_count): + """Test that the resources are correct.""" + active_wires = len(pauli_word.replace("I", "")) + + if set(pauli_word) == {"I"}: + expected = {re.ResourceGlobalPhase.resource_rep(): 1} + else: + expected = { + re.ResourceHadamard.resource_rep(): expected_h_count, + re.ResourceRX.resource_rep(): expected_rx_count, + re.ResourceMultiRZ.resource_rep(active_wires): 1, + } + + assert re.ResourcePauliRot.resources(pauli_word) == expected + + @pytest.mark.parametrize("pauli_word, expected_h_count, expected_rx_count", params) + def test_resources_from_rep(self, pauli_word, expected_h_count, expected_rx_count): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourcePauliRot(0.5, pauli_word, wires=range(len(pauli_word))) + active_wires = len(pauli_word.replace("I", "")) + + if set(pauli_word) == {"I"}: + expected = {re.ResourceGlobalPhase.resource_rep(): 1} + else: + expected = { + re.ResourceHadamard.resource_rep(): expected_h_count, + re.ResourceRX.resource_rep(): expected_rx_count, + re.ResourceMultiRZ.resource_rep(active_wires): 1, + } + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + +class TestIsingXX: + """Test the IsingXX class.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceIsingXX(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceIsingXX, {}) + assert re.ResourceIsingXX.resource_rep() == expected + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRX.resource_rep(): 1, + } + assert re.ResourceIsingXX.resources() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourceIsingXX(0.5, wires=[0, 1]) + expected = { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRX.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + +class TestIsingXY: + """Test the IsingXY class.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceIsingXY(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceIsingXY, {}) + assert re.ResourceIsingXY.resource_rep() == expected + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceHadamard.resource_rep(): 2, + re.ResourceCY.resource_rep(): 2, + re.ResourceRY.resource_rep(): 1, + re.ResourceRX.resource_rep(): 1, + } + assert re.ResourceIsingXY.resources() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourceIsingXY(0.5, wires=[0, 1]) + expected = { + re.ResourceHadamard.resource_rep(): 2, + re.ResourceCY.resource_rep(): 2, + re.ResourceRY.resource_rep(): 1, + re.ResourceRX.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + +class TestIsingYY: + """Test the IsingYY class.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceIsingYY(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceIsingYY, {}) + assert re.ResourceIsingYY.resource_rep() == expected + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceCY.resource_rep(): 2, + re.ResourceRY.resource_rep(): 1, + } + assert re.ResourceIsingYY.resources() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourceIsingYY(0.5, wires=[0, 1]) + expected = { + re.ResourceCY.resource_rep(): 2, + re.ResourceRY.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + +class TestIsingZZ: + """Test the IsingZZ class.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceIsingZZ(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceIsingZZ, {}) + assert re.ResourceIsingZZ.resource_rep() == expected + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 1, + } + assert re.ResourceIsingZZ.resources() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourceIsingZZ(0.5, wires=[0, 1]) + expected = { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + +class TestPSWAP: + """Test the PSWAP class.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourcePSWAP(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourcePSWAP, {}) + assert re.ResourcePSWAP.resource_rep() == expected + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceSWAP.resource_rep(): 1, + re.ResourceCNOT.resource_rep(): 2, + re.ResourcePhaseShift.resource_rep(): 1, + } + assert re.ResourcePSWAP.resources() == expected + + def test_resources_from_rep(self): + """Test that the resources can be computed from the compressed representation and params.""" + op = re.ResourcePSWAP(0.5, wires=[0, 1]) + expected = { + re.ResourceSWAP.resource_rep(): 1, + re.ResourceCNOT.resource_rep(): 2, + re.ResourcePhaseShift.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected From 7a9475278395e13a7f0ad310a143c602ad4e3f31 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 13:41:42 -0500 Subject: [PATCH 216/335] qchem tests and formatting --- .../ops/qubit/parametric_ops_multi_qubit.py | 4 +- .../ops/qubit/qchem_ops.py | 2 +- .../qubit/test_parametric_ops_multi_qubit.py | 9 +- .../ops/qubit/test_qchem_ops.py | 253 ++++++++++++++++++ 4 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 2e2d62e4ae1..797e8d71543 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -90,9 +90,7 @@ def resource_params(self): @classmethod def resource_rep(cls, pauli_word, **kwargs): - return re.CompressedResourceOp( - cls, {"pauli_word": pauli_word} - ) + return re.CompressedResourceOp(cls, {"pauli_word": pauli_word}) class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 81f8e311047..3033999f101 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -93,7 +93,7 @@ class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): def _resource_decomp(*args, **kwargs): """See https://arxiv.org/abs/2104.05695""" h = re.ResourceHadamard.resource_rep(**kwargs) - ry = re.ResourceX.resource_rep(**kwargs) + ry = re.ResourceRY.resource_rep(**kwargs) cnot = re.ResourceCNOT.resource_rep(**kwargs) gate_types = {} diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py index 3280f4be01b..b66d183ae22 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py @@ -17,7 +17,8 @@ import pennylane.labs.resource_estimation as re -#pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison + class TestMultiRZ: """Test the ResourceMultiRZ class.""" @@ -57,6 +58,7 @@ def test_resources_from_rep(self, num_wires): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + class TestPauliRot: """Test the ResourcePauliRot class.""" @@ -114,6 +116,7 @@ def test_resources_from_rep(self, pauli_word, expected_h_count, expected_rx_coun op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + class TestIsingXX: """Test the IsingXX class.""" @@ -147,6 +150,7 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + class TestIsingXY: """Test the IsingXY class.""" @@ -184,6 +188,7 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + class TestIsingYY: """Test the IsingYY class.""" @@ -217,6 +222,7 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + class TestIsingZZ: """Test the IsingZZ class.""" @@ -250,6 +256,7 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + class TestPSWAP: """Test the PSWAP class.""" diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py new file mode 100644 index 00000000000..23611989cfa --- /dev/null +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -0,0 +1,253 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for qchem ops resource operators.""" + +import pennylane.labs.resource_estimation as re + +# pylint: disable=use-implicit-booleaness-not-comparison + + +# TODO: implement in resource_symbolic_ops branch +class TestSingleExcitation: + """Tests for the ResourceSingleExcitation class.""" + + def test_resources(self): + """Test that the resources are correct.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + + +class TestSingleExcitationMinus: + """Tests for the ResourceSingleExcitationMinus class.""" + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceX.resource_rep(): 4, + re.ResourceControlledPhaseShift.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceCRY.resource_rep(): 1, + } + assert re.ResourceSingleExcitationMinus.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceSingleExcitationMinus(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceSingleExcitationMinus, {}) + assert re.ResourceSingleExcitationMinus.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceSingleExcitationMinus(0.5, wires=[0, 1]) + expected = { + re.ResourceX.resource_rep(): 4, + re.ResourceControlledPhaseShift.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceCRY.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + +class TestSingleExcitationPlus: + """Tests for the ResourceSingleExcitationPlus class.""" + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceX.resource_rep(): 4, + re.ResourceControlledPhaseShift.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceCRY.resource_rep(): 1, + } + assert re.ResourceSingleExcitationPlus.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceSingleExcitationPlus(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceSingleExcitationPlus, {}) + assert re.ResourceSingleExcitationPlus.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceSingleExcitationPlus(0.5, wires=[0, 1]) + expected = { + re.ResourceX.resource_rep(): 4, + re.ResourceControlledPhaseShift.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceCRY.resource_rep(): 1, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + +class TestDoubleExcitation: + """Tests for the ResourceDoubleExcitation class.""" + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceRY.resource_rep(): 8, + re.ResourceCNOT.resource_rep(): 14, + } + assert re.ResourceDoubleExcitation.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceDoubleExcitation(0.5, wires=[0, 1, 2, 3]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceDoubleExcitation, {}) + assert re.ResourceDoubleExcitation.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceDoubleExcitation(0.5, wires=[0, 1, 2, 3]) + expected = { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceRY.resource_rep(): 8, + re.ResourceCNOT.resource_rep(): 14, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + +# TODO: add tests in resource_symbolic_ops branch +class TestDoubleExcitationMinus: + """Tests for the ResourceDoubleExcitationMinus class.""" + + def test_resources(self): + """Test that the resources are correct.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + + +# TODO: add tests in resource_symbolic_ops branch +class TestDoubleExcitationPlus: + """Tests for the ResourceDoubleExcitationPlus class.""" + + def test_resources(self): + """Test that the resources are correct.""" + + def test_resource_params(self): + """Test that the resource params are correct.""" + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + + +class TestOribatlRotation: + """Tests for the ResourceOrbitalRotation class.""" + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceFermionicSWAP.resource_rep(): 2, + re.ResourceSingleExcitation.resource_rep(): 2, + } + assert re.ResourceOrbitalRotation.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceOrbitalRotation(0.5, wires=[0, 1, 3, 4]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceOrbitalRotation, {}) + assert re.ResourceOrbitalRotation.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceOrbitalRotation(0.5, wires=[0, 1, 3, 4]) + expected = { + re.ResourceFermionicSWAP.resource_rep(): 2, + re.ResourceSingleExcitation.resource_rep(): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + +class TestFermionicSWAP: + """Tests for the ResourceFermionicSWAP class.""" + + def test_resources(self): + """Test that the resources are correct.""" + expected = { + re.ResourceHadamard.resource_rep(): 4, + re.ResourceMultiRZ.resource_rep(num_wires=2): 2, + re.ResourceRX.resource_rep(): 4, + re.ResourceRZ.resource_rep(): 2, + } + assert re.ResourceFermionicSWAP.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct.""" + op = re.ResourceFermionicSWAP(0.5, wires=[0, 1]) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceFermionicSWAP, {}) + assert re.ResourceFermionicSWAP.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceFermionicSWAP(0.5, wires=[0, 1]) + expected = { + re.ResourceHadamard.resource_rep(): 4, + re.ResourceMultiRZ.resource_rep(num_wires=2): 2, + re.ResourceRX.resource_rep(): 4, + re.ResourceRZ.resource_rep(): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected From fc0b865f8ecc047a97f9006d031b7c863f87448f Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 14:28:03 -0500 Subject: [PATCH 217/335] formatting and doc fixes for CI --- pennylane/labs/resource_estimation/__init__.py | 14 ++++++++++---- .../ops/qubit/test_parametric_ops_multi_qubit.py | 2 +- .../ops/qubit/test_qchem_ops.py | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index aeb118a726d..c92373a904e 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -40,9 +40,9 @@ ~ResourceCNOT ~ResourceControlledPhaseShift - ~ResourceDoubleExcitation, - ~ResourceDoubleExcitationMinus, - ~ResourceDoubleExcitationPlus, + ~ResourceDoubleExcitation + ~ResourceDoubleExcitationMinus + ~ResourceDoubleExcitationPlus ~ResourceCRY ~ResourceCY ~ResourceCZ @@ -50,9 +50,15 @@ ~ResourceGlobalPhase ~ResourceHadamard ~ResourceIdentity + ~ResourceIsingXX + ~ResourceIsingXY + ~ResourceIsingYY + ~ResourceIsingZZ ~ResourceMultiRZ ~ResourceOrbitalRotation + ~ResourcePauliRot ~ResourcePhaseShift + ~ResourcePSWAP ~ResourceRot ~ResourceRX ~ResourceRY @@ -60,7 +66,7 @@ ~ResourceS ~ResourceSingleExcitation ~ResourceSingleExcitationMinus - ~ResourceSingleExcitationPlus, + ~ResourceSingleExcitationPlus ~ResourceSWAP ~ResourceT ~ResourceToffoli diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py index b66d183ae22..77c3665c91d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py @@ -17,7 +17,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison,no-self-use class TestMultiRZ: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index 23611989cfa..eefe56bcfe7 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -15,7 +15,7 @@ import pennylane.labs.resource_estimation as re -# pylint: disable=use-implicit-booleaness-not-comparison +# pylint: disable=use-implicit-booleaness-not-comparison,no-self-use # TODO: implement in resource_symbolic_ops branch From 4956bc25c06a206bab75920f3d30142346e7866d Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 14:55:54 -0500 Subject: [PATCH 218/335] filled in TODOs from resource_multi_qubit --- .../ops/op_math/symbolic.py | 2 +- .../ops/qubit/qchem_ops.py | 30 ++++++-- .../ops/qubit/test_qchem_ops.py | 76 ++++++++++++++++++- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 45105512e4d..25b6d0d5de3 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -73,7 +73,7 @@ def resource_params(self): "base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires), - "num_zeros": len([val for val in self.control_values if not val]) + "num_zeros": len([val for val in self.control_values if not val]), } @classmethod diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 84bceef423b..aa758ba289c 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -23,8 +23,26 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_ops branch""" - raise re.ResourcesNotDefined + t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) + h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep() + s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() + t = re.ResourceT.resource_rep() + + gate_types = {} + gate_types[t_dag] = 2 + gate_types[h] = 4 + gate_types[s] = 2 + gate_types[s_dag] = 2 + gate_types[cnot] = 2 + gate_types[rz] = 1 + gate_types[ry] = 1 + gate_types[t] = 2 + + return gate_types def resource_params(self): return {} @@ -118,8 +136,8 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat def _resource_decomp(*args, **kwargs): phase = re.ResourceGlobalPhase.resource_rep() double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled(re.ResourceZ, {}, 3, 1) - ctrl_phase = re.ResourceControlled(re.ResourcePhaseShift, {}, 3, 1) + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1) gate_types = {} gate_types[phase] = 1 @@ -144,8 +162,8 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator def _resource_decomp(*args, **kwargs): phase = re.ResourceGlobalPhase.resource_rep() double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled(re.ResourceZ, {}, 3, 1) - ctrl_phase = re.ResourceControlled(re.ResourcePhaseShift, {}, 3, 1) + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1) gate_types = {} gate_types[phase] = 1 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index eefe56bcfe7..8f14616c4cc 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -24,15 +24,45 @@ class TestSingleExcitation: def test_resources(self): """Test that the resources are correct.""" + expected = { + re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, + re.ResourceHadamard.resource_rep(): 4, + re.ResourceS.resource_rep(): 2, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 1, + re.ResourceRY.resource_rep(): 1, + re.ResourceT.resource_rep(): 2, + } + assert re.ResourceSingleExcitation.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" + op = re.ResourceSingleExcitation(0.5, wires=[0, 1]) + assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceSingleExcitation, {}) + assert re.ResourceSingleExcitation.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceSingleExcitation(0.5, wires=[0, 1]) + expected = { + re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, + re.ResourceHadamard.resource_rep(): 4, + re.ResourceS.resource_rep(): 2, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 1, + re.ResourceRY.resource_rep(): 1, + re.ResourceT.resource_rep(): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected class TestSingleExcitationMinus: @@ -147,38 +177,80 @@ def test_resources_from_rep(self): assert op_resource_type.resources(**op_resource_params) == expected -# TODO: add tests in resource_symbolic_ops branch class TestDoubleExcitationMinus: """Tests for the ResourceDoubleExcitationMinus class.""" def test_resources(self): """Test that the resources are correct.""" + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + } + assert re.ResourceDoubleExcitationMinus.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" + op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3]) + assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceDoubleExcitationMinus, {}) + assert re.ResourceDoubleExcitationMinus.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3]) + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected -# TODO: add tests in resource_symbolic_ops branch class TestDoubleExcitationPlus: """Tests for the ResourceDoubleExcitationPlus class.""" def test_resources(self): """Test that the resources are correct.""" + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + } + assert re.ResourceDoubleExcitationPlus.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" + op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4]) + assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceDoubleExcitationPlus, {}) + assert re.ResourceDoubleExcitationPlus.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4]) + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected class TestOribatlRotation: From 15cd778dceced352c90958ae82c9df105df57376 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 21 Nov 2024 15:02:59 -0500 Subject: [PATCH 219/335] add global phase to FermionicSWAP --- pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py | 2 ++ .../labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 3033999f101..2f845a86088 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -173,12 +173,14 @@ def _resource_decomp(*args, **kwargs): multi_rz = re.ResourceMultiRZ.resource_rep(num_wires=2, **kwargs) rx = re.ResourceRX.resource_rep(**kwargs) rz = re.ResourceRZ.resource_rep(**kwargs) + phase = re.ResourceGlobalPhase.resource_rep() gate_types = {} gate_types[h] = 4 gate_types[multi_rz] = 2 gate_types[rx] = 4 gate_types[rz] = 2 + gate_types[phase] = 1 return gate_types diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index eefe56bcfe7..c3c11a014af 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -225,6 +225,7 @@ def test_resources(self): re.ResourceMultiRZ.resource_rep(num_wires=2): 2, re.ResourceRX.resource_rep(): 4, re.ResourceRZ.resource_rep(): 2, + re.ResourceGlobalPhase.resource_rep(): 1, } assert re.ResourceFermionicSWAP.resources() == expected @@ -246,6 +247,7 @@ def test_resources_from_rep(self): re.ResourceMultiRZ.resource_rep(num_wires=2): 2, re.ResourceRX.resource_rep(): 4, re.ResourceRZ.resource_rep(): 2, + re.ResourceGlobalPhase.resource_rep(): 1, } op_compressed_rep = op.resource_rep_from_op() op_resource_type = op_compressed_rep.op_type From e4a73311f818553fcbf31f680e708ed33e00f2f2 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 22 Nov 2024 09:57:22 -0500 Subject: [PATCH 220/335] update multiX --- .../ops/op_math/controlled_ops.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 0b3bfcb21bb..685e155b63c 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -306,17 +306,35 @@ class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): """ @staticmethod - def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: raise re.ResourcesNotDefined def resource_params(self) -> dict: num_control = len(self.hyperparameters["control_wires"]) num_work_wires = len(self.hyperparameters["work_wires"]) - return {"num_ctrl_wires": num_control, "num_work_wires": num_work_wires} + + num_control_values = len([val for val in self.hyperparameters["control_values"] if val]) + + return { + "num_ctrl_wires": num_control, + "num_ctrl_values": num_control_values, + "num_work_wires": num_work_wires, + } @classmethod - def resource_rep(cls) -> re.CompressedResourceOp: - return re.CompressedResourceOp(cls, {}) + def resource_rep( + cls, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> re.CompressedResourceOp: + return re.CompressedResourceOp( + cls, + { + "num_ctrl_wires": num_ctrl_wires, + "num_ctrl_values": num_ctrl_values, + "num_work_wires": num_work_wires, + }, + ) class ResourceCRX(qml.CRX, re.ResourceOperator): From 8ef2c3deb4322383c936b091a15427d8fde7cd3b Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 22 Nov 2024 10:04:24 -0500 Subject: [PATCH 221/335] add multix to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index d969eb02556..70dd9091552 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -46,6 +46,7 @@ ~ResourceGlobalPhase ~ResourceHadamard ~ResourceIdentity + ~ResourceMultiControlledX ~ResourcePhaseShift ~ResourceRot ~ResourceRX @@ -88,6 +89,7 @@ ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, + ResourceMultiControlledX, ResourcePhaseShift, ResourceRot, ResourceRX, From 43f989176c8de9e2ef5ecbc1b67f70e9f2ac9dd2 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 22 Nov 2024 10:09:59 -0500 Subject: [PATCH 222/335] merge --- pennylane/labs/resource_estimation/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 365d4bf3d0f..e4c1cd61397 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -113,19 +113,16 @@ ResourceGlobalPhase, ResourceHadamard, ResourceIdentity, -<<<<<<< HEAD ResourceIsingXX, ResourceIsingXY, ResourceIsingYY, ResourceIsingZZ, + ResourceMultiControlledX, ResourceMultiRZ, ResourceOrbitalRotation, ResourcePauliRot, ResourcePow, ResourcePSWAP, -======= - ResourceMultiControlledX, ->>>>>>> 8ef2c3deb4322383c936b091a15427d8fde7cd3b ResourcePhaseShift, ResourceRot, ResourceRX, From 861509ca3d60b47b2628a9f8611468d2f5c16f36 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 25 Nov 2024 11:42:51 -0500 Subject: [PATCH 223/335] resource decomps for nested adjoints and pows, some fixes for controlled --- .../ops/op_math/symbolic.py | 48 +++++++--- .../ops/op_math/test_symbolic.py | 90 +++++++++++++++++++ 2 files changed, 126 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 25b6d0d5de3..17e30506519 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -20,7 +20,7 @@ from pennylane.ops.op_math.controlled import ControlledOp from pennylane.ops.op_math.pow import PowOperation -# pylint: disable=too-many-ancestors,arguments-differ +# pylint: disable=too-many-ancestors,arguments-differ,protected-access class ResourceAdjoint(AdjointOperation, re.ResourceOperator): @@ -50,21 +50,31 @@ def resource_rep(cls, base_class, base_params, **kwargs): cls, {"base_class": base_class, "base_params": base_params}, name=name ) + @staticmethod + def adjoint_resource_decomp(base_class, base_params, **kwargs): + return base_class._resource_decomp(**base_params) + class ResourceControlled(ControlledOp, re.ResourceOperator): """Resource class for Controlled""" @staticmethod - def _resource_decomp(base_class, base_params, num_ctrl_wires, **kwargs): + def _resource_decomp(base_class, base_params, num_ctrl_wires, num_ctrl_vals, **kwargs): try: - return base_class.controlled_resource_decomp(num_ctrl_wires, **base_params) + return base_class.controlled_resource_decomp( + num_ctrl_wires, num_ctrl_vals, **base_params + ) except re.ResourcesNotDefined: - gate_types = defaultdict(int) - decomp = base_class.resources(**base_params) - for gate, count in decomp.items(): - resources = gate.op_type.controlled_resource_decomp(**gate.params) - _scale_dict(resources, count, in_place=True) - _combine_dict(gate_types, resources, in_place=True) + pass + + gate_types = defaultdict(int) + decomp = base_class.resources(**base_params) + for gate, count in decomp.items(): + resources = gate.op_type.controlled_resource_decomp( + num_ctrl_wires, num_ctrl_vals, **gate.params + ) + _scale_dict(resources, count, in_place=True) + _combine_dict(gate_types, resources, in_place=True) return gate_types @@ -73,11 +83,11 @@ def resource_params(self): "base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires), - "num_zeros": len([val for val in self.control_values if not val]), + "num_ctrl_vals": len([val for val in self.control_values if val]), } @classmethod - def resource_rep(cls, base_class, base_params, num_ctrl_wires, num_zeros, **kwargs): + def resource_rep(cls, base_class, base_params, num_ctrl_wires, num_ctrl_vals, **kwargs): name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( cls, @@ -85,11 +95,21 @@ def resource_rep(cls, base_class, base_params, num_ctrl_wires, num_zeros, **kwar "base_class": base_class, "base_params": base_params, "num_ctrl_wires": num_ctrl_wires, - "num_zeros": num_zeros, + "num_ctrl_vals": num_ctrl_vals, }, name=name, ) + @classmethod + def controlled_resource_decomp( + cls, base_class, base_params, num_ctrl_wires, num_ctrl_vals, **kwargs + ): + print("controlled_resources") + print("base_class:", base_class) + print("base_params:", base_params) + print("num_ctrl_wires:", num_ctrl_wires) + print("num_ctrl_vals:", num_ctrl_vals) + class ResourcePow(PowOperation, re.ResourceOperator): """Resource class for Pow""" @@ -121,3 +141,7 @@ def resource_rep(cls, base_class, z, base_params, **kwargs): return re.CompressedResourceOp( cls, {"base_class": base_class, "z": z, "base_params": base_params}, name=name ) + + @classmethod + def pow_resource_decomp(cls, z0, base_class, z, base_params, **kwargs): + return cls._resource_decomp(base_class, z0 * z, base_params) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index eac3387f93e..afdef5193ff 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -15,7 +15,10 @@ Tests for symbolic resource operators. """ +import pytest + import pennylane.labs.resource_estimation as re +from pennylane.labs.resource_estimation.resource_container import _scale_dict # pylint: disable=protected-access,no-self-use @@ -40,6 +43,54 @@ def test_name(self): op = re.ResourceAdjoint(base=base) assert op.resource_rep_from_op()._name == "Adjoint(QFT)" + @pytest.mark.parametrize( + "nested_op, base_op", + [ + ( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))), + re.ResourceQFT([0, 1, 2]), + ), + ( + re.ResourceAdjoint( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) + ), + re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])), + ), + ( + re.ResourceAdjoint( + re.ResourceAdjoint( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) + ) + ), + re.ResourceQFT([0, 1, 2]), + ), + ( + re.ResourceAdjoint( + re.ResourceAdjoint( + re.ResourceAdjoint( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) + ) + ) + ), + re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])), + ), + ], + ) + def test_nested_adjoints(self, nested_op, base_op): + """Test the resources of nested Adjoints.""" + + nested_rep = nested_op.resource_rep_from_op() + nested_params = nested_rep.params + nested_type = nested_rep.op_type + nested_resources = nested_type.resources(**nested_params) + + base_op = base_op.resource_rep_from_op() + base_params = base_op.params + base_type = base_op.op_type + base_resources = base_type.resources(**base_params) + + assert nested_resources == base_resources + class TestResourceControlled: """Tests for ResourceControlled""" @@ -53,6 +104,7 @@ def test_resource_params(self): "base_class": re.ResourceQFT, "base_params": base.resource_params(), "num_ctrl_wires": 1, + "num_ctrl_vals": 1, } def test_name(self): @@ -83,3 +135,41 @@ def test_name(self): base = re.ResourceQFT(wires=[0, 1, 2]) op = re.ResourcePow(base=base, z=5) assert op.resource_rep_from_op()._name == "QFT**5" + + @pytest.mark.parametrize( + "nested_op, base_op, z", + [ + ( + re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 2), + re.ResourceQFT([0, 1]), + 4, + ), + ( + re.ResourcePow(re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 2), 2), + re.ResourceQFT([0, 1]), + 8, + ), + ( + re.ResourcePow( + re.ResourcePow(re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 2), 2), + 2, + ), + re.ResourceQFT([0, 1]), + 16, + ), + ], + ) + def test_nested_pow(self, nested_op, base_op, z): + """Test the resources for nested Pow operators.""" + + nested_rep = nested_op.resource_rep_from_op() + nested_params = nested_rep.params + nested_type = nested_rep.op_type + nested_resources = nested_type.resources(**nested_params) + + base_op = base_op.resource_rep_from_op() + base_params = base_op.params + base_type = base_op.op_type + base_resources = base_type.resources(**base_params) + + assert nested_resources == _scale_dict(base_resources, z) From 66b49d532360911fa1d0e18b6ede367196ec9ac1 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 25 Nov 2024 14:51:41 -0500 Subject: [PATCH 224/335] nested controlled operations --- .../ops/op_math/symbolic.py | 42 +++++++++++++------ .../ops/qubit/non_parametric_ops.py | 13 +++++- .../resource_estimation/resource_operator.py | 2 +- .../ops/op_math/test_symbolic.py | 41 ++++++++++++++++-- .../ops/qubit/test_non_parametric_ops.py | 25 +++++++++++ 5 files changed, 103 insertions(+), 20 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 17e30506519..3110f51ceea 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -20,7 +20,7 @@ from pennylane.ops.op_math.controlled import ControlledOp from pennylane.ops.op_math.pow import PowOperation -# pylint: disable=too-many-ancestors,arguments-differ,protected-access +# pylint: disable=too-many-ancestors,arguments-differ,protected-access,too-many-arguments class ResourceAdjoint(AdjointOperation, re.ResourceOperator): @@ -59,10 +59,12 @@ class ResourceControlled(ControlledOp, re.ResourceOperator): """Resource class for Controlled""" @staticmethod - def _resource_decomp(base_class, base_params, num_ctrl_wires, num_ctrl_vals, **kwargs): + def _resource_decomp( + base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ): try: return base_class.controlled_resource_decomp( - num_ctrl_wires, num_ctrl_vals, **base_params + num_ctrl_wires, num_ctrl_values, num_work_wires, **base_params ) except re.ResourcesNotDefined: pass @@ -71,7 +73,7 @@ def _resource_decomp(base_class, base_params, num_ctrl_wires, num_ctrl_vals, **k decomp = base_class.resources(**base_params) for gate, count in decomp.items(): resources = gate.op_type.controlled_resource_decomp( - num_ctrl_wires, num_ctrl_vals, **gate.params + num_ctrl_wires, num_ctrl_values, num_work_wires, **gate.params ) _scale_dict(resources, count, in_place=True) _combine_dict(gate_types, resources, in_place=True) @@ -83,11 +85,14 @@ def resource_params(self): "base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires), - "num_ctrl_vals": len([val for val in self.control_values if val]), + "num_ctrl_values": len([val for val in self.control_values if val]), + "num_work_wires": len(self.work_wires), } @classmethod - def resource_rep(cls, base_class, base_params, num_ctrl_wires, num_ctrl_vals, **kwargs): + def resource_rep( + cls, base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ): name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( cls, @@ -95,20 +100,31 @@ def resource_rep(cls, base_class, base_params, num_ctrl_wires, num_ctrl_vals, ** "base_class": base_class, "base_params": base_params, "num_ctrl_wires": num_ctrl_wires, - "num_ctrl_vals": num_ctrl_vals, + "num_ctrl_values": num_ctrl_values, + "num_work_wires": num_work_wires, }, name=name, ) @classmethod def controlled_resource_decomp( - cls, base_class, base_params, num_ctrl_wires, num_ctrl_vals, **kwargs + cls, + outer_num_ctrl_wires, + outer_num_ctrl_values, + outer_num_work_wires, + base_class, + base_params, + num_ctrl_wires, + num_ctrl_values, + num_work_wires, ): - print("controlled_resources") - print("base_class:", base_class) - print("base_params:", base_params) - print("num_ctrl_wires:", num_ctrl_wires) - print("num_ctrl_vals:", num_ctrl_vals) + return cls._resource_decomp( + base_class, + base_params, + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ) class ResourcePow(PowOperation, re.ResourceOperator): diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 56fd48bbe0b..1c055561e8a 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -1,5 +1,3 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -163,6 +161,17 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_vals, num_work_wires, **kwargs): + if num_ctrl_wires == 1 and num_ctrl_vals == 1: + return re.ResourceCNOT.resources(**kwargs) + if num_ctrl_wires == 2 and num_ctrl_vals == 2: + return re.ResourceToffoli.resources(**kwargs) + + return re.ResourceMultiControlledX.resources( + num_ctrl_wires, num_ctrl_vals, num_work_wires, **kwargs + ) + class ResourceY(qml.Y, re.ResourceOperator): """Resource class for the Y gate.""" diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index f7f462b2125..212e33e36bb 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -111,7 +111,7 @@ def adjoint_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, @classmethod def controlled_resource_decomp( - cls, num_ctrl_wires, *args, **kwargs + cls, num_ctrl_wires, num_ctrl_vals, num_work_wires, *args, **kwargs ) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the controlled version of the operator""" raise ResourcesNotDefined diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index afdef5193ff..008b3e2addd 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -104,7 +104,8 @@ def test_resource_params(self): "base_class": re.ResourceQFT, "base_params": base.resource_params(), "num_ctrl_wires": 1, - "num_ctrl_vals": 1, + "num_ctrl_values": 1, + "num_work_wires": 0, } def test_name(self): @@ -114,6 +115,38 @@ def test_name(self): op = re.ResourceControlled(base=base, control_wires=[3]) assert op.resource_rep_from_op()._name == "Controlled(QFT, wires=1)" + @pytest.mark.parametrize( + "nested_op, expected_op", + [ + ( + re.ResourceControlled( + re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2] + ), + re.ResourceToffoli([0, 1, 2]), + ), + ( + re.ResourceControlled( + re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2] + ), + re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]), + ), + ], + ) + def test_nested_controls(self, nested_op, expected_op): + """Test the resources for nested Controlled operators.""" + + nested_rep = nested_op.resource_rep_from_op() + nested_params = nested_rep.params + nested_type = nested_rep.op_type + nested_resources = nested_type.resources(**nested_params) + + expected_rep = expected_op.resource_rep_from_op() + expected_params = expected_rep.params + expected_type = expected_rep.op_type + expected_resources = expected_type.resources(**expected_params) + + assert nested_resources == expected_resources + class TestResourcePow: """Tests for ResourcePow""" @@ -167,9 +200,9 @@ def test_nested_pow(self, nested_op, base_op, z): nested_type = nested_rep.op_type nested_resources = nested_type.resources(**nested_params) - base_op = base_op.resource_rep_from_op() - base_params = base_op.params - base_type = base_op.op_type + base_rep = base_op.resource_rep_from_op() + base_params = base_rep.params + base_type = base_rep.op_type base_resources = base_type.resources(**base_params) assert nested_resources == _scale_dict(base_resources, z) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index b59c519d71d..9fa36eb1f1a 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -123,3 +123,28 @@ def test_resource_rep(self): """Test that the compact representation is correct""" expected = re.CompressedResourceOp(re.ResourceT, {}) assert re.ResourceT.resource_rep() == expected + + +class TestX: + """Tests for ResourceX""" + + @pytest.mark.parametrize( + "controlled_op, expected_op", + [ + (re.ResourceControlled(re.ResourceX(0), control_wires=[1]), re.ResourceCNOT([0, 1])), + ( + re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]), + re.ResourceToffoli([0, 1, 2]), + ), + ], + ) + def test_controlled_resources(self, controlled_op, expected_op): + """Test that the controlled_resource_decomp method dispatches correctly.""" + + controlled_cp = controlled_op.resource_rep_from_op() + controlled_resources = controlled_cp.op_type.resources(**controlled_cp.params) + + expected_cp = expected_op.resource_rep_from_op() + expected_resources = expected_cp.op_type.resources(**expected_cp.params) + + assert controlled_resources == expected_resources From d5ae48af9fe93e6af99720ae89dd209211aead7b Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 25 Nov 2024 16:21:05 -0500 Subject: [PATCH 225/335] adding some resource methods --- .../labs/resource_estimation/ops/identity.py | 24 +++++++++++++ .../ops/op_math/controlled_ops.py | 35 +++++++++++++++++++ .../resource_estimation/resource_operator.py | 2 +- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 7f9b59729db..173a2ae6f81 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,6 +34,18 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp(): + return {} + + @staticmethod + def controlled_resource_decomp(): + return {} + + @staticmethod + def pow_resource_decomp(): + return {} + class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -48,3 +60,15 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @staticmethod + def adjoint_resource_decomp(): + return {} + + @staticmethod + def controlled_resource_decomp(): + return {} + + @staticmethod + def pow_resource_decomp(): + return {} diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 00ad4c9e80f..41b3bfc1f03 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -220,6 +220,25 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @classmethod + def controlled_resource_decomp( + cls, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceToffoli.resources() + + return re.ResourceMultiControlledX.resources( + num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceToffoli(qml.Toffoli, re.ResourceOperator): r"""Resource class for Toffoli @@ -296,6 +315,22 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceMultiControlledX.resources( + num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): r"""Resource class for MultiControlledX diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 212e33e36bb..79e87c08ee1 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -111,7 +111,7 @@ def adjoint_resource_decomp(cls, *args, **kwargs) -> Dict[CompressedResourceOp, @classmethod def controlled_resource_decomp( - cls, num_ctrl_wires, num_ctrl_vals, num_work_wires, *args, **kwargs + cls, num_ctrl_wires, num_ctrl_values, num_work_wires, *args, **kwargs ) -> Dict[CompressedResourceOp, int]: """Returns a compressed representation of the controlled version of the operator""" raise ResourcesNotDefined From cef11edeca88cf30ea54ffee287c6290f4df1023 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 25 Nov 2024 16:39:59 -0500 Subject: [PATCH 226/335] adjoint resource methods for RX,RY,RZ --- .../ops/qubit/parametric_ops_single_qubit.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index fe4d37a78fe..118a84c706f 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -77,6 +77,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + return _rotation_resources(epsilon=config["error_rx"]) + class ResourceRY(qml.RY, re.ResourceOperator): """Resource class for the RY gate.""" @@ -92,6 +96,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + return _rotation_resources(epsilon=config["error_ry"]) + class ResourceRZ(qml.RZ, re.ResourceOperator): r"""Resource class for the RZ gate. @@ -112,6 +120,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + return _rotation_resources(epsilon=config["error_rz"]) + class ResourceRot(qml.Rot, re.ResourceOperator): """Resource class for the Rot gate.""" From d7e1dbbe99c653822a03885ba50137e1f750c40a Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 25 Nov 2024 20:07:51 -0500 Subject: [PATCH 227/335] add CH to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 70dd9091552..f36a01dd19e 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -38,6 +38,7 @@ .. autosummary:: :toctree: api + ~ResourceCH ~ResourceCNOT ~ResourceControlledPhaseShift ~ResourceCRY @@ -81,6 +82,7 @@ from .resource_container import CompressedResourceOp, Resources from .ops import ( + ResourceCH, ResourceCNOT, ResourceControlledPhaseShift, ResourceCRY, From 1269e11b8232d107abd460a8c2edac7e1aec9128 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 08:11:07 -0500 Subject: [PATCH 228/335] add cswap to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index f36a01dd19e..c0aa5fa951b 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -42,6 +42,7 @@ ~ResourceCNOT ~ResourceControlledPhaseShift ~ResourceCRY + ~ResourceCSWAP ~ResourceCY ~ResourceCZ ~ResourceGlobalPhase @@ -86,6 +87,7 @@ ResourceCNOT, ResourceControlledPhaseShift, ResourceCRY, + ResourceCSWAP, ResourceCY, ResourceCZ, ResourceGlobalPhase, From c64365634b4532c2d3d08c3c1c7a1b22104838d8 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 08:55:23 -0500 Subject: [PATCH 229/335] add ccz to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 61049573ab7..e4d44219aa7 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -38,6 +38,7 @@ .. autosummary:: :toctree: api + ~ResourceCCZ ~ResourceCH ~ResourceCNOT ~ResourceControlledPhaseShift @@ -94,6 +95,7 @@ from .resource_tracking import DefaultGateSet, get_resources, resource_config from .ops import ( + ResourceCCZ, ResourceCH, ResourceCNOT, ResourceControlledPhaseShift, From 756e4c9c1d0139511d8cc2834600652dbabe3f4d Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 08:58:35 -0500 Subject: [PATCH 230/335] adjoint/control methods for non parametric ops --- .../ops/qubit/non_parametric_ops.py | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 1c055561e8a..a555961af42 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -36,6 +36,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCH.resources(**kwargs) + + raise re.ResourcesNotDefined + class ResourceS(qml.S, re.ResourceOperator): """Resource class for the S gate.""" @@ -55,6 +62,14 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 3} + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 4} + class ResourceSWAP(qml.SWAP, re.ResourceOperator): r"""Resource class for the SWAP gate. @@ -110,6 +125,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCSWAP.resources(**kwargs) + + raise re.ResourcesNotDefined + @classmethod def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): z % 2} @@ -161,17 +183,25 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_vals, num_work_wires, **kwargs): - if num_ctrl_wires == 1 and num_ctrl_vals == 1: + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs): + if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCNOT.resources(**kwargs) - if num_ctrl_wires == 2 and num_ctrl_vals == 2: + if num_ctrl_wires == 2 and num_ctrl_values == 2: return re.ResourceToffoli.resources(**kwargs) return re.ResourceMultiControlledX.resources( - num_ctrl_wires, num_ctrl_vals, num_work_wires, **kwargs + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceY(qml.Y, re.ResourceOperator): """Resource class for the Y gate.""" @@ -194,6 +224,13 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCY.resources(**kwargs) + + raise re.ResourcesNotDefined + class ResourceZ(qml.Z, re.ResourceOperator): """Resource class for the Z gate.""" @@ -213,3 +250,13 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCZ.resources(**kwargs) + + if num_ctrl_wires == 2 and num_ctrl_wires == 2: + return re.ResourceCCZ.resources(**kwargs) + + raise re.ResourcesNotDefined From 0f733aa177dc91e7ceaa6b0b5f6b80f224681edd Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 08:59:26 -0500 Subject: [PATCH 231/335] add crx,crz to init --- pennylane/labs/resource_estimation/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index e4d44219aa7..97dcd9da8f7 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -42,7 +42,9 @@ ~ResourceCH ~ResourceCNOT ~ResourceControlledPhaseShift + ~ResourceCRX ~ResourceCRY + ~ResourceCRZ ~ResourceCSWAP ~ResourceCY ~ResourceCZ @@ -99,7 +101,9 @@ ResourceCH, ResourceCNOT, ResourceControlledPhaseShift, + ResourceCRX, ResourceCRY, + ResourceCRZ, ResourceCSWAP, ResourceCY, ResourceCZ, From 195f489b07c03a94b33ae93591a706672d2e2407 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 09:12:04 -0500 Subject: [PATCH 232/335] add CRot to init --- pennylane/labs/resource_estimation/__init__.py | 2 ++ pennylane/labs/resource_estimation/ops/__init__.py | 1 + 2 files changed, 3 insertions(+) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 97dcd9da8f7..a3e626e5253 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -42,6 +42,7 @@ ~ResourceCH ~ResourceCNOT ~ResourceControlledPhaseShift + ~ResourceCRot ~ResourceCRX ~ResourceCRY ~ResourceCRZ @@ -101,6 +102,7 @@ ResourceCH, ResourceCNOT, ResourceControlledPhaseShift, + ResourceCRot, ResourceCRX, ResourceCRY, ResourceCRZ, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 89cb39a3ec8..59d1bad666f 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -39,6 +39,7 @@ ResourceCZ, ResourceCSWAP, ResourceCCZ, + ResourceCRot, ResourceCRX, ResourceCRY, ResourceCRZ, From 854b8224fd8977ee3caefd6201eb9fed201a4bb4 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 09:23:36 -0500 Subject: [PATCH 233/335] controlled/adjoint resources methods for parametric ops --- .../ops/op_math/controlled_ops.py | 2 +- .../ops/qubit/parametric_ops_single_qubit.py | 51 +++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 41b3bfc1f03..b2633d03fa9 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -553,4 +553,4 @@ def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int] @classmethod def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {cls.resource_rep(): 1} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 118a84c706f..474166360d0 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -62,6 +62,21 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceControlledPhaseShift.resources() + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceRX(qml.RX, re.ResourceOperator): """Resource class for the RX gate.""" @@ -81,6 +96,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rx"]) + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCRX.resources() + + raise re.ResourcesNotDefined + class ResourceRY(qml.RY, re.ResourceOperator): """Resource class for the RY gate.""" @@ -100,6 +122,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_ry"]) + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCRY.resources() + + raise re.ResourcesNotDefined + class ResourceRZ(qml.RZ, re.ResourceOperator): r"""Resource class for the RZ gate. @@ -124,6 +153,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rz"]) + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCRZ.resources() + + raise re.ResourcesNotDefined + class ResourceRot(qml.Rot, re.ResourceOperator): """Resource class for the Rot gate.""" @@ -145,9 +181,16 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, config): - return cls.resources(config) + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCRot.resources() + + raise re.ResourcesNotDefined @classmethod - def pow_resource_decomp(cls, z, config): - return cls.resources(config) + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() From ffb4c8cf433a369313af0f6f061e3749da2681e5 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 09:29:07 -0500 Subject: [PATCH 234/335] formatting --- .../ops/qubit/non_parametric_ops.py | 16 +++++++++++---- .../ops/qubit/parametric_ops_single_qubit.py | 20 ++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index a555961af42..560f7e25f77 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -37,7 +37,9 @@ def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int] return {cls.resource_rep(): 1} @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCH.resources(**kwargs) @@ -126,7 +128,9 @@ def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int] return {cls.resource_rep(): 1} @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCSWAP.resources(**kwargs) @@ -225,7 +229,9 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCY.resources(**kwargs) @@ -252,7 +258,9 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCZ.resources(**kwargs) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 474166360d0..4abd0f010f2 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -67,7 +67,9 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceControlledPhaseShift.resources() @@ -97,7 +99,9 @@ def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rx"]) @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCRX.resources() @@ -123,7 +127,9 @@ def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_ry"]) @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCRY.resources() @@ -154,7 +160,9 @@ def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rz"]) @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCRZ.resources() @@ -185,7 +193,9 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: return cls.resources() @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: return re.ResourceCRot.resources() From 54dd010e09f475ef6028eedd272855e10153af50 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 10:27:54 -0500 Subject: [PATCH 235/335] type annotations --- .../ops/op_math/symbolic.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 3110f51ceea..767b2db2ba0 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -13,6 +13,7 @@ # limitations under the License. r"""Resource operators for symbolic operations.""" from collections import defaultdict +from typing import Dict import pennylane.labs.resource_estimation as re from pennylane.labs.resource_estimation.resource_container import _combine_dict, _scale_dict @@ -27,7 +28,7 @@ class ResourceAdjoint(AdjointOperation, re.ResourceOperator): """Resource class for Adjoint""" @staticmethod - def _resource_decomp(base_class, base_params, **kwargs): + def _resource_decomp(base_class, base_params, **kwargs) -> Dict[re.CompressedResourceOp, int]: try: return base_class.adjoint_resource_decomp(**base_params) except re.ResourcesNotDefined: @@ -40,18 +41,20 @@ def _resource_decomp(base_class, base_params, **kwargs): return gate_types - def resource_params(self): + def resource_params(self) -> dict: return {"base_class": type(self.base), "base_params": self.base.resource_params()} @classmethod - def resource_rep(cls, base_class, base_params, **kwargs): + def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourceOp: name = f"Adjoint({base_class.__name__})".replace("Resource", "") return re.CompressedResourceOp( cls, {"base_class": base_class, "base_params": base_params}, name=name ) @staticmethod - def adjoint_resource_decomp(base_class, base_params, **kwargs): + def adjoint_resource_decomp( + base_class, base_params, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: return base_class._resource_decomp(**base_params) @@ -61,7 +64,7 @@ class ResourceControlled(ControlledOp, re.ResourceOperator): @staticmethod def _resource_decomp( base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ): + ) -> Dict[re.CompressedResourceOp, int]: try: return base_class.controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **base_params @@ -80,7 +83,7 @@ def _resource_decomp( return gate_types - def resource_params(self): + def resource_params(self) -> dict: return { "base_class": type(self.base), "base_params": self.base.resource_params(), @@ -92,7 +95,7 @@ def resource_params(self): @classmethod def resource_rep( cls, base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ): + ) -> re.CompressedResourceOp: name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( cls, @@ -117,7 +120,7 @@ def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, - ): + ) -> Dict[re.CompressedResourceOp, int]: return cls._resource_decomp( base_class, base_params, @@ -131,7 +134,9 @@ class ResourcePow(PowOperation, re.ResourceOperator): """Resource class for Pow""" @staticmethod - def _resource_decomp(base_class, z, base_params, **kwargs): + def _resource_decomp( + base_class, z, base_params, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: try: return base_class.pow_resource_decomp(z, **base_params) except re.ResourcesNotDefined: @@ -144,7 +149,7 @@ def _resource_decomp(base_class, z, base_params, **kwargs): return {base_class.resource_rep(): z} - def resource_params(self): + def resource_params(self) -> dict: return { "base_class": type(self.base), "z": self.z, @@ -152,12 +157,14 @@ def resource_params(self): } @classmethod - def resource_rep(cls, base_class, z, base_params, **kwargs): + def resource_rep(cls, base_class, z, base_params, **kwargs) -> re.CompressedResourceOp: name = f"{base_class.__name__}**{z}".replace("Resource", "") return re.CompressedResourceOp( cls, {"base_class": base_class, "z": z, "base_params": base_params}, name=name ) @classmethod - def pow_resource_decomp(cls, z0, base_class, z, base_params, **kwargs): + def pow_resource_decomp( + cls, z0, base_class, z, base_params, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: return cls._resource_decomp(base_class, z0 * z, base_params) From d8f8fba01c0e033cf1665dbae61a13f821a161d4 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 10:53:30 -0500 Subject: [PATCH 236/335] add accidentally removed part of dcostring --- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 560f7e25f77..f5a190ce99d 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -1,3 +1,5 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at From dea8747386056dbfeb71fa9b14cfb2b0eb5a328c Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 15:20:47 -0500 Subject: [PATCH 237/335] add tracking_name static method and update tests --- .../ops/op_math/symbolic.py | 23 +++++++--- .../ops/qubit/qchem_ops.py | 8 ++-- .../resource_estimation/resource_container.py | 2 +- .../resource_estimation/resource_operator.py | 5 ++ .../templates/subroutines.py | 4 ++ .../ops/op_math/test_symbolic.py | 46 ++++++++++++------- .../ops/qubit/test_non_parametric_ops.py | 41 ++++++++++++----- .../ops/qubit/test_qchem_ops.py | 17 ++++--- .../test_resource_operator.py | 4 ++ .../test_resource_tracking.py | 2 +- 10 files changed, 104 insertions(+), 48 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 767b2db2ba0..18aa7a61cbf 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -46,9 +46,8 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourceOp: - name = f"Adjoint({base_class.__name__})".replace("Resource", "") return re.CompressedResourceOp( - cls, {"base_class": base_class, "base_params": base_params}, name=name + cls, {"base_class": base_class, "base_params": base_params} ) @staticmethod @@ -57,6 +56,11 @@ def adjoint_resource_decomp( ) -> Dict[re.CompressedResourceOp, int]: return base_class._resource_decomp(**base_params) + @staticmethod + def tracking_name(base_class, base_params) -> str: + base_name = base_class.tracking_name(**base_params) + return f"Adjoint({base_name})" + class ResourceControlled(ControlledOp, re.ResourceOperator): """Resource class for Controlled""" @@ -96,7 +100,6 @@ def resource_params(self) -> dict: def resource_rep( cls, base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> re.CompressedResourceOp: - name = f"Controlled({base_class.__name__}, wires={num_ctrl_wires})".replace("Resource", "") return re.CompressedResourceOp( cls, { @@ -106,7 +109,6 @@ def resource_rep( "num_ctrl_values": num_ctrl_values, "num_work_wires": num_work_wires, }, - name=name, ) @classmethod @@ -129,6 +131,11 @@ def controlled_resource_decomp( outer_num_work_wires + num_work_wires, ) + @staticmethod + def tracking_name(base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires): + base_name = base_class.tracking_name(**base_params) + return f"C({base_name},{num_ctrl_wires},{num_ctrl_values},{num_work_wires})" + class ResourcePow(PowOperation, re.ResourceOperator): """Resource class for Pow""" @@ -158,9 +165,8 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, base_class, z, base_params, **kwargs) -> re.CompressedResourceOp: - name = f"{base_class.__name__}**{z}".replace("Resource", "") return re.CompressedResourceOp( - cls, {"base_class": base_class, "z": z, "base_params": base_params}, name=name + cls, {"base_class": base_class, "z": z, "base_params": base_params} ) @classmethod @@ -168,3 +174,8 @@ def pow_resource_decomp( cls, z0, base_class, z, base_params, **kwargs ) -> Dict[re.CompressedResourceOp, int]: return cls._resource_decomp(base_class, z0 * z, base_params) + + @staticmethod + def tracking_name(base_class, z, base_params) -> str: + base_name = base_class.tracking_name(**base_params) + return f"({base_name})**{z}" diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index d00d89dea74..1c1d356d3c8 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -136,8 +136,8 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat def _resource_decomp(*args, **kwargs): phase = re.ResourceGlobalPhase.resource_rep() double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1) - ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1) + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) gate_types = {} gate_types[phase] = 1 @@ -162,8 +162,8 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator def _resource_decomp(*args, **kwargs): phase = re.ResourceGlobalPhase.resource_rep() double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1) - ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1) + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) gate_types = {} gate_types[phase] = 1 diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index c04059f0bc2..0a208553890 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -62,7 +62,7 @@ def __init__(self, op_type, params: dict, name=None) -> None: self.op_type = op_type self.params = params self._hashable_params = _make_hashable(params) - self._name = name or self.op_type.__name__.replace("Resource", "") + self._name = name or op_type.tracking_name(**params) def __hash__(self) -> int: return hash((self._name, self._hashable_params)) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 79e87c08ee1..01ea396440c 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -121,6 +121,11 @@ def pow_resource_decomp(cls, z, *args, **kwargs) -> Dict[CompressedResourceOp, i """Returns a compressed representation of the operator raised to a power""" raise ResourcesNotDefined + @classmethod + def tracking_name(cls, *args, **kwargs) -> str: + """Returns a name used to track the operator during resource estimation.""" + return cls.__name__.replace("Resource", "") + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 43514278e63..233ec1b5535 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -56,3 +56,7 @@ def resource_params(self) -> dict: def resource_rep(cls, num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} return CompressedResourceOp(cls, params) + + @staticmethod + def tracking_name(num_wires) -> str: + return f"QFT({num_wires})" diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index 008b3e2addd..445d3ab4c2a 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -36,12 +36,16 @@ def test_resource_params(self): "base_params": base.resource_params(), } - def test_name(self): - """Test that the name of the compressed representation is correct""" + @pytest.mark.parametrize("op, expected", [ + (re.ResourceAdjoint(re.ResourceQFT([0, 1])), "Adjoint(QFT(2))"), + (re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1]))), "Adjoint(Adjoint(QFT(2)))"), + ]) + def test_tracking_name(self, op, expected): + """Test that the tracking name is correct""" + rep = op.resource_rep_from_op() + name = rep.op_type.tracking_name(**rep.params) + assert name == expected - base = re.ResourceQFT(wires=[0, 1, 2]) - op = re.ResourceAdjoint(base=base) - assert op.resource_rep_from_op()._name == "Adjoint(QFT)" @pytest.mark.parametrize( "nested_op, base_op", @@ -108,12 +112,18 @@ def test_resource_params(self): "num_work_wires": 0, } - def test_name(self): - """Test that the name of the compressed representation is correct""" + @pytest.mark.parametrize("op, expected", [ + (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT(2),1,1,0)"), + (re.ResourceControlled(re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), control_wires=[3]), "C(C(QFT(2),1,1,0),1,1,0)"), + (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1]), "C(QFT(2),2,1,0)"), + (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1], work_wires=[4]), "C(QFT(2),2,1,1)"), + ]) + def test_tracking_name(self, op, expected): + """Test that the tracking name is correct""" + rep = op.resource_rep_from_op() + name = rep.op_type.tracking_name(**rep.params) + assert name == expected - base = re.ResourceQFT(wires=[0, 1, 2]) - op = re.ResourceControlled(base=base, control_wires=[3]) - assert op.resource_rep_from_op()._name == "Controlled(QFT, wires=1)" @pytest.mark.parametrize( "nested_op, expected_op", @@ -162,12 +172,16 @@ def test_resource_params(self): "base_params": base.resource_params(), } - def test_name(self): - """Test that the name of the compressed representation is correct""" - - base = re.ResourceQFT(wires=[0, 1, 2]) - op = re.ResourcePow(base=base, z=5) - assert op.resource_rep_from_op()._name == "QFT**5" + @pytest.mark.parametrize("op, expected", [ + (re.ResourcePow(re.ResourceQFT([0, 1]), 2), "(QFT(2))**2"), + (re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), "(Adjoint(QFT(2)))**2"), + (re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), "((QFT(2))**2)**3"), + ]) + def test_tracking_name(self, op, expected): + """Test that the tracking name is correct""" + rep = op.resource_rep_from_op() + name = rep.op_type.tracking_name(**rep.params) + assert name == expected @pytest.mark.parametrize( "nested_op, base_op, z", diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 9fa36eb1f1a..b02efbc5f13 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -128,18 +128,37 @@ def test_resource_rep(self): class TestX: """Tests for ResourceX""" - @pytest.mark.parametrize( - "controlled_op, expected_op", - [ - (re.ResourceControlled(re.ResourceX(0), control_wires=[1]), re.ResourceCNOT([0, 1])), - ( - re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]), - re.ResourceToffoli([0, 1, 2]), - ), - ], - ) - def test_controlled_resources(self, controlled_op, expected_op): + def test_resources(self): + """Test that ResourceT does not implement a decomposition""" + expected = { + re.ResourceS.resource_rep(): 2, + re.ResourceHadamard.resource_rep(): 2, + } + assert re.ResourceX.resources() == expected + + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceX(0) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compact representation is correct""" + expected = re.CompressedResourceOp(re.ResourceX, {}) + assert re.ResourceX.resource_rep() == expected + + def test_single_controlled_resources(self): + """Test that the controlled_resource_decomp method dispatches correctly.""" + controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1]) + controlled_cp = controlled_op.resource_rep_from_op() + + with pytest.raises(re.ResourcesNotDefined): + controlled_cp.op_type.resources(**controlled_cp.params) + + def test_double_controlled_resources(self): """Test that the controlled_resource_decomp method dispatches correctly.""" + controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]) + expected_op = re.ResourceToffoli([0, 1, 2]) controlled_cp = controlled_op.resource_rep_from_op() controlled_resources = controlled_cp.op_type.resources(**controlled_cp.params) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index 0fbaa7926dc..7cd8eee6afe 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -18,7 +18,6 @@ # pylint: disable=use-implicit-booleaness-not-comparison,no-self-use -# TODO: implement in resource_symbolic_ops branch class TestSingleExcitation: """Tests for the ResourceSingleExcitation class.""" @@ -185,8 +184,8 @@ def test_resources(self): expected = { re.ResourceGlobalPhase.resource_rep(): 1, re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, } assert re.ResourceDoubleExcitationMinus.resources() == expected @@ -206,8 +205,8 @@ def test_resources_from_rep(self): expected = { re.ResourceGlobalPhase.resource_rep(): 1, re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, } op_compressed_rep = op.resource_rep_from_op() op_resource_type = op_compressed_rep.op_type @@ -223,8 +222,8 @@ def test_resources(self): expected = { re.ResourceGlobalPhase.resource_rep(): 1, re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, } assert re.ResourceDoubleExcitationPlus.resources() == expected @@ -244,8 +243,8 @@ def test_resources_from_rep(self): expected = { re.ResourceGlobalPhase.resource_rep(): 1, re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1): 2, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, } op_compressed_rep = op.resource_rep_from_op() op_resource_type = op_compressed_rep.op_type diff --git a/pennylane/labs/tests/resource_estimation/test_resource_operator.py b/pennylane/labs/tests/resource_estimation/test_resource_operator.py index f88026f86b7..27c8428de57 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_operator.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_operator.py @@ -120,5 +120,9 @@ def resource_params(self): def resource_rep(cls, foo, bar): return re.CompressedResourceOp(cls, {"foo": foo, "bar": bar}) + @staticmethod + def tracking_name(foo, bar): + return f"DummyClass({foo}, {bar})" + op = DummyClass() assert op.resource_rep_from_op() == op.__class__.resource_rep(**op.resource_params()) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index 4ffd8af8ca8..e4ad71b2b0c 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -230,7 +230,7 @@ def test_clean_gate_counts(self): }, ) - expected_clean_counts = defaultdict(int, {"CNOT": 1, "Hadamard": 3, "QFT": 5}) + expected_clean_counts = defaultdict(int, {"CNOT": 1, "Hadamard": 3, "QFT(3)": 4, "QFT(5)": 1}) assert _clean_gate_counts(gate_counts) == expected_clean_counts From 3769f8a6f6527095b0b308c950fe42e10e3cef7b Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 26 Nov 2024 15:21:35 -0500 Subject: [PATCH 238/335] formatting --- .../ops/op_math/symbolic.py | 4 +- .../ops/op_math/test_symbolic.py | 63 ++++++++++++++----- .../ops/qubit/test_non_parametric_ops.py | 1 - .../test_resource_tracking.py | 4 +- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 18aa7a61cbf..39ec6d7a281 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -46,9 +46,7 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourceOp: - return re.CompressedResourceOp( - cls, {"base_class": base_class, "base_params": base_params} - ) + return re.CompressedResourceOp(cls, {"base_class": base_class, "base_params": base_params}) @staticmethod def adjoint_resource_decomp( diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index 445d3ab4c2a..a9526fb1dce 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -36,17 +36,22 @@ def test_resource_params(self): "base_params": base.resource_params(), } - @pytest.mark.parametrize("op, expected", [ - (re.ResourceAdjoint(re.ResourceQFT([0, 1])), "Adjoint(QFT(2))"), - (re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1]))), "Adjoint(Adjoint(QFT(2)))"), - ]) + @pytest.mark.parametrize( + "op, expected", + [ + (re.ResourceAdjoint(re.ResourceQFT([0, 1])), "Adjoint(QFT(2))"), + ( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1]))), + "Adjoint(Adjoint(QFT(2)))", + ), + ], + ) def test_tracking_name(self, op, expected): """Test that the tracking name is correct""" rep = op.resource_rep_from_op() name = rep.op_type.tracking_name(**rep.params) assert name == expected - @pytest.mark.parametrize( "nested_op, base_op", [ @@ -112,19 +117,40 @@ def test_resource_params(self): "num_work_wires": 0, } - @pytest.mark.parametrize("op, expected", [ - (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT(2),1,1,0)"), - (re.ResourceControlled(re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), control_wires=[3]), "C(C(QFT(2),1,1,0),1,1,0)"), - (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1]), "C(QFT(2),2,1,0)"), - (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1], work_wires=[4]), "C(QFT(2),2,1,1)"), - ]) + @pytest.mark.parametrize( + "op, expected", + [ + (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT(2),1,1,0)"), + ( + re.ResourceControlled( + re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), + control_wires=[3], + ), + "C(C(QFT(2),1,1,0),1,1,0)", + ), + ( + re.ResourceControlled( + re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1] + ), + "C(QFT(2),2,1,0)", + ), + ( + re.ResourceControlled( + re.ResourceQFT([0, 1]), + control_wires=[2, 3], + control_values=[0, 1], + work_wires=[4], + ), + "C(QFT(2),2,1,1)", + ), + ], + ) def test_tracking_name(self, op, expected): """Test that the tracking name is correct""" rep = op.resource_rep_from_op() name = rep.op_type.tracking_name(**rep.params) assert name == expected - @pytest.mark.parametrize( "nested_op, expected_op", [ @@ -172,11 +198,14 @@ def test_resource_params(self): "base_params": base.resource_params(), } - @pytest.mark.parametrize("op, expected", [ - (re.ResourcePow(re.ResourceQFT([0, 1]), 2), "(QFT(2))**2"), - (re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), "(Adjoint(QFT(2)))**2"), - (re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), "((QFT(2))**2)**3"), - ]) + @pytest.mark.parametrize( + "op, expected", + [ + (re.ResourcePow(re.ResourceQFT([0, 1]), 2), "(QFT(2))**2"), + (re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), "(Adjoint(QFT(2)))**2"), + (re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), "((QFT(2))**2)**3"), + ], + ) def test_tracking_name(self, op, expected): """Test that the tracking name is correct""" rep = op.resource_rep_from_op() diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index b02efbc5f13..618a61fce6f 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -136,7 +136,6 @@ def test_resources(self): } assert re.ResourceX.resources() == expected - def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceX(0) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index e4ad71b2b0c..396f9149cb4 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -230,7 +230,9 @@ def test_clean_gate_counts(self): }, ) - expected_clean_counts = defaultdict(int, {"CNOT": 1, "Hadamard": 3, "QFT(3)": 4, "QFT(5)": 1}) + expected_clean_counts = defaultdict( + int, {"CNOT": 1, "Hadamard": 3, "QFT(3)": 4, "QFT(5)": 1} + ) assert _clean_gate_counts(gate_counts) == expected_clean_counts From 88a6af0b7f9bf9f3d67269829804f5167c9182fd Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 27 Nov 2024 10:24:44 -0500 Subject: [PATCH 239/335] documentation updates --- .../labs/resource_estimation/__init__.py | 3 - .../ops/qubit/parametric_ops_multi_qubit.py | 14 +-- .../ops/qubit/qchem_ops.py | 99 +++++++++++++++++-- 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index 4c98a369656..708e27bf814 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -42,9 +42,6 @@ ~ResourceCH ~ResourceCNOT ~ResourceControlledPhaseShift - ~ResourceDoubleExcitation - ~ResourceDoubleExcitationMinus - ~ResourceDoubleExcitationPlus ~ResourceCRot ~ResourceCRX ~ResourceCRY diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 797e8d71543..49ebae4e829 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -19,7 +19,7 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): - r"""Resource class for MultiRZ + r"""Resource class for the MultiRZ gate. Resources: .. math:: @@ -47,7 +47,7 @@ def resource_rep(cls, num_wires, **kwargs): class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): - r"""Resource class for PauliRot + r"""Resource class for the PauliRot gate. Resources: .. math:: @@ -94,7 +94,7 @@ def resource_rep(cls, pauli_word, **kwargs): class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): - r"""Resource class for IsingXX + r"""Resource class for the IsingXX gate. Resources: Ising XX coupling gate @@ -128,7 +128,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): - r"""Resource class for IsingYY + r"""Resource class for the IsingYY gate. Resources: Ising YY coupling gate @@ -162,7 +162,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): - r"""Resource class for IsingXY + r"""Resource class for the IsingXY gate. Resources: Ising (XX + YY) coupling gate @@ -200,7 +200,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): - r"""Resource class for IsingZZ + r"""Resource class for the IsingZZ gate. Resources: Ising ZZ coupling gate @@ -234,7 +234,7 @@ def resource_rep(cls, *args, **kwargs): class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): - r"""Resource class for PSWAP + r"""Resource class for the PSWAP gate. Resources: .. math:: PSWAP(\phi) = \begin{bmatrix} diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 2f845a86088..e45efd9d4d1 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -19,7 +19,19 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): - """Resource class for the SingleExcitation gate.""" + r"""Resource class for the SingleExcitation gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & 1 + \end{bmatrix}. + + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -35,7 +47,18 @@ def resource_rep(cls, **kwargs): class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperator): - """Resource class for the SingleExcitationMinus gate.""" + r"""Resource class for the SingleExcitationMinus gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U_-(\phi) = \begin{bmatrix} + e^{-i\phi/2} & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{-i\phi/2} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -61,7 +84,18 @@ def resource_rep(cls, **kwargs): class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator): - """Resource class for the SingleExcitationPlus gate.""" + r"""Resource class for the SingleExcitationPlus gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U_+(\phi) = \begin{bmatrix} + e^{i\phi/2} & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{i\phi/2} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -87,7 +121,16 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): - """Resource class for the DoubleExcitation gate.""" + r"""Resource class for the DoubleExcitation gate. + + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + + &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle + \sin(\phi/2) |1100\rangle\\ + &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle - \sin(\phi/2) |0011\rangle, + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -112,7 +155,18 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator): - """Resource class for the DoubleExcitationMinus gate.""" + r"""Resource class for the DoubleExcitationMinus gate. + + + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + + &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ + &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ + &|x\rangle \rightarrow e^{-i\phi/2} |x\rangle, + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -127,7 +181,17 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator): - """Resource class for the DoubleExcitationPlus gate.""" + r"""Resource class for the DoubleExcitationPlus gate. + + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + + &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ + &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ + &|x\rangle \rightarrow e^{i\phi/2} |x\rangle, + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -143,7 +207,15 @@ def resource_rep(cls, **kwargs): class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): - """Resource class for the OrbitalRotation gate.""" + r"""Resource class for the OrbitalRotation gate. + + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + &|\Phi_{0}\rangle = \cos(\phi/2)|\Phi_{0}\rangle - \sin(\phi/2)|\Phi_{1}\rangle\\ + &|\Phi_{1}\rangle = \cos(\phi/2)|\Phi_{0}\rangle + \sin(\phi/2)|\Phi_{1}\rangle, + """ @staticmethod def _resource_decomp(**kwargs): @@ -165,7 +237,18 @@ def resource_rep(cls, **kwargs): class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): - """Resource class for the FermionicSWAP gate.""" + r"""Resource class for the FermionicSWAP gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & e^{i \phi/2} \cos(\phi/2) & -ie^{i \phi/2} \sin(\phi/2) & 0 \\ + 0 & -ie^{i \phi/2} \sin(\phi/2) & e^{i \phi/2} \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{i \phi} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): From dfecfc65c98b827c255e9d739f952325465b83ae Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 27 Nov 2024 10:35:12 -0500 Subject: [PATCH 240/335] update documentation --- pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index e45efd9d4d1..47a29dc5e0a 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -1,5 +1,3 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at From 078464bded2843c736ac9763d714880f75164a57 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 27 Nov 2024 12:05:06 -0500 Subject: [PATCH 241/335] more resource methods --- .../ops/qubit/non_parametric_ops.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index f5a190ce99d..bea802e7193 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -47,6 +47,10 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceS(qml.S, re.ResourceOperator): """Resource class for the S gate.""" @@ -230,6 +234,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs @@ -239,6 +247,10 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceZ(qml.Z, re.ResourceOperator): """Resource class for the Z gate.""" @@ -259,6 +271,10 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs @@ -270,3 +286,7 @@ def controlled_resource_decomp( return re.ResourceCCZ.resources(**kwargs) raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} From 60c08e29088b9b3ff5029a16fff18d80b480522a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 27 Nov 2024 14:04:46 -0500 Subject: [PATCH 242/335] more resource methods --- .../ops/op_math/controlled_ops.py | 169 +++++++++++++++++- .../ops/qubit/parametric_ops_single_qubit.py | 34 ++-- .../ops/op_math/test_controlled_ops.py | 22 +++ 3 files changed, 211 insertions(+), 14 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index b2633d03fa9..eb07e761a2b 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -62,6 +62,18 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceHadamard.controlled_resource_decomp( + num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + class ResourceCY(qml.CY, re.ResourceOperator): r"""Resource class for CY gate. @@ -94,6 +106,22 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceY.controlled_resource_decomp( + num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCZ(qml.CZ, re.ResourceOperator): r"""Resource class for CZ @@ -126,6 +154,25 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCCZ.resources() + + return re.ResourceControlled.resources( + re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCSWAP(qml.CSWAP, re.ResourceOperator): r"""Resource class for CSWAP @@ -165,6 +212,22 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCCZ(qml.CCZ, re.ResourceOperator): r"""Resource class for CCZ @@ -200,6 +263,18 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(cls, **kwargs): return {cls.resource_rep(): 1} + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCNOT(qml.CNOT, re.ResourceOperator): """Resource class for the CNOT gate. @@ -375,6 +450,34 @@ def resource_rep( }, ) + @classmethod + def adjoint_resource_decomp( + cls, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(num_ctrl_wires, num_ctrl_values, num_work_wires) + + @classmethod + def controlled_resource_decomp( + cls, + outer_num_ctrl_wires, + outer_num_ctrl_values, + outer_num_work_wires, + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + return cls.resources( + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ) + + @classmethod + def pow_resource_decomp( + cls, z, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): z % 2} + class ResourceCRX(qml.CRX, re.ResourceOperator): r"""Resource class for CRX @@ -415,6 +518,18 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourceRX, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + class ResourceCRY(qml.CRY, re.ResourceOperator): r"""Resource class for CRY @@ -446,6 +561,22 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourceRY, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + class ResourceCRZ(qml.CRZ, re.ResourceOperator): r"""Resource class for CRZ @@ -477,6 +608,22 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourceRZ, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + class ResourceCRot(qml.CRot, re.ResourceOperator): r"""Resource class for CRot @@ -507,6 +654,18 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourceRot, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for the ControlledPhaseShift gate. @@ -551,6 +710,14 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: return cls.resources(**kwargs) + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return re.ResourceControlled.resources( + re.ResourcePhaseShift, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + @classmethod def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} + return cls.resources(**kwargs) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 4abd0f010f2..fb05e27bd97 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -94,9 +94,9 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=config["error_rx"]) + @classmethod + def adjoint_resource_decomp(cls, config) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(config) @staticmethod def controlled_resource_decomp( @@ -107,6 +107,10 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined + @classmethod + def pow_resource_decomp(cls, z, config) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(config) + class ResourceRY(qml.RY, re.ResourceOperator): """Resource class for the RY gate.""" @@ -122,9 +126,9 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=config["error_ry"]) + @classmethod + def adjoint_resource_decomp(cls, config) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(config) @staticmethod def controlled_resource_decomp( @@ -135,6 +139,10 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined + @classmethod + def pow_resource_decomp(cls, z, config) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(config) + class ResourceRZ(qml.RZ, re.ResourceOperator): r"""Resource class for the RZ gate. @@ -155,9 +163,9 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: - return _rotation_resources(epsilon=config["error_rz"]) + @classmethod + def adjoint_resource_decomp(cls, config) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(config) @staticmethod def controlled_resource_decomp( @@ -168,6 +176,10 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined + @classmethod + def pow_resource_decomp(cls, z, config) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(config) + class ResourceRot(qml.Rot, re.ResourceOperator): """Resource class for the Rot gate.""" @@ -200,7 +212,3 @@ def controlled_resource_decomp( return re.ResourceCRot.resources() raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 07d95ef8c9e..b1924e01a25 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -81,6 +81,28 @@ def test_resources_from_rep(self, phi, wires): assert op_compressed_rep_type.resources(**op_resource_params) == expected + @pytest.mark.parametrize("phi, wires", params) + def test_adjoint_decomp(self, phi, wires): + """Test that the adjoint resources are correct.""" + + op = re.ResourceControlledPhaseShift(phi, wires) + rep = op.resource_rep_from_op() + res = rep.op_type.resources(**rep.params) + adjoint_res = rep.op_type.adjoint_resource_decomp(**rep.params) + + assert res == adjoint_res + + @pytest.mark.parametrize("phi, wires", params) + def test_pow_decomp(self, phi, wires): + """Test that the adjoint resources are correct.""" + + op = re.ResourceControlledPhaseShift(phi, wires) + rep = op.resource_rep_from_op() + res = rep.op_type.resources(**rep.params) + adjoint_res = rep.op_type.pow_resource_decomp(2, **rep.params) + + assert res == adjoint_res + class TestCNOT: """Test ResourceCNOT""" From e2b98b09450063c22451078d454177eb2d5963f1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 08:58:17 -0500 Subject: [PATCH 243/335] tests for cnot --- .../ops/op_math/controlled_ops.py | 2 +- .../ops/op_math/test_controlled_ops.py | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index eb07e761a2b..ef616721eba 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -304,7 +304,7 @@ def controlled_resource_decomp( cls, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceToffoli.resources() + return {re.ResourceToffoli.resource_rep(): 1} return re.ResourceMultiControlledX.resources( num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index b1924e01a25..2d664de6ef8 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -109,12 +109,14 @@ class TestCNOT: def test_resources(self): """Test that the resources method is not implemented""" + op = re.ResourceCNOT([0, 1]) with pytest.raises(re.ResourcesNotDefined): op.resources() def test_resource_rep(self): """Test the compressed representation""" + op = re.ResourceCNOT([0, 1]) expected = re.CompressedResourceOp(re.ResourceCNOT, {}) assert op.resource_rep() == expected @@ -123,3 +125,38 @@ def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceCNOT([0, 1]) assert op.resource_params() == {} + + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct.""" + + expected = {re.ResourceCNOT.resource_rep(): 1} + assert re.ResourceCNOT.adjoint_resource_decomp() == expected + + cnot = re.ResourceCNOT([0, 1]) + cnot_dag = re.ResourceAdjoint(re.ResourceCNOT([0, 1])) + + r1 = re.get_resources(cnot) + r2 = re.get_resources(cnot_dag) + + assert r1 == r2 + + def test_controlled_decomp(self): + """Test that the controlled decomposition is correct.""" + + expected = {re.ResourceToffoli.resource_rep(): 1} + assert re.ResourceCNOT.controlled_resource_decomp(1, 1, 0) == expected + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + + expected = {re.ResourceCNOT.resource_rep(): z % 2} + assert re.ResourceCNOT.pow_resource_decomp(z) == expected + + cnot = re.ResourceCNOT([0, 1]) + cnot_pow = re.ResourcePow(re.ResourceCNOT([0, 1]), z) + + r1 = re.get_resources(cnot) * (z % 2) + r2 = re.get_resources(cnot_pow) + + assert r1 == r2 From 6177e54982ed58eeb4068dc6a1bf54f6f8e3460b Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 11:26:25 -0500 Subject: [PATCH 244/335] tests for resource decomps --- .../ops/op_math/controlled_ops.py | 46 ++++-- .../ops/qubit/non_parametric_ops.py | 18 +- .../ops/qubit/parametric_ops_single_qubit.py | 46 +++--- .../ops/qubit/test_non_parametric_ops.py | 156 +++++++++++++++++- .../qubit/test_parametric_ops_single_qubit.py | 78 +++++++++ 5 files changed, 286 insertions(+), 58 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index ef616721eba..b3aad6a1430 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -501,13 +501,13 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - h = re.ResourceHadamard.resource_rep() rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() cnot = re.ResourceCNOT.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 2 - gate_types[h] = 2 + gate_types[ry] = 2 return gate_types @@ -520,15 +520,21 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourceRX, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceRX, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ) + } + + @classmethod + def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} class ResourceCRY(qml.CRY, re.ResourceOperator): @@ -563,19 +569,21 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourceRY, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceRY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} class ResourceCRZ(qml.CRZ, re.ResourceOperator): @@ -594,10 +602,10 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep() + phase = re.ResourcePhaseShift.resource_rep() gate_types[cnot] = 2 - gate_types[rz] = 2 + gate_types[phase] = 2 return gate_types @@ -610,19 +618,21 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourceRZ, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceRZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} class ResourceCRot(qml.CRot, re.ResourceOperator): diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index bea802e7193..b1adc302c68 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -43,7 +43,7 @@ def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCH.resources(**kwargs) + return {re.ResourceCH.resource_rep(): 1} raise re.ResourcesNotDefined @@ -58,7 +58,7 @@ class ResourceS(qml.S, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - t = ResourceT.resource_rep(**kwargs) + t = ResourceT.resource_rep() gate_types[t] = 2 return gate_types @@ -138,7 +138,7 @@ def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCSWAP.resources(**kwargs) + return {re.ResourceCSWAP.resource_rep(): 1} raise re.ResourcesNotDefined @@ -177,8 +177,8 @@ class ResourceX(qml.X, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - s = re.ResourceS.resource_rep(**kwargs) - h = re.ResourceHadamard.resource_rep(**kwargs) + s = re.ResourceS.resource_rep() + h = re.ResourceHadamard.resource_rep() gate_types = {} gate_types[s] = 2 @@ -200,11 +200,11 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs): if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCNOT.resources(**kwargs) + return {re.ResourceCNOT.resource_rep(): 1} if num_ctrl_wires == 2 and num_ctrl_values == 2: - return re.ResourceToffoli.resources(**kwargs) + return {re.ResourceToffoli.resource_rep(): 1} - return re.ResourceMultiControlledX.resources( + return re.ResourceMultiControlledX.resource_rep( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) @@ -257,7 +257,7 @@ class ResourceZ(qml.Z, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - s = re.ResourceS.resource_rep(**kwargs) + s = re.ResourceS.resource_rep() gate_types = {} gate_types[s] = 2 diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index fb05e27bd97..66238d2aba4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -46,7 +46,7 @@ class ResourcePhaseShift(qml.PhaseShift, re.ResourceOperator): """ @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} rz = re.ResourceRZ.resource_rep() global_phase = re.ResourceGlobalPhase.resource_rep() @@ -71,7 +71,7 @@ def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceControlledPhaseShift.resources() + return {re.ResourceControlledPhaseShift.resource_rep(): 1} raise re.ResourcesNotDefined @@ -84,7 +84,7 @@ class ResourceRX(qml.RX, re.ResourceOperator): """Resource class for the RX gate.""" @staticmethod - def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rx"]) def resource_params(self) -> dict: @@ -95,28 +95,28 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, config) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(config) + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCRX.resources() + return {re.ResourceCRX.resource_rep(): 1} raise re.ResourcesNotDefined @classmethod - def pow_resource_decomp(cls, z, config) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(config) + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} class ResourceRY(qml.RY, re.ResourceOperator): """Resource class for the RY gate.""" @staticmethod - def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_ry"]) def resource_params(self) -> dict: @@ -127,21 +127,21 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, config) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(config) + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCRY.resources() + return {re.ResourceCRY.resource_rep(): 1} raise re.ResourcesNotDefined @classmethod - def pow_resource_decomp(cls, z, config) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(config) + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} class ResourceRZ(qml.RZ, re.ResourceOperator): @@ -153,7 +153,7 @@ class ResourceRZ(qml.RZ, re.ResourceOperator): """ @staticmethod - def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rz"]) def resource_params(self) -> dict: @@ -164,28 +164,28 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, config) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(config) + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCRZ.resources() + return {re.ResourceCRZ.resource_rep(): 1} raise re.ResourcesNotDefined @classmethod - def pow_resource_decomp(cls, z, config) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(config) + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} class ResourceRot(qml.Rot, re.ResourceOperator): """Resource class for the Rot gate.""" @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: rx = ResourceRX.resource_rep() ry = ResourceRY.resource_rep() rz = ResourceRZ.resource_rep() @@ -202,13 +202,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCRot.resources() + return {re.ResourceCRot.resource_rep(): 1} raise re.ResourcesNotDefined diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 618a61fce6f..eeafd9398e5 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -40,6 +40,39 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(re.ResourceHadamard, {}) assert re.ResourceHadamard.resource_rep() == expected + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct.""" + h = re.ResourceHadamard(0) + h_dag = re.ResourceAdjoint(re.ResourceHadamard(0)) + + assert re.get_resources(h) == re.get_resources(h_dag) + + def test_controlled_decomp(self): + """Test that the controlled decomposition is correct.""" + expected = {re.ResourceCH.resource_rep(): 1} + assert re.ResourceHadamard.controlled_resource_decomp(1, 1, 0) == expected + + controlled_h = re.ResourceControlled(re.ResourceHadamard(0), control_wires=[1]) + ch = re.ResourceCH([0, 1]) + + r1 = re.get_resources(controlled_h) + r2 = re.get_resources(ch) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceHadamard.resource_rep(): z % 2} + assert re.ResourceHadamard.pow_resource_decomp(z) == expected + + h = re.ResourceHadamard(0) + pow_h = re.ResourcePow(re.ResourceHadamard(0), z) + + r1 = re.get_resources(h) * (z % 2) + r2 = re.get_resources(pow_h) + + assert r1 == r2 + class TestSWAP: """Tests for ResourceSWAP""" @@ -73,6 +106,39 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct.""" + swap = re.ResourceSWAP([0, 1]) + swap_dag = re.ResourceAdjoint(re.ResourceSWAP([0, 1])) + + assert re.get_resources(swap) == re.get_resources(swap_dag) + + def test_controlled_decomp(self): + """Test that the controlled decomposition is correct.""" + expected = {re.ResourceCSWAP.resource_rep(): 1} + assert re.ResourceSWAP.controlled_resource_decomp(1, 1, 0) == expected + + controlled_swap = re.ResourceControlled(re.ResourceSWAP([0, 1]), control_wires=[2]) + cswap = re.ResourceCSWAP([0, 1, 2]) + + r1 = re.get_resources(controlled_swap) + r2 = re.get_resources(cswap) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceSWAP.resource_rep(): z % 2} + assert re.ResourceSWAP.pow_resource_decomp(z) == expected + + swap = re.ResourceSWAP([0, 1]) + pow_swap = re.ResourcePow(re.ResourceSWAP([0, 1]), z) + + r1 = re.get_resources(swap) * (z % 2) + r2 = re.get_resources(pow_swap) + + assert r1 == r2 + class TestS: """Tests for ResourceS""" @@ -104,6 +170,32 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceS.resource_rep(): 3} + assert re.ResourceS.adjoint_resource_decomp() == expected + + s = re.ResourceS(0) + s_dag = re.ResourceAdjoint(s) + + r1 = re.get_resources(s) * 3 + r2 = re.get_resources(s_dag) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceS.resource_rep(): z % 4} + assert re.ResourceS.pow_resource_decomp(z) == expected + + s = re.ResourceS(0) + pow_s = re.ResourcePow(s, z) + + r1 = re.get_resources(s) * (z % 4) + r2 = re.get_resources(pow_s) + + assert r1 == r2 + class TestT: """Tests for ResourceT""" @@ -124,6 +216,32 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(re.ResourceT, {}) assert re.ResourceT.resource_rep() == expected + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceT.resource_rep(): 7} + assert re.ResourceT.adjoint_resource_decomp() == expected + + t = re.ResourceT(0) + t_dag = re.ResourceAdjoint(t) + + r1 = re.get_resources(t) * 7 + r2 = re.get_resources(t_dag) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceT.resource_rep(): z % 8} + assert re.ResourceT.pow_resource_decomp(z) == expected + + t = re.ResourceT(0) + pow_t = re.ResourcePow(t, z) + + r1 = re.get_resources(t) * (z % 8) + r2 = re.get_resources(pow_t) + + assert r1 == r2 + class TestX: """Tests for ResourceX""" @@ -149,20 +267,42 @@ def test_resource_rep(self): def test_single_controlled_resources(self): """Test that the controlled_resource_decomp method dispatches correctly.""" controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1]) - controlled_cp = controlled_op.resource_rep_from_op() - with pytest.raises(re.ResourcesNotDefined): - controlled_cp.op_type.resources(**controlled_cp.params) + cnot = re.ResourceCNOT([0, 1]) + assert re.get_resources(controlled_op) == re.get_resources(cnot) def test_double_controlled_resources(self): """Test that the controlled_resource_decomp method dispatches correctly.""" controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]) expected_op = re.ResourceToffoli([0, 1, 2]) - controlled_cp = controlled_op.resource_rep_from_op() - controlled_resources = controlled_cp.op_type.resources(**controlled_cp.params) + r1 = re.get_resources(controlled_op) + r2 = re.get_resources(expected_op) + + assert r1 == r2 + + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceX.resource_rep(): 1} + assert re.ResourceX.adjoint_resource_decomp() == expected + + x = re.ResourceX(0) + x_dag = re.ResourceAdjoint(x) + + r1 = re.get_resources(x) + r2 = re.get_resources(x_dag) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceX.resource_rep(): z % 2} + assert re.ResourceX.pow_resource_decomp(z) == expected + + x = re.ResourceX(0) + pow_x = re.ResourcePow(x, z) - expected_cp = expected_op.resource_rep_from_op() - expected_resources = expected_cp.op_type.resources(**expected_cp.params) + r1 = re.get_resources(x) * (z % 2) + r2 = re.get_resources(pow_x) - assert controlled_resources == expected_resources + assert r1 == r2 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index f93c363dbde..3c94748ded5 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -82,6 +82,70 @@ def test_resource_params(self, resource_class, epsilon): # pylint: disable=unus op = resource_class(1.24, wires=0) assert op.resource_params() == {} + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + def test_adjoint_decomposition(self, resource_class, epsilon): + """Test that the adjoint decompositions are correct.""" + + expected = {resource_class.resource_rep(): 1} + assert resource_class.adjoint_resource_decomp() == expected + + op = resource_class(1.24, wires=0) + dag = re.ResourceAdjoint(op) + + label = "error_" + resource_class.__name__.replace("Resource", "").lower() + config = {label: epsilon} + + r1 = re.get_resources(op, config=config) + r2 = re.get_resources(dag, config=config) + + assert r1 == r2 + + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomposition(self, resource_class, epsilon, z): + """Test that the pow decompositions are correct.""" + + expected = {resource_class.resource_rep(): 1} + assert resource_class.pow_resource_decomp(z) == expected + + op = resource_class(1.24, wires=0) + dag = re.ResourcePow(op, z) + + label = "error_" + resource_class.__name__.replace("Resource", "").lower() + config = {label: epsilon} + + r1 = re.get_resources(op, config=config) + r2 = re.get_resources(dag, config=config) + + assert r1 == r2 + + params_classes = ( + (re.ResourceRX, re.ResourceCRX), + (re.ResourceRY, re.ResourceCRY), + (re.ResourceRZ, re.ResourceCRZ), + ) + + @pytest.mark.parametrize("resource_class, controlled_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + def test_controlled_decomposition(self, resource_class, controlled_class, epsilon): + """Test that the controlled decompositions are correct.""" + expected = {controlled_class.resource_rep(): 1} + assert resource_class.controlled_resource_decomp(1, 1, 0) == expected + + op = resource_class(1.24, wires=0) + c_op = re.ResourceControlled(op, control_wires=[1]) + + c = controlled_class(1.24, wires=[0, 1]) + + config = {"error_rx": epsilon, "error_ry": epsilon, "error_rz": epsilon} + + r1 = re.get_resources(c, config=config) + r2 = re.get_resources(c_op, config=config) + + assert r1 == r2 + class TestRot: """Test ResourceRot""" @@ -119,3 +183,17 @@ def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) assert op.resource_params() == {} + + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct""" + + expected = {re.ResourceRot.resource_rep(): 1} + assert re.ResourceRot.adjoint_resource_decomp() == expected + + op = re.ResourceRot(1.24, 1.25, 1.26, wires=0) + dag = re.ResourceAdjoint(op) + + r1 = re.get_resources(op) + r2 = re.get_resources(dag) + + assert r1 == r2 From 5723ae40a8ebe996b06d791f9e00ac2af70a057a Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 12:36:44 -0500 Subject: [PATCH 245/335] update controlled decomps --- .../ops/op_math/controlled_ops.py | 92 ++++++++++++------- .../ops/op_math/test_symbolic.py | 11 +-- 2 files changed, 60 insertions(+), 43 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index b3aad6a1430..c6d1dd629da 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -70,9 +70,15 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceHadamard.controlled_resource_decomp( - num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceHadamard, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} class ResourceCY(qml.CY, re.ResourceOperator): @@ -114,9 +120,11 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceY.controlled_resource_decomp( - num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -163,11 +171,13 @@ def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCCZ.resources() + return {re.ResourceCCZ.resource_rep(): 1} - return re.ResourceControlled.resources( - re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -220,9 +230,11 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -267,9 +279,11 @@ def adjoint_resource_decomp(cls, **kwargs): def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -306,9 +320,11 @@ def controlled_resource_decomp( if num_ctrl_wires == 1 and num_ctrl_values == 1: return {re.ResourceToffoli.resource_rep(): 1} - return re.ResourceMultiControlledX.resources( - num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -398,9 +414,11 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceMultiControlledX.resources( - num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires - ) + return { + re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -466,11 +484,13 @@ def controlled_resource_decomp( num_ctrl_values, num_work_wires, ) -> Dict[re.CompressedResourceOp, int]: - return cls.resources( - outer_num_ctrl_wires + num_ctrl_wires, - outer_num_ctrl_values + num_ctrl_values, - outer_num_work_wires + num_work_wires, - ) + return { + cls.resource_rep( + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ): 1 + } @classmethod def pow_resource_decomp( @@ -529,7 +549,7 @@ def controlled_resource_decomp( return { re.ResourceControlled.resource_rep( re.ResourceRX, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + ): 1 } @classmethod @@ -672,9 +692,11 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourceRot, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourceRot, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): @@ -724,9 +746,11 @@ def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int] def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return re.ResourceControlled.resources( - re.ResourcePhaseShift, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ) + return { + re.ResourceControlled.resource_rep( + re.ResourcePhaseShift, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } @classmethod def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index a9526fb1dce..f7b31f6f691 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -171,15 +171,8 @@ def test_tracking_name(self, op, expected): def test_nested_controls(self, nested_op, expected_op): """Test the resources for nested Controlled operators.""" - nested_rep = nested_op.resource_rep_from_op() - nested_params = nested_rep.params - nested_type = nested_rep.op_type - nested_resources = nested_type.resources(**nested_params) - - expected_rep = expected_op.resource_rep_from_op() - expected_params = expected_rep.params - expected_type = expected_rep.op_type - expected_resources = expected_type.resources(**expected_params) + nested_resources = re.get_resources(nested_op) + expected_resources = re.get_resources(expected_op) assert nested_resources == expected_resources From 0a7ca0ea54f3fecaa01c2f7690e34024ea323134 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 12:57:48 -0500 Subject: [PATCH 246/335] revert identity --- .../labs/resource_estimation/ops/identity.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 173a2ae6f81..7f9b59729db 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,18 +34,6 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp(): - return {} - - @staticmethod - def controlled_resource_decomp(): - return {} - - @staticmethod - def pow_resource_decomp(): - return {} - class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -60,15 +48,3 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @staticmethod - def adjoint_resource_decomp(): - return {} - - @staticmethod - def controlled_resource_decomp(): - return {} - - @staticmethod - def pow_resource_decomp(): - return {} From 854d9e5c3f3304b39d0e1dc28e8adb93db21b5f1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:02:12 -0500 Subject: [PATCH 247/335] revert identity changes --- .../labs/resource_estimation/ops/identity.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 7f9b59729db..b6bcb1c25dd 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,6 +34,18 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -48,3 +60,15 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} From f58da1d2ef6c1466d4d3d8451af488b57bd47dc2 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:04:44 -0500 Subject: [PATCH 248/335] for rebasing From 0c2d23717e2b4d801fdd983c065f10d805475d30 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:09:06 -0500 Subject: [PATCH 249/335] revert identity --- .../labs/resource_estimation/ops/identity.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index b6bcb1c25dd..7f9b59729db 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,18 +34,6 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -60,15 +48,3 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} From c7d5ca1ff89aa93225904fe68f3f4a18f66c575b Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:10:47 -0500 Subject: [PATCH 250/335] undo --- .../labs/resource_estimation/ops/identity.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index b6bcb1c25dd..7f9b59729db 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,18 +34,6 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -60,15 +48,3 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} From 877da469fe3d50308614b83adb9d22086ad94fd6 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:13:02 -0500 Subject: [PATCH 251/335] trying again --- .../labs/resource_estimation/ops/identity.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 7f9b59729db..b6bcb1c25dd 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,6 +34,18 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -48,3 +60,15 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} From 8c640f153291bee1bebbbe6479a74da222ae2468 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:16:55 -0500 Subject: [PATCH 252/335] revert again --- .../labs/resource_estimation/ops/identity.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index b6bcb1c25dd..7f9b59729db 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,18 +34,6 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -60,15 +48,3 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} From fb3b14e964a718c0050d62b8f72a23c078f5bf46 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:22:06 -0500 Subject: [PATCH 253/335] again --- .../labs/resource_estimation/ops/identity.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 7f9b59729db..b6bcb1c25dd 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,6 +34,18 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -48,3 +60,15 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} From e6b10d9d5f09bcae5de0ec67f52b643a5f7503b8 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:44:16 -0500 Subject: [PATCH 254/335] identity --- .../labs/resource_estimation/ops/identity.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index b6bcb1c25dd..7f9b59729db 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,18 +34,6 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -60,15 +48,3 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} - - @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} From f7c1ac645171b62438375761613261c6d8730138 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:45:51 -0500 Subject: [PATCH 255/335] move changes --- .../labs/resource_estimation/ops/identity.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 7f9b59729db..b6bcb1c25dd 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,6 +34,18 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): """Resource class for the GlobalPhase gate.""" @@ -48,3 +60,15 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @staticmethod + def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} + + @staticmethod + def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + return {} From 58435510359aeac540cf9afb302c88e19ce0ece1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:50:35 -0500 Subject: [PATCH 256/335] reverting qubit --- .../ops/qubit/non_parametric_ops.py | 116 +----------------- .../ops/qubit/parametric_ops_single_qubit.py | 91 +------------- .../ops/qubit/qchem_ops.py | 49 +------- 3 files changed, 14 insertions(+), 242 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index b1adc302c68..7a3511022d4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -34,23 +34,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCH.resource_rep(): 1} - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceS(qml.S, re.ResourceOperator): """Resource class for the S gate.""" @@ -58,7 +41,7 @@ class ResourceS(qml.S, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - t = ResourceT.resource_rep() + t = ResourceT.resource_rep(**kwargs) gate_types[t] = 2 return gate_types @@ -70,14 +53,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 3} - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 4} - class ResourceSWAP(qml.SWAP, re.ResourceOperator): r"""Resource class for the SWAP gate. @@ -129,23 +104,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCSWAP.resource_rep(): 1} - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceT(qml.T, re.ResourceOperator): """Resource class for the T gate.""" @@ -161,24 +119,14 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: - """Resources obtained from the identity T^8 = I.""" - return {cls.resource_rep(): 7} - - @classmethod - def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: - """Resources obtained from the identity T^8 = I.""" - return {cls.resource_rep(): z % 8} - class ResourceX(qml.X, re.ResourceOperator): """Resource class for the X gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - s = re.ResourceS.resource_rep() - h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep(**kwargs) + h = re.ResourceHadamard.resource_rep(**kwargs) gate_types = {} gate_types[s] = 2 @@ -193,25 +141,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs): - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCNOT.resource_rep(): 1} - if num_ctrl_wires == 2 and num_ctrl_values == 2: - return {re.ResourceToffoli.resource_rep(): 1} - - return re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ) - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceY(qml.Y, re.ResourceOperator): """Resource class for the Y gate.""" @@ -234,30 +163,13 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCY.resources(**kwargs) - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceZ(qml.Z, re.ResourceOperator): """Resource class for the Z gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - s = re.ResourceS.resource_rep() + s = re.ResourceS.resource_rep(**kwargs) gate_types = {} gate_types[s] = 2 @@ -270,23 +182,3 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return re.ResourceCZ.resources(**kwargs) - - if num_ctrl_wires == 2 and num_ctrl_wires == 2: - return re.ResourceCCZ.resources(**kwargs) - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 66238d2aba4..572c12f9ebd 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -46,7 +46,7 @@ class ResourcePhaseShift(qml.PhaseShift, re.ResourceOperator): """ @staticmethod - def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: gate_types = {} rz = re.ResourceRZ.resource_rep() global_phase = re.ResourceGlobalPhase.resource_rep() @@ -62,29 +62,12 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceControlledPhaseShift.resource_rep(): 1} - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceRX(qml.RX, re.ResourceOperator): """Resource class for the RX gate.""" @staticmethod - def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rx"]) def resource_params(self) -> dict: @@ -94,29 +77,12 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCRX.resource_rep(): 1} - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceRY(qml.RY, re.ResourceOperator): """Resource class for the RY gate.""" @staticmethod - def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_ry"]) def resource_params(self) -> dict: @@ -126,23 +92,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCRY.resource_rep(): 1} - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceRZ(qml.RZ, re.ResourceOperator): r"""Resource class for the RZ gate. @@ -153,7 +102,7 @@ class ResourceRZ(qml.RZ, re.ResourceOperator): """ @staticmethod - def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rz"]) def resource_params(self) -> dict: @@ -163,29 +112,12 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCRZ.resource_rep(): 1} - - raise re.ResourcesNotDefined - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceRot(qml.Rot, re.ResourceOperator): """Resource class for the Rot gate.""" @staticmethod - def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: rx = ResourceRX.resource_rep() ry = ResourceRY.resource_rep() rz = ResourceRZ.resource_rep() @@ -199,16 +131,3 @@ def resource_params(self): @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCRot.resource_rep(): 1} - - raise re.ResourcesNotDefined diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 740d8f391ad..47a29dc5e0a 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -33,26 +33,8 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) - h = re.ResourceHadamard.resource_rep() - s = re.ResourceS.resource_rep() - s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) - cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep() - ry = re.ResourceRY.resource_rep() - t = re.ResourceT.resource_rep() - - gate_types = {} - gate_types[t_dag] = 2 - gate_types[h] = 4 - gate_types[s] = 2 - gate_types[s_dag] = 2 - gate_types[cnot] = 2 - gate_types[rz] = 1 - gate_types[ry] = 1 - gate_types[t] = 2 - - return gate_types + """TODO: implement in resource_symbolic_ops branch""" + raise re.ResourcesNotDefined def resource_params(self): return {} @@ -186,18 +168,7 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat @staticmethod def _resource_decomp(*args, **kwargs): - phase = re.ResourceGlobalPhase.resource_rep() - double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) - ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) - - gate_types = {} - gate_types[phase] = 1 - gate_types[double] = 1 - gate_types[ctrl_z] = 2 - gate_types[ctrl_phase] = 2 - - return gate_types + """TODO: implement in resource_symbolic_op branch""" def resource_params(self): return {} @@ -222,18 +193,8 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator @staticmethod def _resource_decomp(*args, **kwargs): - phase = re.ResourceGlobalPhase.resource_rep() - double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) - ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) - - gate_types = {} - gate_types[phase] = 1 - gate_types[double] = 1 - gate_types[ctrl_z] = 2 - gate_types[ctrl_phase] = 2 - - return gate_types + """TODO: implement in resource_symbolic_op branch""" + raise re.ResourcesNotDefined def resource_params(self): return {} From 220dfd9aa76d9b1e64adac551e72433444002cda Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:51:51 -0500 Subject: [PATCH 257/335] moving qubit changes --- .../ops/qubit/non_parametric_ops.py | 116 +++++++++++++- .../ops/qubit/parametric_ops_multi_qubit.py | 14 +- .../ops/qubit/parametric_ops_single_qubit.py | 91 ++++++++++- .../ops/qubit/qchem_ops.py | 150 +++++++----------- 4 files changed, 259 insertions(+), 112 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 7a3511022d4..b1adc302c68 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -34,6 +34,23 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCH.resource_rep(): 1} + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceS(qml.S, re.ResourceOperator): """Resource class for the S gate.""" @@ -41,7 +58,7 @@ class ResourceS(qml.S, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - t = ResourceT.resource_rep(**kwargs) + t = ResourceT.resource_rep() gate_types[t] = 2 return gate_types @@ -53,6 +70,14 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 3} + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 4} + class ResourceSWAP(qml.SWAP, re.ResourceOperator): r"""Resource class for the SWAP gate. @@ -104,6 +129,23 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCSWAP.resource_rep(): 1} + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceT(qml.T, re.ResourceOperator): """Resource class for the T gate.""" @@ -119,14 +161,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + """Resources obtained from the identity T^8 = I.""" + return {cls.resource_rep(): 7} + + @classmethod + def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + """Resources obtained from the identity T^8 = I.""" + return {cls.resource_rep(): z % 8} + class ResourceX(qml.X, re.ResourceOperator): """Resource class for the X gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - s = re.ResourceS.resource_rep(**kwargs) - h = re.ResourceHadamard.resource_rep(**kwargs) + s = re.ResourceS.resource_rep() + h = re.ResourceHadamard.resource_rep() gate_types = {} gate_types[s] = 2 @@ -141,6 +193,25 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs): + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCNOT.resource_rep(): 1} + if num_ctrl_wires == 2 and num_ctrl_values == 2: + return {re.ResourceToffoli.resource_rep(): 1} + + return re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceY(qml.Y, re.ResourceOperator): """Resource class for the Y gate.""" @@ -163,13 +234,30 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCY.resources(**kwargs) + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceZ(qml.Z, re.ResourceOperator): """Resource class for the Z gate.""" @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - s = re.ResourceS.resource_rep(**kwargs) + s = re.ResourceS.resource_rep() gate_types = {} gate_types[s] = 2 @@ -182,3 +270,23 @@ def resource_params(self) -> dict: @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return re.ResourceCZ.resources(**kwargs) + + if num_ctrl_wires == 2 and num_ctrl_wires == 2: + return re.ResourceCCZ.resources(**kwargs) + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 49ebae4e829..797e8d71543 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -19,7 +19,7 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): - r"""Resource class for the MultiRZ gate. + r"""Resource class for MultiRZ Resources: .. math:: @@ -47,7 +47,7 @@ def resource_rep(cls, num_wires, **kwargs): class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): - r"""Resource class for the PauliRot gate. + r"""Resource class for PauliRot Resources: .. math:: @@ -94,7 +94,7 @@ def resource_rep(cls, pauli_word, **kwargs): class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): - r"""Resource class for the IsingXX gate. + r"""Resource class for IsingXX Resources: Ising XX coupling gate @@ -128,7 +128,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): - r"""Resource class for the IsingYY gate. + r"""Resource class for IsingYY Resources: Ising YY coupling gate @@ -162,7 +162,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): - r"""Resource class for the IsingXY gate. + r"""Resource class for IsingXY Resources: Ising (XX + YY) coupling gate @@ -200,7 +200,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): - r"""Resource class for the IsingZZ gate. + r"""Resource class for IsingZZ Resources: Ising ZZ coupling gate @@ -234,7 +234,7 @@ def resource_rep(cls, *args, **kwargs): class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): - r"""Resource class for the PSWAP gate. + r"""Resource class for PSWAP Resources: .. math:: PSWAP(\phi) = \begin{bmatrix} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 572c12f9ebd..66238d2aba4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -46,7 +46,7 @@ class ResourcePhaseShift(qml.PhaseShift, re.ResourceOperator): """ @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} rz = re.ResourceRZ.resource_rep() global_phase = re.ResourceGlobalPhase.resource_rep() @@ -62,12 +62,29 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceControlledPhaseShift.resource_rep(): 1} + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceRX(qml.RX, re.ResourceOperator): """Resource class for the RX gate.""" @staticmethod - def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rx"]) def resource_params(self) -> dict: @@ -77,12 +94,29 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCRX.resource_rep(): 1} + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceRY(qml.RY, re.ResourceOperator): """Resource class for the RY gate.""" @staticmethod - def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_ry"]) def resource_params(self) -> dict: @@ -92,6 +126,23 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCRY.resource_rep(): 1} + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceRZ(qml.RZ, re.ResourceOperator): r"""Resource class for the RZ gate. @@ -102,7 +153,7 @@ class ResourceRZ(qml.RZ, re.ResourceOperator): """ @staticmethod - def _resource_decomp(config) -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(config, **kwargs) -> Dict[re.CompressedResourceOp, int]: return _rotation_resources(epsilon=config["error_rz"]) def resource_params(self) -> dict: @@ -112,12 +163,29 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCRZ.resource_rep(): 1} + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceRot(qml.Rot, re.ResourceOperator): """Resource class for the Rot gate.""" @staticmethod - def _resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: rx = ResourceRX.resource_rep() ry = ResourceRY.resource_rep() rz = ResourceRZ.resource_rep() @@ -131,3 +199,16 @@ def resource_params(self): @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCRot.resource_rep(): 1} + + raise re.ResourcesNotDefined diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 47a29dc5e0a..1c1d356d3c8 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -1,3 +1,5 @@ +# Copyright 2024 Xanadu Quantum Technologies Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -17,24 +19,30 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): - r"""Resource class for the SingleExcitation gate. - - Resources: - The resources are obtained by decomposing the following matrix into fundamental gates. - - .. math:: U(\phi) = \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ - 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ - 0 & 0 & 0 & 1 - \end{bmatrix}. - - """ + """Resource class for the SingleExcitation gate.""" @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_ops branch""" - raise re.ResourcesNotDefined + t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) + h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep() + s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() + t = re.ResourceT.resource_rep() + + gate_types = {} + gate_types[t_dag] = 2 + gate_types[h] = 4 + gate_types[s] = 2 + gate_types[s_dag] = 2 + gate_types[cnot] = 2 + gate_types[rz] = 1 + gate_types[ry] = 1 + gate_types[t] = 2 + + return gate_types def resource_params(self): return {} @@ -45,18 +53,7 @@ def resource_rep(cls, **kwargs): class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperator): - r"""Resource class for the SingleExcitationMinus gate. - - Resources: - The resources are obtained by decomposing the following matrix into fundamental gates. - - .. math:: U_-(\phi) = \begin{bmatrix} - e^{-i\phi/2} & 0 & 0 & 0 \\ - 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ - 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ - 0 & 0 & 0 & e^{-i\phi/2} - \end{bmatrix}. - """ + """Resource class for the SingleExcitationMinus gate.""" @staticmethod def _resource_decomp(*args, **kwargs): @@ -82,18 +79,7 @@ def resource_rep(cls, **kwargs): class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator): - r"""Resource class for the SingleExcitationPlus gate. - - Resources: - The resources are obtained by decomposing the following matrix into fundamental gates. - - .. math:: U_+(\phi) = \begin{bmatrix} - e^{i\phi/2} & 0 & 0 & 0 \\ - 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ - 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ - 0 & 0 & 0 & e^{i\phi/2} - \end{bmatrix}. - """ + """Resource class for the SingleExcitationPlus gate.""" @staticmethod def _resource_decomp(*args, **kwargs): @@ -119,16 +105,7 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): - r"""Resource class for the DoubleExcitation gate. - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - - &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle + \sin(\phi/2) |1100\rangle\\ - &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle - \sin(\phi/2) |0011\rangle, - """ + """Resource class for the DoubleExcitation gate.""" @staticmethod def _resource_decomp(*args, **kwargs): @@ -153,22 +130,22 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator): - r"""Resource class for the DoubleExcitationMinus gate. - - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - - &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ - &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ - &|x\rangle \rightarrow e^{-i\phi/2} |x\rangle, - """ + """Resource class for the DoubleExcitationMinus gate.""" @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" + phase = re.ResourceGlobalPhase.resource_rep() + double = re.ResourceDoubleExcitation.resource_rep() + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) + + gate_types = {} + gate_types[phase] = 1 + gate_types[double] = 1 + gate_types[ctrl_z] = 2 + gate_types[ctrl_phase] = 2 + + return gate_types def resource_params(self): return {} @@ -179,22 +156,22 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator): - r"""Resource class for the DoubleExcitationPlus gate. - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - - &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ - &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ - &|x\rangle \rightarrow e^{i\phi/2} |x\rangle, - """ + """Resource class for the DoubleExcitationPlus gate.""" @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" - raise re.ResourcesNotDefined + phase = re.ResourceGlobalPhase.resource_rep() + double = re.ResourceDoubleExcitation.resource_rep() + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) + + gate_types = {} + gate_types[phase] = 1 + gate_types[double] = 1 + gate_types[ctrl_z] = 2 + gate_types[ctrl_phase] = 2 + + return gate_types def resource_params(self): return {} @@ -205,15 +182,7 @@ def resource_rep(cls, **kwargs): class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): - r"""Resource class for the OrbitalRotation gate. - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - &|\Phi_{0}\rangle = \cos(\phi/2)|\Phi_{0}\rangle - \sin(\phi/2)|\Phi_{1}\rangle\\ - &|\Phi_{1}\rangle = \cos(\phi/2)|\Phi_{0}\rangle + \sin(\phi/2)|\Phi_{1}\rangle, - """ + """Resource class for the OrbitalRotation gate.""" @staticmethod def _resource_decomp(**kwargs): @@ -235,18 +204,7 @@ def resource_rep(cls, **kwargs): class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): - r"""Resource class for the FermionicSWAP gate. - - Resources: - The resources are obtained by decomposing the following matrix into fundamental gates. - - .. math:: U(\phi) = \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & e^{i \phi/2} \cos(\phi/2) & -ie^{i \phi/2} \sin(\phi/2) & 0 \\ - 0 & -ie^{i \phi/2} \sin(\phi/2) & e^{i \phi/2} \cos(\phi/2) & 0 \\ - 0 & 0 & 0 & e^{i \phi} - \end{bmatrix}. - """ + """Resource class for the FermionicSWAP gate.""" @staticmethod def _resource_decomp(*args, **kwargs): From 0f6563a65ed43a0a221c7286fdd6270cc277fe92 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:52:53 -0500 Subject: [PATCH 258/335] revert change --- .../ops/qubit/parametric_ops_multi_qubit.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 797e8d71543..49ebae4e829 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -19,7 +19,7 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): - r"""Resource class for MultiRZ + r"""Resource class for the MultiRZ gate. Resources: .. math:: @@ -47,7 +47,7 @@ def resource_rep(cls, num_wires, **kwargs): class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): - r"""Resource class for PauliRot + r"""Resource class for the PauliRot gate. Resources: .. math:: @@ -94,7 +94,7 @@ def resource_rep(cls, pauli_word, **kwargs): class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): - r"""Resource class for IsingXX + r"""Resource class for the IsingXX gate. Resources: Ising XX coupling gate @@ -128,7 +128,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): - r"""Resource class for IsingYY + r"""Resource class for the IsingYY gate. Resources: Ising YY coupling gate @@ -162,7 +162,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): - r"""Resource class for IsingXY + r"""Resource class for the IsingXY gate. Resources: Ising (XX + YY) coupling gate @@ -200,7 +200,7 @@ def resource_rep(cls, *args, **kwargs): class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): - r"""Resource class for IsingZZ + r"""Resource class for the IsingZZ gate. Resources: Ising ZZ coupling gate @@ -234,7 +234,7 @@ def resource_rep(cls, *args, **kwargs): class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): - r"""Resource class for PSWAP + r"""Resource class for the PSWAP gate. Resources: .. math:: PSWAP(\phi) = \begin{bmatrix} From f3773b0eabaae798111f39a9ad46455ae958e170 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:54:17 -0500 Subject: [PATCH 259/335] revert test changes --- .../ops/qubit/test_non_parametric_ops.py | 183 ------------------ .../qubit/test_parametric_ops_single_qubit.py | 78 -------- .../ops/qubit/test_qchem_ops.py | 77 +------- 3 files changed, 3 insertions(+), 335 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index eeafd9398e5..b59c519d71d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -40,39 +40,6 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(re.ResourceHadamard, {}) assert re.ResourceHadamard.resource_rep() == expected - def test_adjoint_decomp(self): - """Test that the adjoint decomposition is correct.""" - h = re.ResourceHadamard(0) - h_dag = re.ResourceAdjoint(re.ResourceHadamard(0)) - - assert re.get_resources(h) == re.get_resources(h_dag) - - def test_controlled_decomp(self): - """Test that the controlled decomposition is correct.""" - expected = {re.ResourceCH.resource_rep(): 1} - assert re.ResourceHadamard.controlled_resource_decomp(1, 1, 0) == expected - - controlled_h = re.ResourceControlled(re.ResourceHadamard(0), control_wires=[1]) - ch = re.ResourceCH([0, 1]) - - r1 = re.get_resources(controlled_h) - r2 = re.get_resources(ch) - assert r1 == r2 - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceHadamard.resource_rep(): z % 2} - assert re.ResourceHadamard.pow_resource_decomp(z) == expected - - h = re.ResourceHadamard(0) - pow_h = re.ResourcePow(re.ResourceHadamard(0), z) - - r1 = re.get_resources(h) * (z % 2) - r2 = re.get_resources(pow_h) - - assert r1 == r2 - class TestSWAP: """Tests for ResourceSWAP""" @@ -106,39 +73,6 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected - def test_adjoint_decomp(self): - """Test that the adjoint decomposition is correct.""" - swap = re.ResourceSWAP([0, 1]) - swap_dag = re.ResourceAdjoint(re.ResourceSWAP([0, 1])) - - assert re.get_resources(swap) == re.get_resources(swap_dag) - - def test_controlled_decomp(self): - """Test that the controlled decomposition is correct.""" - expected = {re.ResourceCSWAP.resource_rep(): 1} - assert re.ResourceSWAP.controlled_resource_decomp(1, 1, 0) == expected - - controlled_swap = re.ResourceControlled(re.ResourceSWAP([0, 1]), control_wires=[2]) - cswap = re.ResourceCSWAP([0, 1, 2]) - - r1 = re.get_resources(controlled_swap) - r2 = re.get_resources(cswap) - assert r1 == r2 - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceSWAP.resource_rep(): z % 2} - assert re.ResourceSWAP.pow_resource_decomp(z) == expected - - swap = re.ResourceSWAP([0, 1]) - pow_swap = re.ResourcePow(re.ResourceSWAP([0, 1]), z) - - r1 = re.get_resources(swap) * (z % 2) - r2 = re.get_resources(pow_swap) - - assert r1 == r2 - class TestS: """Tests for ResourceS""" @@ -170,32 +104,6 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected - def test_adjoint_decomposition(self): - """Test that the adjoint resources are correct.""" - expected = {re.ResourceS.resource_rep(): 3} - assert re.ResourceS.adjoint_resource_decomp() == expected - - s = re.ResourceS(0) - s_dag = re.ResourceAdjoint(s) - - r1 = re.get_resources(s) * 3 - r2 = re.get_resources(s_dag) - assert r1 == r2 - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceS.resource_rep(): z % 4} - assert re.ResourceS.pow_resource_decomp(z) == expected - - s = re.ResourceS(0) - pow_s = re.ResourcePow(s, z) - - r1 = re.get_resources(s) * (z % 4) - r2 = re.get_resources(pow_s) - - assert r1 == r2 - class TestT: """Tests for ResourceT""" @@ -215,94 +123,3 @@ def test_resource_rep(self): """Test that the compact representation is correct""" expected = re.CompressedResourceOp(re.ResourceT, {}) assert re.ResourceT.resource_rep() == expected - - def test_adjoint_decomposition(self): - """Test that the adjoint resources are correct.""" - expected = {re.ResourceT.resource_rep(): 7} - assert re.ResourceT.adjoint_resource_decomp() == expected - - t = re.ResourceT(0) - t_dag = re.ResourceAdjoint(t) - - r1 = re.get_resources(t) * 7 - r2 = re.get_resources(t_dag) - assert r1 == r2 - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceT.resource_rep(): z % 8} - assert re.ResourceT.pow_resource_decomp(z) == expected - - t = re.ResourceT(0) - pow_t = re.ResourcePow(t, z) - - r1 = re.get_resources(t) * (z % 8) - r2 = re.get_resources(pow_t) - - assert r1 == r2 - - -class TestX: - """Tests for ResourceX""" - - def test_resources(self): - """Test that ResourceT does not implement a decomposition""" - expected = { - re.ResourceS.resource_rep(): 2, - re.ResourceHadamard.resource_rep(): 2, - } - assert re.ResourceX.resources() == expected - - def test_resource_params(self): - """Test that the resource params are correct""" - op = re.ResourceX(0) - assert op.resource_params() == {} - - def test_resource_rep(self): - """Test that the compact representation is correct""" - expected = re.CompressedResourceOp(re.ResourceX, {}) - assert re.ResourceX.resource_rep() == expected - - def test_single_controlled_resources(self): - """Test that the controlled_resource_decomp method dispatches correctly.""" - controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1]) - - cnot = re.ResourceCNOT([0, 1]) - assert re.get_resources(controlled_op) == re.get_resources(cnot) - - def test_double_controlled_resources(self): - """Test that the controlled_resource_decomp method dispatches correctly.""" - controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]) - expected_op = re.ResourceToffoli([0, 1, 2]) - - r1 = re.get_resources(controlled_op) - r2 = re.get_resources(expected_op) - - assert r1 == r2 - - def test_adjoint_decomposition(self): - """Test that the adjoint resources are correct.""" - expected = {re.ResourceX.resource_rep(): 1} - assert re.ResourceX.adjoint_resource_decomp() == expected - - x = re.ResourceX(0) - x_dag = re.ResourceAdjoint(x) - - r1 = re.get_resources(x) - r2 = re.get_resources(x_dag) - assert r1 == r2 - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceX.resource_rep(): z % 2} - assert re.ResourceX.pow_resource_decomp(z) == expected - - x = re.ResourceX(0) - pow_x = re.ResourcePow(x, z) - - r1 = re.get_resources(x) * (z % 2) - r2 = re.get_resources(pow_x) - - assert r1 == r2 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 3c94748ded5..f93c363dbde 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -82,70 +82,6 @@ def test_resource_params(self, resource_class, epsilon): # pylint: disable=unus op = resource_class(1.24, wires=0) assert op.resource_params() == {} - @pytest.mark.parametrize("resource_class", params_classes) - @pytest.mark.parametrize("epsilon", params_errors) - def test_adjoint_decomposition(self, resource_class, epsilon): - """Test that the adjoint decompositions are correct.""" - - expected = {resource_class.resource_rep(): 1} - assert resource_class.adjoint_resource_decomp() == expected - - op = resource_class(1.24, wires=0) - dag = re.ResourceAdjoint(op) - - label = "error_" + resource_class.__name__.replace("Resource", "").lower() - config = {label: epsilon} - - r1 = re.get_resources(op, config=config) - r2 = re.get_resources(dag, config=config) - - assert r1 == r2 - - @pytest.mark.parametrize("resource_class", params_classes) - @pytest.mark.parametrize("epsilon", params_errors) - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomposition(self, resource_class, epsilon, z): - """Test that the pow decompositions are correct.""" - - expected = {resource_class.resource_rep(): 1} - assert resource_class.pow_resource_decomp(z) == expected - - op = resource_class(1.24, wires=0) - dag = re.ResourcePow(op, z) - - label = "error_" + resource_class.__name__.replace("Resource", "").lower() - config = {label: epsilon} - - r1 = re.get_resources(op, config=config) - r2 = re.get_resources(dag, config=config) - - assert r1 == r2 - - params_classes = ( - (re.ResourceRX, re.ResourceCRX), - (re.ResourceRY, re.ResourceCRY), - (re.ResourceRZ, re.ResourceCRZ), - ) - - @pytest.mark.parametrize("resource_class, controlled_class", params_classes) - @pytest.mark.parametrize("epsilon", params_errors) - def test_controlled_decomposition(self, resource_class, controlled_class, epsilon): - """Test that the controlled decompositions are correct.""" - expected = {controlled_class.resource_rep(): 1} - assert resource_class.controlled_resource_decomp(1, 1, 0) == expected - - op = resource_class(1.24, wires=0) - c_op = re.ResourceControlled(op, control_wires=[1]) - - c = controlled_class(1.24, wires=[0, 1]) - - config = {"error_rx": epsilon, "error_ry": epsilon, "error_rz": epsilon} - - r1 = re.get_resources(c, config=config) - r2 = re.get_resources(c_op, config=config) - - assert r1 == r2 - class TestRot: """Test ResourceRot""" @@ -183,17 +119,3 @@ def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) assert op.resource_params() == {} - - def test_adjoint_decomp(self): - """Test that the adjoint decomposition is correct""" - - expected = {re.ResourceRot.resource_rep(): 1} - assert re.ResourceRot.adjoint_resource_decomp() == expected - - op = re.ResourceRot(1.24, 1.25, 1.26, wires=0) - dag = re.ResourceAdjoint(op) - - r1 = re.get_resources(op) - r2 = re.get_resources(dag) - - assert r1 == r2 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index 7cd8eee6afe..c3c11a014af 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -18,50 +18,21 @@ # pylint: disable=use-implicit-booleaness-not-comparison,no-self-use +# TODO: implement in resource_symbolic_ops branch class TestSingleExcitation: """Tests for the ResourceSingleExcitation class.""" def test_resources(self): """Test that the resources are correct.""" - expected = { - re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, - re.ResourceHadamard.resource_rep(): 4, - re.ResourceS.resource_rep(): 2, - re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2, - re.ResourceCNOT.resource_rep(): 2, - re.ResourceRZ.resource_rep(): 1, - re.ResourceRY.resource_rep(): 1, - re.ResourceT.resource_rep(): 2, - } - assert re.ResourceSingleExcitation.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" - op = re.ResourceSingleExcitation(0.5, wires=[0, 1]) - assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" - expected = re.CompressedResourceOp(re.ResourceSingleExcitation, {}) - assert re.ResourceSingleExcitation.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" - op = re.ResourceSingleExcitation(0.5, wires=[0, 1]) - expected = { - re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, - re.ResourceHadamard.resource_rep(): 4, - re.ResourceS.resource_rep(): 2, - re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2, - re.ResourceCNOT.resource_rep(): 2, - re.ResourceRZ.resource_rep(): 1, - re.ResourceRY.resource_rep(): 1, - re.ResourceT.resource_rep(): 2, - } - op_compressed_rep = op.resource_rep_from_op() - op_resource_type = op_compressed_rep.op_type - op_resource_params = op_compressed_rep.params - assert op_resource_type.resources(**op_resource_params) == expected class TestSingleExcitationMinus: @@ -176,80 +147,38 @@ def test_resources_from_rep(self): assert op_resource_type.resources(**op_resource_params) == expected +# TODO: add tests in resource_symbolic_ops branch class TestDoubleExcitationMinus: """Tests for the ResourceDoubleExcitationMinus class.""" def test_resources(self): """Test that the resources are correct.""" - expected = { - re.ResourceGlobalPhase.resource_rep(): 1, - re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, - } - assert re.ResourceDoubleExcitationMinus.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" - op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3]) - assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" - expected = re.CompressedResourceOp(re.ResourceDoubleExcitationMinus, {}) - assert re.ResourceDoubleExcitationMinus.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" - op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3]) - expected = { - re.ResourceGlobalPhase.resource_rep(): 1, - re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, - } - op_compressed_rep = op.resource_rep_from_op() - op_resource_type = op_compressed_rep.op_type - op_resource_params = op_compressed_rep.params - assert op_resource_type.resources(**op_resource_params) == expected +# TODO: add tests in resource_symbolic_ops branch class TestDoubleExcitationPlus: """Tests for the ResourceDoubleExcitationPlus class.""" def test_resources(self): """Test that the resources are correct.""" - expected = { - re.ResourceGlobalPhase.resource_rep(): 1, - re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, - } - assert re.ResourceDoubleExcitationPlus.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" - op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4]) - assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" - expected = re.CompressedResourceOp(re.ResourceDoubleExcitationPlus, {}) - assert re.ResourceDoubleExcitationPlus.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" - op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4]) - expected = { - re.ResourceGlobalPhase.resource_rep(): 1, - re.ResourceDoubleExcitation.resource_rep(): 1, - re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, - re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, - } - op_compressed_rep = op.resource_rep_from_op() - op_resource_type = op_compressed_rep.op_type - op_resource_params = op_compressed_rep.params - assert op_resource_type.resources(**op_resource_params) == expected class TestOribatlRotation: From 35e7f114a677a92478c8cb9a955354b6c936691f Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:55:02 -0500 Subject: [PATCH 260/335] move tests --- .../ops/qubit/test_non_parametric_ops.py | 183 ++++++++++++++++++ .../qubit/test_parametric_ops_single_qubit.py | 78 ++++++++ .../ops/qubit/test_qchem_ops.py | 77 +++++++- 3 files changed, 335 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index b59c519d71d..eeafd9398e5 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -40,6 +40,39 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(re.ResourceHadamard, {}) assert re.ResourceHadamard.resource_rep() == expected + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct.""" + h = re.ResourceHadamard(0) + h_dag = re.ResourceAdjoint(re.ResourceHadamard(0)) + + assert re.get_resources(h) == re.get_resources(h_dag) + + def test_controlled_decomp(self): + """Test that the controlled decomposition is correct.""" + expected = {re.ResourceCH.resource_rep(): 1} + assert re.ResourceHadamard.controlled_resource_decomp(1, 1, 0) == expected + + controlled_h = re.ResourceControlled(re.ResourceHadamard(0), control_wires=[1]) + ch = re.ResourceCH([0, 1]) + + r1 = re.get_resources(controlled_h) + r2 = re.get_resources(ch) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceHadamard.resource_rep(): z % 2} + assert re.ResourceHadamard.pow_resource_decomp(z) == expected + + h = re.ResourceHadamard(0) + pow_h = re.ResourcePow(re.ResourceHadamard(0), z) + + r1 = re.get_resources(h) * (z % 2) + r2 = re.get_resources(pow_h) + + assert r1 == r2 + class TestSWAP: """Tests for ResourceSWAP""" @@ -73,6 +106,39 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct.""" + swap = re.ResourceSWAP([0, 1]) + swap_dag = re.ResourceAdjoint(re.ResourceSWAP([0, 1])) + + assert re.get_resources(swap) == re.get_resources(swap_dag) + + def test_controlled_decomp(self): + """Test that the controlled decomposition is correct.""" + expected = {re.ResourceCSWAP.resource_rep(): 1} + assert re.ResourceSWAP.controlled_resource_decomp(1, 1, 0) == expected + + controlled_swap = re.ResourceControlled(re.ResourceSWAP([0, 1]), control_wires=[2]) + cswap = re.ResourceCSWAP([0, 1, 2]) + + r1 = re.get_resources(controlled_swap) + r2 = re.get_resources(cswap) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceSWAP.resource_rep(): z % 2} + assert re.ResourceSWAP.pow_resource_decomp(z) == expected + + swap = re.ResourceSWAP([0, 1]) + pow_swap = re.ResourcePow(re.ResourceSWAP([0, 1]), z) + + r1 = re.get_resources(swap) * (z % 2) + r2 = re.get_resources(pow_swap) + + assert r1 == r2 + class TestS: """Tests for ResourceS""" @@ -104,6 +170,32 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceS.resource_rep(): 3} + assert re.ResourceS.adjoint_resource_decomp() == expected + + s = re.ResourceS(0) + s_dag = re.ResourceAdjoint(s) + + r1 = re.get_resources(s) * 3 + r2 = re.get_resources(s_dag) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceS.resource_rep(): z % 4} + assert re.ResourceS.pow_resource_decomp(z) == expected + + s = re.ResourceS(0) + pow_s = re.ResourcePow(s, z) + + r1 = re.get_resources(s) * (z % 4) + r2 = re.get_resources(pow_s) + + assert r1 == r2 + class TestT: """Tests for ResourceT""" @@ -123,3 +215,94 @@ def test_resource_rep(self): """Test that the compact representation is correct""" expected = re.CompressedResourceOp(re.ResourceT, {}) assert re.ResourceT.resource_rep() == expected + + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceT.resource_rep(): 7} + assert re.ResourceT.adjoint_resource_decomp() == expected + + t = re.ResourceT(0) + t_dag = re.ResourceAdjoint(t) + + r1 = re.get_resources(t) * 7 + r2 = re.get_resources(t_dag) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceT.resource_rep(): z % 8} + assert re.ResourceT.pow_resource_decomp(z) == expected + + t = re.ResourceT(0) + pow_t = re.ResourcePow(t, z) + + r1 = re.get_resources(t) * (z % 8) + r2 = re.get_resources(pow_t) + + assert r1 == r2 + + +class TestX: + """Tests for ResourceX""" + + def test_resources(self): + """Test that ResourceT does not implement a decomposition""" + expected = { + re.ResourceS.resource_rep(): 2, + re.ResourceHadamard.resource_rep(): 2, + } + assert re.ResourceX.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceX(0) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compact representation is correct""" + expected = re.CompressedResourceOp(re.ResourceX, {}) + assert re.ResourceX.resource_rep() == expected + + def test_single_controlled_resources(self): + """Test that the controlled_resource_decomp method dispatches correctly.""" + controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1]) + + cnot = re.ResourceCNOT([0, 1]) + assert re.get_resources(controlled_op) == re.get_resources(cnot) + + def test_double_controlled_resources(self): + """Test that the controlled_resource_decomp method dispatches correctly.""" + controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]) + expected_op = re.ResourceToffoli([0, 1, 2]) + + r1 = re.get_resources(controlled_op) + r2 = re.get_resources(expected_op) + + assert r1 == r2 + + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceX.resource_rep(): 1} + assert re.ResourceX.adjoint_resource_decomp() == expected + + x = re.ResourceX(0) + x_dag = re.ResourceAdjoint(x) + + r1 = re.get_resources(x) + r2 = re.get_resources(x_dag) + assert r1 == r2 + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + expected = {re.ResourceX.resource_rep(): z % 2} + assert re.ResourceX.pow_resource_decomp(z) == expected + + x = re.ResourceX(0) + pow_x = re.ResourcePow(x, z) + + r1 = re.get_resources(x) * (z % 2) + r2 = re.get_resources(pow_x) + + assert r1 == r2 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index f93c363dbde..3c94748ded5 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -82,6 +82,70 @@ def test_resource_params(self, resource_class, epsilon): # pylint: disable=unus op = resource_class(1.24, wires=0) assert op.resource_params() == {} + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + def test_adjoint_decomposition(self, resource_class, epsilon): + """Test that the adjoint decompositions are correct.""" + + expected = {resource_class.resource_rep(): 1} + assert resource_class.adjoint_resource_decomp() == expected + + op = resource_class(1.24, wires=0) + dag = re.ResourceAdjoint(op) + + label = "error_" + resource_class.__name__.replace("Resource", "").lower() + config = {label: epsilon} + + r1 = re.get_resources(op, config=config) + r2 = re.get_resources(dag, config=config) + + assert r1 == r2 + + @pytest.mark.parametrize("resource_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomposition(self, resource_class, epsilon, z): + """Test that the pow decompositions are correct.""" + + expected = {resource_class.resource_rep(): 1} + assert resource_class.pow_resource_decomp(z) == expected + + op = resource_class(1.24, wires=0) + dag = re.ResourcePow(op, z) + + label = "error_" + resource_class.__name__.replace("Resource", "").lower() + config = {label: epsilon} + + r1 = re.get_resources(op, config=config) + r2 = re.get_resources(dag, config=config) + + assert r1 == r2 + + params_classes = ( + (re.ResourceRX, re.ResourceCRX), + (re.ResourceRY, re.ResourceCRY), + (re.ResourceRZ, re.ResourceCRZ), + ) + + @pytest.mark.parametrize("resource_class, controlled_class", params_classes) + @pytest.mark.parametrize("epsilon", params_errors) + def test_controlled_decomposition(self, resource_class, controlled_class, epsilon): + """Test that the controlled decompositions are correct.""" + expected = {controlled_class.resource_rep(): 1} + assert resource_class.controlled_resource_decomp(1, 1, 0) == expected + + op = resource_class(1.24, wires=0) + c_op = re.ResourceControlled(op, control_wires=[1]) + + c = controlled_class(1.24, wires=[0, 1]) + + config = {"error_rx": epsilon, "error_ry": epsilon, "error_rz": epsilon} + + r1 = re.get_resources(c, config=config) + r2 = re.get_resources(c_op, config=config) + + assert r1 == r2 + class TestRot: """Test ResourceRot""" @@ -119,3 +183,17 @@ def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) assert op.resource_params() == {} + + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct""" + + expected = {re.ResourceRot.resource_rep(): 1} + assert re.ResourceRot.adjoint_resource_decomp() == expected + + op = re.ResourceRot(1.24, 1.25, 1.26, wires=0) + dag = re.ResourceAdjoint(op) + + r1 = re.get_resources(op) + r2 = re.get_resources(dag) + + assert r1 == r2 diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index c3c11a014af..7cd8eee6afe 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -18,21 +18,50 @@ # pylint: disable=use-implicit-booleaness-not-comparison,no-self-use -# TODO: implement in resource_symbolic_ops branch class TestSingleExcitation: """Tests for the ResourceSingleExcitation class.""" def test_resources(self): """Test that the resources are correct.""" + expected = { + re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, + re.ResourceHadamard.resource_rep(): 4, + re.ResourceS.resource_rep(): 2, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 1, + re.ResourceRY.resource_rep(): 1, + re.ResourceT.resource_rep(): 2, + } + assert re.ResourceSingleExcitation.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" + op = re.ResourceSingleExcitation(0.5, wires=[0, 1]) + assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceSingleExcitation, {}) + assert re.ResourceSingleExcitation.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceSingleExcitation(0.5, wires=[0, 1]) + expected = { + re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, + re.ResourceHadamard.resource_rep(): 4, + re.ResourceS.resource_rep(): 2, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 2, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 1, + re.ResourceRY.resource_rep(): 1, + re.ResourceT.resource_rep(): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected class TestSingleExcitationMinus: @@ -147,38 +176,80 @@ def test_resources_from_rep(self): assert op_resource_type.resources(**op_resource_params) == expected -# TODO: add tests in resource_symbolic_ops branch class TestDoubleExcitationMinus: """Tests for the ResourceDoubleExcitationMinus class.""" def test_resources(self): """Test that the resources are correct.""" + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, + } + assert re.ResourceDoubleExcitationMinus.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" + op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3]) + assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceDoubleExcitationMinus, {}) + assert re.ResourceDoubleExcitationMinus.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceDoubleExcitationMinus(0.5, wires=[0, 1, 2, 3]) + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected -# TODO: add tests in resource_symbolic_ops branch class TestDoubleExcitationPlus: """Tests for the ResourceDoubleExcitationPlus class.""" def test_resources(self): """Test that the resources are correct.""" + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, + } + assert re.ResourceDoubleExcitationPlus.resources() == expected def test_resource_params(self): """Test that the resource params are correct.""" + op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4]) + assert op.resource_params() == {} def test_resource_rep(self): """Test that the compressed representation is correct.""" + expected = re.CompressedResourceOp(re.ResourceDoubleExcitationPlus, {}) + assert re.ResourceDoubleExcitationPlus.resource_rep() == expected def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" + op = re.ResourceDoubleExcitationPlus(0.5, wires=[0, 1, 3, 4]) + expected = { + re.ResourceGlobalPhase.resource_rep(): 1, + re.ResourceDoubleExcitation.resource_rep(): 1, + re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0): 2, + re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0): 2, + } + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected class TestOribatlRotation: From 2984553b87b571027c6a98cdeeca3bf34f3cb063 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:56:55 -0500 Subject: [PATCH 261/335] remove controlled_ops --- .../ops/op_math/controlled_ops.py | 258 +----------------- 1 file changed, 5 insertions(+), 253 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index c6d1dd629da..685e155b63c 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -62,24 +62,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceHadamard, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceCY(qml.CY, re.ResourceOperator): r"""Resource class for CY gate. @@ -112,24 +94,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceCZ(qml.CZ, re.ResourceOperator): r"""Resource class for CZ @@ -162,27 +126,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceCCZ.resource_rep(): 1} - - return { - re.ResourceControlled.resource_rep( - re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceCSWAP(qml.CSWAP, re.ResourceOperator): r"""Resource class for CSWAP @@ -222,24 +165,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceCCZ(qml.CCZ, re.ResourceOperator): r"""Resource class for CCZ @@ -271,27 +196,9 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls, **kwargs): - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceCNOT(qml.CNOT, re.ResourceOperator): - """Resource class for the CNOT gate. + r"""Resource class for CNOT Resources: There is no further decomposition provided for this gate. @@ -309,27 +216,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @classmethod - def controlled_resource_decomp( - cls, num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: - return {re.ResourceToffoli.resource_rep(): 1} - - return { - re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceToffoli(qml.Toffoli, re.ResourceOperator): r"""Resource class for Toffoli @@ -406,24 +292,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} - class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): r"""Resource class for MultiControlledX @@ -468,36 +336,6 @@ def resource_rep( }, ) - @classmethod - def adjoint_resource_decomp( - cls, num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(num_ctrl_wires, num_ctrl_values, num_work_wires) - - @classmethod - def controlled_resource_decomp( - cls, - outer_num_ctrl_wires, - outer_num_ctrl_values, - outer_num_work_wires, - num_ctrl_wires, - num_ctrl_values, - num_work_wires, - ) -> Dict[re.CompressedResourceOp, int]: - return { - cls.resource_rep( - outer_num_ctrl_wires + num_ctrl_wires, - outer_num_ctrl_values + num_ctrl_values, - outer_num_work_wires + num_work_wires, - ): 1 - } - - @classmethod - def pow_resource_decomp( - cls, z, num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): z % 2} - class ResourceCRX(qml.CRX, re.ResourceOperator): r"""Resource class for CRX @@ -521,13 +359,13 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} + h = re.ResourceHadamard.resource_rep() rz = re.ResourceRZ.resource_rep() - ry = re.ResourceRY.resource_rep() cnot = re.ResourceCNOT.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 2 - gate_types[ry] = 2 + gate_types[h] = 2 return gate_types @@ -538,24 +376,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceRX, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceCRY(qml.CRY, re.ResourceOperator): r"""Resource class for CRY @@ -587,24 +407,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceRY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceCRZ(qml.CRZ, re.ResourceOperator): r"""Resource class for CRZ @@ -622,10 +424,10 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() - phase = re.ResourcePhaseShift.resource_rep() + rz = re.ResourceRZ.resource_rep() gate_types[cnot] = 2 - gate_types[phase] = 2 + gate_types[rz] = 2 return gate_types @@ -636,24 +438,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceRZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} - class ResourceCRot(qml.CRot, re.ResourceOperator): r"""Resource class for CRot @@ -684,20 +468,6 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourceRot, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for the ControlledPhaseShift gate. @@ -737,21 +507,3 @@ def resource_params(self): @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - - @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(**kwargs) - - @staticmethod - def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires - ) -> Dict[re.CompressedResourceOp, int]: - return { - re.ResourceControlled.resource_rep( - re.ResourcePhaseShift, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires - ): 1 - } - - @classmethod - def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(**kwargs) From 9b3a486c74368e0c0bc0b80b9747f7d6ef395e38 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:57:35 -0500 Subject: [PATCH 262/335] add controlled ops --- .../ops/op_math/controlled_ops.py | 258 +++++++++++++++++- 1 file changed, 253 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 685e155b63c..c6d1dd629da 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -62,6 +62,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceHadamard, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCY(qml.CY, re.ResourceOperator): r"""Resource class for CY gate. @@ -94,6 +112,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCZ(qml.CZ, re.ResourceOperator): r"""Resource class for CZ @@ -126,6 +162,27 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceCCZ.resource_rep(): 1} + + return { + re.ResourceControlled.resource_rep( + re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCSWAP(qml.CSWAP, re.ResourceOperator): r"""Resource class for CSWAP @@ -165,6 +222,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCCZ(qml.CCZ, re.ResourceOperator): r"""Resource class for CCZ @@ -196,9 +271,27 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls, **kwargs): + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceCNOT(qml.CNOT, re.ResourceOperator): - r"""Resource class for CNOT + """Resource class for the CNOT gate. Resources: There is no further decomposition provided for this gate. @@ -216,6 +309,27 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @classmethod + def controlled_resource_decomp( + cls, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_wires == 1 and num_ctrl_values == 1: + return {re.ResourceToffoli.resource_rep(): 1} + + return { + re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceToffoli(qml.Toffoli, re.ResourceOperator): r"""Resource class for Toffoli @@ -292,6 +406,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): z % 2} + class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): r"""Resource class for MultiControlledX @@ -336,6 +468,36 @@ def resource_rep( }, ) + @classmethod + def adjoint_resource_decomp( + cls, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(num_ctrl_wires, num_ctrl_values, num_work_wires) + + @classmethod + def controlled_resource_decomp( + cls, + outer_num_ctrl_wires, + outer_num_ctrl_values, + outer_num_work_wires, + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + return { + cls.resource_rep( + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ): 1 + } + + @classmethod + def pow_resource_decomp( + cls, z, num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): z % 2} + class ResourceCRX(qml.CRX, re.ResourceOperator): r"""Resource class for CRX @@ -359,13 +521,13 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - h = re.ResourceHadamard.resource_rep() rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() cnot = re.ResourceCNOT.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 2 - gate_types[h] = 2 + gate_types[ry] = 2 return gate_types @@ -376,6 +538,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceRX, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceCRY(qml.CRY, re.ResourceOperator): r"""Resource class for CRY @@ -407,6 +587,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceRY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceCRZ(qml.CRZ, re.ResourceOperator): r"""Resource class for CRZ @@ -424,10 +622,10 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep() + phase = re.ResourcePhaseShift.resource_rep() gate_types[cnot] = 2 - gate_types[rz] = 2 + gate_types[phase] = 2 return gate_types @@ -438,6 +636,24 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceRZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceCRot(qml.CRot, re.ResourceOperator): r"""Resource class for CRot @@ -468,6 +684,20 @@ def resource_params(self) -> dict: def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return cls.resources() + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourceRot, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for the ControlledPhaseShift gate. @@ -507,3 +737,21 @@ def resource_params(self): @classmethod def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(**kwargs) + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires + ) -> Dict[re.CompressedResourceOp, int]: + return { + re.ResourceControlled.resource_rep( + re.ResourcePhaseShift, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + ): 1 + } + + @classmethod + def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + return cls.resources(**kwargs) From d8e36a2b3dcb08f7dc22d0b6f1d0d26b2547264d Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:58:33 -0500 Subject: [PATCH 263/335] move controlled tests --- .../ops/op_math/test_controlled_ops.py | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 2d664de6ef8..07d95ef8c9e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -81,42 +81,18 @@ def test_resources_from_rep(self, phi, wires): assert op_compressed_rep_type.resources(**op_resource_params) == expected - @pytest.mark.parametrize("phi, wires", params) - def test_adjoint_decomp(self, phi, wires): - """Test that the adjoint resources are correct.""" - - op = re.ResourceControlledPhaseShift(phi, wires) - rep = op.resource_rep_from_op() - res = rep.op_type.resources(**rep.params) - adjoint_res = rep.op_type.adjoint_resource_decomp(**rep.params) - - assert res == adjoint_res - - @pytest.mark.parametrize("phi, wires", params) - def test_pow_decomp(self, phi, wires): - """Test that the adjoint resources are correct.""" - - op = re.ResourceControlledPhaseShift(phi, wires) - rep = op.resource_rep_from_op() - res = rep.op_type.resources(**rep.params) - adjoint_res = rep.op_type.pow_resource_decomp(2, **rep.params) - - assert res == adjoint_res - class TestCNOT: """Test ResourceCNOT""" def test_resources(self): """Test that the resources method is not implemented""" - op = re.ResourceCNOT([0, 1]) with pytest.raises(re.ResourcesNotDefined): op.resources() def test_resource_rep(self): """Test the compressed representation""" - op = re.ResourceCNOT([0, 1]) expected = re.CompressedResourceOp(re.ResourceCNOT, {}) assert op.resource_rep() == expected @@ -125,38 +101,3 @@ def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceCNOT([0, 1]) assert op.resource_params() == {} - - def test_adjoint_decomp(self): - """Test that the adjoint decomposition is correct.""" - - expected = {re.ResourceCNOT.resource_rep(): 1} - assert re.ResourceCNOT.adjoint_resource_decomp() == expected - - cnot = re.ResourceCNOT([0, 1]) - cnot_dag = re.ResourceAdjoint(re.ResourceCNOT([0, 1])) - - r1 = re.get_resources(cnot) - r2 = re.get_resources(cnot_dag) - - assert r1 == r2 - - def test_controlled_decomp(self): - """Test that the controlled decomposition is correct.""" - - expected = {re.ResourceToffoli.resource_rep(): 1} - assert re.ResourceCNOT.controlled_resource_decomp(1, 1, 0) == expected - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - - expected = {re.ResourceCNOT.resource_rep(): z % 2} - assert re.ResourceCNOT.pow_resource_decomp(z) == expected - - cnot = re.ResourceCNOT([0, 1]) - cnot_pow = re.ResourcePow(re.ResourceCNOT([0, 1]), z) - - r1 = re.get_resources(cnot) * (z % 2) - r2 = re.get_resources(cnot_pow) - - assert r1 == r2 From c7585b5ef9d18a4efa31b39a3d0bfad23fd46e86 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 13:59:02 -0500 Subject: [PATCH 264/335] move controlled tests --- .../ops/op_math/test_controlled_ops.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 07d95ef8c9e..2d664de6ef8 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -81,18 +81,42 @@ def test_resources_from_rep(self, phi, wires): assert op_compressed_rep_type.resources(**op_resource_params) == expected + @pytest.mark.parametrize("phi, wires", params) + def test_adjoint_decomp(self, phi, wires): + """Test that the adjoint resources are correct.""" + + op = re.ResourceControlledPhaseShift(phi, wires) + rep = op.resource_rep_from_op() + res = rep.op_type.resources(**rep.params) + adjoint_res = rep.op_type.adjoint_resource_decomp(**rep.params) + + assert res == adjoint_res + + @pytest.mark.parametrize("phi, wires", params) + def test_pow_decomp(self, phi, wires): + """Test that the adjoint resources are correct.""" + + op = re.ResourceControlledPhaseShift(phi, wires) + rep = op.resource_rep_from_op() + res = rep.op_type.resources(**rep.params) + adjoint_res = rep.op_type.pow_resource_decomp(2, **rep.params) + + assert res == adjoint_res + class TestCNOT: """Test ResourceCNOT""" def test_resources(self): """Test that the resources method is not implemented""" + op = re.ResourceCNOT([0, 1]) with pytest.raises(re.ResourcesNotDefined): op.resources() def test_resource_rep(self): """Test the compressed representation""" + op = re.ResourceCNOT([0, 1]) expected = re.CompressedResourceOp(re.ResourceCNOT, {}) assert op.resource_rep() == expected @@ -101,3 +125,38 @@ def test_resource_params(self): """Test that the resource params are correct""" op = re.ResourceCNOT([0, 1]) assert op.resource_params() == {} + + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct.""" + + expected = {re.ResourceCNOT.resource_rep(): 1} + assert re.ResourceCNOT.adjoint_resource_decomp() == expected + + cnot = re.ResourceCNOT([0, 1]) + cnot_dag = re.ResourceAdjoint(re.ResourceCNOT([0, 1])) + + r1 = re.get_resources(cnot) + r2 = re.get_resources(cnot_dag) + + assert r1 == r2 + + def test_controlled_decomp(self): + """Test that the controlled decomposition is correct.""" + + expected = {re.ResourceToffoli.resource_rep(): 1} + assert re.ResourceCNOT.controlled_resource_decomp(1, 1, 0) == expected + + @pytest.mark.parametrize("z", list(range(10))) + def test_pow_decomp(self, z): + """Test that the pow decomposition is correct.""" + + expected = {re.ResourceCNOT.resource_rep(): z % 2} + assert re.ResourceCNOT.pow_resource_decomp(z) == expected + + cnot = re.ResourceCNOT([0, 1]) + cnot_pow = re.ResourcePow(re.ResourceCNOT([0, 1]), z) + + r1 = re.get_resources(cnot) * (z % 2) + r2 = re.get_resources(cnot_pow) + + assert r1 == r2 From 20f348d5fd0d7036f46b8be69d836b642b6fdbd7 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:29:21 -0500 Subject: [PATCH 265/335] update nested symbolic ops --- .../ops/op_math/symbolic.py | 12 ++--- .../ops/op_math/test_symbolic.py | 49 ++++--------------- 2 files changed, 16 insertions(+), 45 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 39ec6d7a281..7685f27e9f9 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -50,9 +50,9 @@ def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourc @staticmethod def adjoint_resource_decomp( - base_class, base_params, **kwargs + base_class, base_params ) -> Dict[re.CompressedResourceOp, int]: - return base_class._resource_decomp(**base_params) + return {base_class.resource_rep(**base_params): 1} @staticmethod def tracking_name(base_class, base_params) -> str: @@ -96,7 +96,7 @@ def resource_params(self) -> dict: @classmethod def resource_rep( - cls, base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + cls, base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> re.CompressedResourceOp: return re.CompressedResourceOp( cls, @@ -121,13 +121,13 @@ def controlled_resource_decomp( num_ctrl_values, num_work_wires, ) -> Dict[re.CompressedResourceOp, int]: - return cls._resource_decomp( + return {cls.resource_rep( base_class, base_params, outer_num_ctrl_wires + num_ctrl_wires, outer_num_ctrl_values + num_ctrl_values, outer_num_work_wires + num_work_wires, - ) + ): 1} @staticmethod def tracking_name(base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires): @@ -171,7 +171,7 @@ def resource_rep(cls, base_class, z, base_params, **kwargs) -> re.CompressedReso def pow_resource_decomp( cls, z0, base_class, z, base_params, **kwargs ) -> Dict[re.CompressedResourceOp, int]: - return cls._resource_decomp(base_class, z0 * z, base_params) + return {cls.resource_rep(base_class, z0 * z, base_params): 1} @staticmethod def tracking_name(base_class, z, base_params) -> str: diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index f7b31f6f691..afcca7163c9 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -53,7 +53,7 @@ def test_tracking_name(self, op, expected): assert name == expected @pytest.mark.parametrize( - "nested_op, base_op", + "nested_op, expected_op", [ ( re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))), @@ -85,20 +85,9 @@ def test_tracking_name(self, op, expected): ), ], ) - def test_nested_adjoints(self, nested_op, base_op): + def test_nested_adjoints(self, nested_op, expected_op): """Test the resources of nested Adjoints.""" - - nested_rep = nested_op.resource_rep_from_op() - nested_params = nested_rep.params - nested_type = nested_rep.op_type - nested_resources = nested_type.resources(**nested_params) - - base_op = base_op.resource_rep_from_op() - base_params = base_op.params - base_type = base_op.op_type - base_resources = base_type.resources(**base_params) - - assert nested_resources == base_resources + assert re.get_resources(nested_op) == re.get_resources(expected_op) class TestResourceControlled: @@ -170,11 +159,7 @@ def test_tracking_name(self, op, expected): ) def test_nested_controls(self, nested_op, expected_op): """Test the resources for nested Controlled operators.""" - - nested_resources = re.get_resources(nested_op) - expected_resources = re.get_resources(expected_op) - - assert nested_resources == expected_resources + assert re.get_resources(nested_op) == re.get_resources(expected_op) class TestResourcePow: @@ -206,39 +191,25 @@ def test_tracking_name(self, op, expected): assert name == expected @pytest.mark.parametrize( - "nested_op, base_op, z", + "nested_op, expected_op", [ ( re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 2), - re.ResourceQFT([0, 1]), - 4, + re.ResourcePow(re.ResourceQFT([0, 1]), 4), ), ( re.ResourcePow(re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 2), 2), - re.ResourceQFT([0, 1]), - 8, + re.ResourcePow(re.ResourceQFT([0, 1]), 8), ), ( re.ResourcePow( re.ResourcePow(re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 2), 2), 2, ), - re.ResourceQFT([0, 1]), - 16, + re.ResourcePow(re.ResourceQFT([0, 1]), 16), ), ], ) - def test_nested_pow(self, nested_op, base_op, z): + def test_nested_pow(self, nested_op, expected_op): """Test the resources for nested Pow operators.""" - - nested_rep = nested_op.resource_rep_from_op() - nested_params = nested_rep.params - nested_type = nested_rep.op_type - nested_resources = nested_type.resources(**nested_params) - - base_rep = base_op.resource_rep_from_op() - base_params = base_rep.params - base_type = base_rep.op_type - base_resources = base_type.resources(**base_params) - - assert nested_resources == _scale_dict(base_resources, z) + assert re.get_resources(nested_op) == re.get_resources(expected_op) From 8d24077d2289dc7f2c3fdbe7644248a55e956cd1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:30:37 -0500 Subject: [PATCH 266/335] remove unused import --- .../labs/tests/resource_estimation/ops/op_math/test_symbolic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index afcca7163c9..95f37a190f0 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -18,7 +18,6 @@ import pytest import pennylane.labs.resource_estimation as re -from pennylane.labs.resource_estimation.resource_container import _scale_dict # pylint: disable=protected-access,no-self-use From 046c02e895e455e0f3a95bca43cdbb6527eec5b7 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:31:46 -0500 Subject: [PATCH 267/335] remove nested tests --- .../ops/op_math/test_symbolic.py | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index 95f37a190f0..f7d0d594a5d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -51,43 +51,6 @@ def test_tracking_name(self, op, expected): name = rep.op_type.tracking_name(**rep.params) assert name == expected - @pytest.mark.parametrize( - "nested_op, expected_op", - [ - ( - re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))), - re.ResourceQFT([0, 1, 2]), - ), - ( - re.ResourceAdjoint( - re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) - ), - re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])), - ), - ( - re.ResourceAdjoint( - re.ResourceAdjoint( - re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) - ) - ), - re.ResourceQFT([0, 1, 2]), - ), - ( - re.ResourceAdjoint( - re.ResourceAdjoint( - re.ResourceAdjoint( - re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) - ) - ) - ), - re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])), - ), - ], - ) - def test_nested_adjoints(self, nested_op, expected_op): - """Test the resources of nested Adjoints.""" - assert re.get_resources(nested_op) == re.get_resources(expected_op) - class TestResourceControlled: """Tests for ResourceControlled""" @@ -139,27 +102,6 @@ def test_tracking_name(self, op, expected): name = rep.op_type.tracking_name(**rep.params) assert name == expected - @pytest.mark.parametrize( - "nested_op, expected_op", - [ - ( - re.ResourceControlled( - re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2] - ), - re.ResourceToffoli([0, 1, 2]), - ), - ( - re.ResourceControlled( - re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2] - ), - re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]), - ), - ], - ) - def test_nested_controls(self, nested_op, expected_op): - """Test the resources for nested Controlled operators.""" - assert re.get_resources(nested_op) == re.get_resources(expected_op) - class TestResourcePow: """Tests for ResourcePow""" From e266b89e359501f7dcb7574d3900bf9f8d3ffef1 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:32:13 -0500 Subject: [PATCH 268/335] add nested tests --- .../ops/op_math/test_symbolic.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index f7d0d594a5d..95f37a190f0 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -51,6 +51,43 @@ def test_tracking_name(self, op, expected): name = rep.op_type.tracking_name(**rep.params) assert name == expected + @pytest.mark.parametrize( + "nested_op, expected_op", + [ + ( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))), + re.ResourceQFT([0, 1, 2]), + ), + ( + re.ResourceAdjoint( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) + ), + re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])), + ), + ( + re.ResourceAdjoint( + re.ResourceAdjoint( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) + ) + ), + re.ResourceQFT([0, 1, 2]), + ), + ( + re.ResourceAdjoint( + re.ResourceAdjoint( + re.ResourceAdjoint( + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1, 2]))) + ) + ) + ), + re.ResourceAdjoint(re.ResourceQFT([0, 1, 2])), + ), + ], + ) + def test_nested_adjoints(self, nested_op, expected_op): + """Test the resources of nested Adjoints.""" + assert re.get_resources(nested_op) == re.get_resources(expected_op) + class TestResourceControlled: """Tests for ResourceControlled""" @@ -102,6 +139,27 @@ def test_tracking_name(self, op, expected): name = rep.op_type.tracking_name(**rep.params) assert name == expected + @pytest.mark.parametrize( + "nested_op, expected_op", + [ + ( + re.ResourceControlled( + re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2] + ), + re.ResourceToffoli([0, 1, 2]), + ), + ( + re.ResourceControlled( + re.ResourceControlled(re.ResourceX(0), control_wires=[1]), control_wires=[2] + ), + re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]), + ), + ], + ) + def test_nested_controls(self, nested_op, expected_op): + """Test the resources for nested Controlled operators.""" + assert re.get_resources(nested_op) == re.get_resources(expected_op) + class TestResourcePow: """Tests for ResourcePow""" From af89cccd8d5d8b3713df7f0b5d041cd0e96c4c66 Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:34:03 -0500 Subject: [PATCH 269/335] add docstrings back --- .../ops/qubit/qchem_ops.py | 150 +++++++++++------- 1 file changed, 96 insertions(+), 54 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 1c1d356d3c8..47a29dc5e0a 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -1,5 +1,3 @@ -# Copyright 2024 Xanadu Quantum Technologies Inc. - # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -19,30 +17,24 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): - """Resource class for the SingleExcitation gate.""" + r"""Resource class for the SingleExcitation gate. - @staticmethod - def _resource_decomp(*args, **kwargs): - t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) - h = re.ResourceHadamard.resource_rep() - s = re.ResourceS.resource_rep() - s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) - cnot = re.ResourceCNOT.resource_rep() - rz = re.ResourceRZ.resource_rep() - ry = re.ResourceRY.resource_rep() - t = re.ResourceT.resource_rep() + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. - gate_types = {} - gate_types[t_dag] = 2 - gate_types[h] = 4 - gate_types[s] = 2 - gate_types[s_dag] = 2 - gate_types[cnot] = 2 - gate_types[rz] = 1 - gate_types[ry] = 1 - gate_types[t] = 2 + .. math:: U(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & 1 + \end{bmatrix}. - return gate_types + """ + + @staticmethod + def _resource_decomp(*args, **kwargs): + """TODO: implement in resource_symbolic_ops branch""" + raise re.ResourcesNotDefined def resource_params(self): return {} @@ -53,7 +45,18 @@ def resource_rep(cls, **kwargs): class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperator): - """Resource class for the SingleExcitationMinus gate.""" + r"""Resource class for the SingleExcitationMinus gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U_-(\phi) = \begin{bmatrix} + e^{-i\phi/2} & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{-i\phi/2} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -79,7 +82,18 @@ def resource_rep(cls, **kwargs): class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator): - """Resource class for the SingleExcitationPlus gate.""" + r"""Resource class for the SingleExcitationPlus gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U_+(\phi) = \begin{bmatrix} + e^{i\phi/2} & 0 & 0 & 0 \\ + 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ + 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{i\phi/2} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -105,7 +119,16 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): - """Resource class for the DoubleExcitation gate.""" + r"""Resource class for the DoubleExcitation gate. + + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + + &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle + \sin(\phi/2) |1100\rangle\\ + &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle - \sin(\phi/2) |0011\rangle, + """ @staticmethod def _resource_decomp(*args, **kwargs): @@ -130,22 +153,22 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator): - """Resource class for the DoubleExcitationMinus gate.""" + r"""Resource class for the DoubleExcitationMinus gate. - @staticmethod - def _resource_decomp(*args, **kwargs): - phase = re.ResourceGlobalPhase.resource_rep() - double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) - ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) - gate_types = {} - gate_types[phase] = 1 - gate_types[double] = 1 - gate_types[ctrl_z] = 2 - gate_types[ctrl_phase] = 2 + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. - return gate_types + .. math:: + + &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ + &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ + &|x\rangle \rightarrow e^{-i\phi/2} |x\rangle, + """ + + @staticmethod + def _resource_decomp(*args, **kwargs): + """TODO: implement in resource_symbolic_op branch""" def resource_params(self): return {} @@ -156,22 +179,22 @@ def resource_rep(cls, **kwargs): class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator): - """Resource class for the DoubleExcitationPlus gate.""" + r"""Resource class for the DoubleExcitationPlus gate. - @staticmethod - def _resource_decomp(*args, **kwargs): - phase = re.ResourceGlobalPhase.resource_rep() - double = re.ResourceDoubleExcitation.resource_rep() - ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) - ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. - gate_types = {} - gate_types[phase] = 1 - gate_types[double] = 1 - gate_types[ctrl_z] = 2 - gate_types[ctrl_phase] = 2 + .. math:: - return gate_types + &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ + &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ + &|x\rangle \rightarrow e^{i\phi/2} |x\rangle, + """ + + @staticmethod + def _resource_decomp(*args, **kwargs): + """TODO: implement in resource_symbolic_op branch""" + raise re.ResourcesNotDefined def resource_params(self): return {} @@ -182,7 +205,15 @@ def resource_rep(cls, **kwargs): class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): - """Resource class for the OrbitalRotation gate.""" + r"""Resource class for the OrbitalRotation gate. + + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + &|\Phi_{0}\rangle = \cos(\phi/2)|\Phi_{0}\rangle - \sin(\phi/2)|\Phi_{1}\rangle\\ + &|\Phi_{1}\rangle = \cos(\phi/2)|\Phi_{0}\rangle + \sin(\phi/2)|\Phi_{1}\rangle, + """ @staticmethod def _resource_decomp(**kwargs): @@ -204,7 +235,18 @@ def resource_rep(cls, **kwargs): class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): - """Resource class for the FermionicSWAP gate.""" + r"""Resource class for the FermionicSWAP gate. + + Resources: + The resources are obtained by decomposing the following matrix into fundamental gates. + + .. math:: U(\phi) = \begin{bmatrix} + 1 & 0 & 0 & 0 \\ + 0 & e^{i \phi/2} \cos(\phi/2) & -ie^{i \phi/2} \sin(\phi/2) & 0 \\ + 0 & -ie^{i \phi/2} \sin(\phi/2) & e^{i \phi/2} \cos(\phi/2) & 0 \\ + 0 & 0 & 0 & e^{i \phi} + \end{bmatrix}. + """ @staticmethod def _resource_decomp(*args, **kwargs): From d35d0639a900fbb359a2fea213ddf87a7a50007b Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:37:20 -0500 Subject: [PATCH 270/335] formatting --- .../ops/op_math/symbolic.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 7685f27e9f9..cd7042cd87f 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -49,9 +49,7 @@ def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourc return re.CompressedResourceOp(cls, {"base_class": base_class, "base_params": base_params}) @staticmethod - def adjoint_resource_decomp( - base_class, base_params - ) -> Dict[re.CompressedResourceOp, int]: + def adjoint_resource_decomp(base_class, base_params) -> Dict[re.CompressedResourceOp, int]: return {base_class.resource_rep(**base_params): 1} @staticmethod @@ -121,13 +119,15 @@ def controlled_resource_decomp( num_ctrl_values, num_work_wires, ) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep( - base_class, - base_params, - outer_num_ctrl_wires + num_ctrl_wires, - outer_num_ctrl_values + num_ctrl_values, - outer_num_work_wires + num_work_wires, - ): 1} + return { + cls.resource_rep( + base_class, + base_params, + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ): 1 + } @staticmethod def tracking_name(base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires): From eb0a13ee89811c5f296ee608705effd32939393a Mon Sep 17 00:00:00 2001 From: Will Date: Thu, 28 Nov 2024 15:37:39 -0500 Subject: [PATCH 271/335] formatting --- .../ops/op_math/symbolic.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 7685f27e9f9..cd7042cd87f 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -49,9 +49,7 @@ def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourc return re.CompressedResourceOp(cls, {"base_class": base_class, "base_params": base_params}) @staticmethod - def adjoint_resource_decomp( - base_class, base_params - ) -> Dict[re.CompressedResourceOp, int]: + def adjoint_resource_decomp(base_class, base_params) -> Dict[re.CompressedResourceOp, int]: return {base_class.resource_rep(**base_params): 1} @staticmethod @@ -121,13 +119,15 @@ def controlled_resource_decomp( num_ctrl_values, num_work_wires, ) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep( - base_class, - base_params, - outer_num_ctrl_wires + num_ctrl_wires, - outer_num_ctrl_values + num_ctrl_values, - outer_num_work_wires + num_work_wires, - ): 1} + return { + cls.resource_rep( + base_class, + base_params, + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ): 1 + } @staticmethod def tracking_name(base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires): From 0bce904c7f89aba4e76a9d063be46ecc4bfb8f3f Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 09:22:11 -0500 Subject: [PATCH 272/335] tracking name test --- .../tests/resource_estimation/templates/test_resource_qft.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index 8bb4b1ef0ef..bf6e898547e 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -78,3 +78,8 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph actual = rep.op_type.resources(**rep.params) assert actual == expected + + @pytest.mark.parametrize("num_wires", range(10)) + def test_tracking_name(self, num_wires): + """Test that the tracking name is correct.""" + assert re.ResourceQFT.tracking_name(num_wires+1) == f"QFT({num_wires+1})" From 188264994cc0f4ebe50eb2f054485771d33890e8 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 10:46:46 -0500 Subject: [PATCH 273/335] Apply suggestions from code review Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> --- .../labs/resource_estimation/resource_tracking.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index b6905f79796..6ad2c4ccb83 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -70,13 +70,13 @@ def get_resources( Args: obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. - gate_set (Set, optional): A set (str) specifying the names of opertions to track. Defaults to DefaultGateSet. + gate_set (Set, optional): A set (str) specifying the names of operations to track. Defaults to DefaultGateSet. config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. Returns: Resources: The total resources of the quantum circuit. - Rasies: + Raises: TypeError: "Could not obtain resources for obj of type (type(obj))". **Example** @@ -275,7 +275,7 @@ def _temp_map_func(op: Operation) -> ResourceOperator: def _clean_gate_counts(gate_counts: Dict[CompressedResourceOp, int]) -> Dict[str, int]: """Map resources with gate_types made from CompressedResourceOps - into one which tracks just strings of operations! + into one which tracks just strings of operations. Args: gate_counts (Dict[CompressedResourceOp, int]): gate counts in terms of compressed resource ops @@ -296,10 +296,10 @@ def _operations_to_compressed_reps(ops: Iterable[Operation]) -> List[CompressedR """Convert the sequence of operations to a list of compressed resource ops. Args: - ops (Iterable[Operation]): set of operations to convert. + ops (Iterable[Operation]): set of operations to convert Returns: - List[CompressedResourceOp]: set of converted compressed resource ops. + List[CompressedResourceOp]: set of converted compressed resource ops """ cmp_rep_ops = [] for op in ops: From 3acd1fe0a407dccd345c62edd785e0c57ff761ac Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 10:48:21 -0500 Subject: [PATCH 274/335] fix ctrl_values --- pennylane/labs/resource_estimation/ops/op_math/symbolic.py | 2 +- .../tests/resource_estimation/ops/op_math/test_symbolic.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index cd7042cd87f..03b059dd5d3 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -88,7 +88,7 @@ def resource_params(self) -> dict: "base_class": type(self.base), "base_params": self.base.resource_params(), "num_ctrl_wires": len(self.control_wires), - "num_ctrl_values": len([val for val in self.control_values if val]), + "num_ctrl_values": len([val for val in self.control_values if not val]), "num_work_wires": len(self.work_wires), } diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index f7d0d594a5d..98d4f530781 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -64,20 +64,20 @@ def test_resource_params(self): "base_class": re.ResourceQFT, "base_params": base.resource_params(), "num_ctrl_wires": 1, - "num_ctrl_values": 1, + "num_ctrl_values": 0, "num_work_wires": 0, } @pytest.mark.parametrize( "op, expected", [ - (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT(2),1,1,0)"), + (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT(2),1,0,0)"), ( re.ResourceControlled( re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), control_wires=[3], ), - "C(C(QFT(2),1,1,0),1,1,0)", + "C(C(QFT(2),1,0,0),1,0,0)", ), ( re.ResourceControlled( From ee4b59a3288643b4a330ef3ab408dbfea5eb9864 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 10:51:32 -0500 Subject: [PATCH 275/335] code review comments --- pennylane/labs/resource_estimation/resource_tracking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 6ad2c4ccb83..cd281afb297 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -86,6 +86,7 @@ def get_resources( .. code-block:: python + import copy import pennylane.labs.resource_estimation as re def my_circuit(): From fe1a247f28b64943540b4feb5ca539c9f37f6c55 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 11:06:00 -0500 Subject: [PATCH 276/335] add kwargs --- pennylane/labs/resource_estimation/ops/op_math/symbolic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 03b059dd5d3..adeebd8a1fe 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -33,7 +33,7 @@ def _resource_decomp(base_class, base_params, **kwargs) -> Dict[re.CompressedRes return base_class.adjoint_resource_decomp(**base_params) except re.ResourcesNotDefined: gate_types = defaultdict(int) - decomp = base_class.resources(**base_params) + decomp = base_class.resources(**base_params, **kwargs) for gate, count in decomp.items(): resources = gate.op_type.adjoint_resource_decomp(**gate.params) _scale_dict(resources, count, in_place=True) @@ -73,7 +73,7 @@ def _resource_decomp( pass gate_types = defaultdict(int) - decomp = base_class.resources(**base_params) + decomp = base_class.resources(**base_params, **kwargs) for gate, count in decomp.items(): resources = gate.op_type.controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **gate.params @@ -148,7 +148,7 @@ def _resource_decomp( pass try: - return _scale_dict(base_class.resources(**base_params), z) + return _scale_dict(base_class.resources(**base_params, **kwargs), z) except re.ResourcesNotDefined: pass From 60814711a556094cd167954cdcca327dcd5eec12 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 11:13:31 -0500 Subject: [PATCH 277/335] fix docs + format --- .../resource_estimation/resource_tracking.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index cd281afb297..5fefd9b9c7a 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -28,7 +28,7 @@ # pylint: disable=dangerous-default-value,protected-access -_StandardGateSet = { +_StandardGateSet = { # user-friendly gateset for visual checks and initial compilation "PauliX", "PauliY", "PauliZ", @@ -45,7 +45,7 @@ } -DefaultGateSet = { +DefaultGateSet = { # practical/realistic gateset for useful compilation of circuits "Hadamard", "CNOT", "S", @@ -54,7 +54,7 @@ } -resource_config = { +resource_config = { # configurations for further configuration of the decompositions "error_rx": 10e-3, "error_ry": 10e-3, "error_rz": 10e-3, @@ -69,15 +69,15 @@ def get_resources( in the gate_set. Args: - obj (Union[Operation, Callable, QuantumScript]): The quantum circuit or operation to obtain resources from. - gate_set (Set, optional): A set (str) specifying the names of operations to track. Defaults to DefaultGateSet. - config (Dict, optional): Additional configurations to specify how resources are tracked. Defaults to resource_config. + obj (Union[Operation, Callable, QuantumScript]): the quantum circuit or operation to obtain resources from + gate_set (Set, optional): python set of strings specifying the names of operations to track + config (Dict, optional): dictionary of additiona; configurations that specify how resources are computed Returns: - Resources: The total resources of the quantum circuit. + Resources: the total resources of the quantum circuit Raises: - TypeError: "Could not obtain resources for obj of type (type(obj))". + TypeError: could not obtain resources for obj of type `type(obj)` **Example** @@ -176,7 +176,7 @@ def resources_from_operation( def resources_from_qfunc( obj: Callable, gate_set: Set = DefaultGateSet, config: Dict = resource_config ) -> Callable: - """Get resources from a quantum function which queues operations!""" + """Get resources from a quantum function which queues operations""" @wraps(obj) def wrapper(*args, **kwargs): From 8a40ecc3adc1eafa01786d4aedfad9b16f2a6816 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 11:21:52 -0500 Subject: [PATCH 278/335] fix failing tests after ctrl value change --- .../ops/qubit/non_parametric_ops.py | 16 +++--- .../ops/qubit/parametric_ops_single_qubit.py | 6 +-- .../ops/qubit/qchem_ops.py | 49 +++++++++++++++++-- .../ops/qubit/test_non_parametric_ops.py | 4 +- .../qubit/test_parametric_ops_single_qubit.py | 2 +- 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index b1adc302c68..edda7448995 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -42,7 +42,7 @@ def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int] def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return {re.ResourceCH.resource_rep(): 1} raise re.ResourcesNotDefined @@ -137,7 +137,7 @@ def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int] def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return {re.ResourceCSWAP.resource_rep(): 1} raise re.ResourcesNotDefined @@ -199,14 +199,14 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs): - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return {re.ResourceCNOT.resource_rep(): 1} - if num_ctrl_wires == 2 and num_ctrl_values == 2: + if num_ctrl_wires == 2 and num_ctrl_values == 0: return {re.ResourceToffoli.resource_rep(): 1} - return re.ResourceMultiControlledX.resource_rep( + return {re.ResourceMultiControlledX.resource_rep( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ) + ): 1} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -242,7 +242,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return re.ResourceCY.resources(**kwargs) raise re.ResourcesNotDefined @@ -279,7 +279,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return re.ResourceCZ.resources(**kwargs) if num_ctrl_wires == 2 and num_ctrl_wires == 2: diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 66238d2aba4..21a6d295dc2 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -102,7 +102,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return {re.ResourceCRX.resource_rep(): 1} raise re.ResourcesNotDefined @@ -134,7 +134,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return {re.ResourceCRY.resource_rep(): 1} raise re.ResourcesNotDefined @@ -171,7 +171,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0: return {re.ResourceCRZ.resource_rep(): 1} raise re.ResourcesNotDefined diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 47a29dc5e0a..740d8f391ad 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -33,8 +33,26 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_ops branch""" - raise re.ResourcesNotDefined + t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) + h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep() + s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() + t = re.ResourceT.resource_rep() + + gate_types = {} + gate_types[t_dag] = 2 + gate_types[h] = 4 + gate_types[s] = 2 + gate_types[s_dag] = 2 + gate_types[cnot] = 2 + gate_types[rz] = 1 + gate_types[ry] = 1 + gate_types[t] = 2 + + return gate_types def resource_params(self): return {} @@ -168,7 +186,18 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" + phase = re.ResourceGlobalPhase.resource_rep() + double = re.ResourceDoubleExcitation.resource_rep() + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) + + gate_types = {} + gate_types[phase] = 1 + gate_types[double] = 1 + gate_types[ctrl_z] = 2 + gate_types[ctrl_phase] = 2 + + return gate_types def resource_params(self): return {} @@ -193,8 +222,18 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator @staticmethod def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" - raise re.ResourcesNotDefined + phase = re.ResourceGlobalPhase.resource_rep() + double = re.ResourceDoubleExcitation.resource_rep() + ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) + ctrl_phase = re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 1, 0) + + gate_types = {} + gate_types[phase] = 1 + gate_types[double] = 1 + gate_types[ctrl_z] = 2 + gate_types[ctrl_phase] = 2 + + return gate_types def resource_params(self): return {} diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index eeafd9398e5..ca888e4e8f1 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -50,7 +50,7 @@ def test_adjoint_decomp(self): def test_controlled_decomp(self): """Test that the controlled decomposition is correct.""" expected = {re.ResourceCH.resource_rep(): 1} - assert re.ResourceHadamard.controlled_resource_decomp(1, 1, 0) == expected + assert re.ResourceHadamard.controlled_resource_decomp(1, 0, 0) == expected controlled_h = re.ResourceControlled(re.ResourceHadamard(0), control_wires=[1]) ch = re.ResourceCH([0, 1]) @@ -116,7 +116,7 @@ def test_adjoint_decomp(self): def test_controlled_decomp(self): """Test that the controlled decomposition is correct.""" expected = {re.ResourceCSWAP.resource_rep(): 1} - assert re.ResourceSWAP.controlled_resource_decomp(1, 1, 0) == expected + assert re.ResourceSWAP.controlled_resource_decomp(1, 0, 0) == expected controlled_swap = re.ResourceControlled(re.ResourceSWAP([0, 1]), control_wires=[2]) cswap = re.ResourceCSWAP([0, 1, 2]) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 3c94748ded5..35930036102 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -132,7 +132,7 @@ def test_pow_decomposition(self, resource_class, epsilon, z): def test_controlled_decomposition(self, resource_class, controlled_class, epsilon): """Test that the controlled decompositions are correct.""" expected = {controlled_class.resource_rep(): 1} - assert resource_class.controlled_resource_decomp(1, 1, 0) == expected + assert resource_class.controlled_resource_decomp(1, 0, 0) == expected op = resource_class(1.24, wires=0) c_op = re.ResourceControlled(op, control_wires=[1]) From 90711119397e6b2b206f15f252be4fe1be960cbb Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 12:06:52 -0500 Subject: [PATCH 279/335] fixed comments --- .../labs/resource_estimation/resource_tracking.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index 5fefd9b9c7a..de93d687901 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -28,7 +28,8 @@ # pylint: disable=dangerous-default-value,protected-access -_StandardGateSet = { # user-friendly gateset for visual checks and initial compilation +# user-friendly gateset for visual checks and initial compilation +_StandardGateSet = { "PauliX", "PauliY", "PauliZ", @@ -44,8 +45,8 @@ "PhaseShift", } - -DefaultGateSet = { # practical/realistic gateset for useful compilation of circuits +# practical/realistic gateset for useful compilation of circuits +DefaultGateSet = { "Hadamard", "CNOT", "S", @@ -54,7 +55,8 @@ } -resource_config = { # configurations for further configuration of the decompositions +# parameters for further configuration of the decompositions +resource_config = { "error_rx": 10e-3, "error_ry": 10e-3, "error_rz": 10e-3, From b1a871155cd0b228d0b030c2ddd6ce22f871aa4e Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 14:29:21 -0500 Subject: [PATCH 280/335] remove resource config and DefaultGateSet from docs --- pennylane/labs/resource_estimation/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index b03b7e9e059..15e2f86a69c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -70,8 +70,6 @@ :toctree: api ~get_resources - ~DefaultGateSet - ~resource_config Exceptions ~~~~~~~~~~ From e305474286960266d02ba4a716b67d5e63d46b49 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 14:54:38 -0500 Subject: [PATCH 281/335] redo control decomposition logic --- .../ops/op_math/symbolic.py | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index adeebd8a1fe..c404e22b998 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -61,9 +61,9 @@ def tracking_name(base_class, base_params) -> str: class ResourceControlled(ControlledOp, re.ResourceOperator): """Resource class for Controlled""" - @staticmethod + @classmethod def _resource_decomp( - base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + cls, base_class, base_params, num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: try: return base_class.controlled_resource_decomp( @@ -72,17 +72,23 @@ def _resource_decomp( except re.ResourcesNotDefined: pass - gate_types = defaultdict(int) - decomp = base_class.resources(**base_params, **kwargs) - for gate, count in decomp.items(): - resources = gate.op_type.controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **gate.params - ) - _scale_dict(resources, count, in_place=True) - _combine_dict(gate_types, resources, in_place=True) + gate_types = {} + + if num_ctrl_values == 0: + decomp = base_class.resources(**base_params, **kwargs) + for gate, count in decomp.items(): + rep = cls.resource_rep(gate.op_type, gate.params, num_ctrl_wires, 0, num_work_wires) + gate_types[rep] = count return gate_types + no_control = cls.resource_rep(base_class, base_params, num_ctrl_wires, 0, num_work_wires) + x = re.ResourceX.resource_rep() + gate_types[no_control] = 1 + gate_types[x] = 2*num_ctrl_values + + return gate_types + def resource_params(self) -> dict: return { "base_class": type(self.base), @@ -177,3 +183,34 @@ def pow_resource_decomp( def tracking_name(base_class, z, base_params) -> str: base_name = base_class.tracking_name(**base_params) return f"({base_name})**{z}" + +def zyz_resources(num_ctrl_wires, num_work_wires): + """The zyz decomposition of a controlled unitary from SU(2) + defined in https://arxiv.org/pdf/quant-ph/9503016""" + + gate_types = {} + + # Lemma 5.1 + if num_ctrl_wires == 1: + cnot = re.ResourceCNOT.resource_rep() + phase = re.ResourceGlobalPhase.resource_rep() + ry = re.ResourceRZ.resource_rep() + rz = re.ResourceRZ.resource_rep() + + gate_types[cnot] = 2 + gate_types[phase] = 1 + gate_types[ry] = 2 + gate_types[rz] = 3 + + return gate_types + + # Lemma 7.9 + cry = re.ResourceCRY.resource_rep() + crz = re.ResourceCRZ.resource_rep() + multix = re.ResourceMultiControlledX.resource_rep(num_ctrl_wires-1, 0, num_work_wires) + + gate_types[cry] = 2 + gate_types[crz] = 3 + gate_types[multix] = 2 + + return gate_types From dcf2630fd6abff31d13c5d600b6cb39f7d74e41f Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 15:00:21 -0500 Subject: [PATCH 282/335] formatting --- pennylane/labs/resource_estimation/ops/op_math/symbolic.py | 5 +++-- .../tests/resource_estimation/templates/test_resource_qft.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index c404e22b998..136a9b55ad2 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -85,7 +85,7 @@ def _resource_decomp( no_control = cls.resource_rep(base_class, base_params, num_ctrl_wires, 0, num_work_wires) x = re.ResourceX.resource_rep() gate_types[no_control] = 1 - gate_types[x] = 2*num_ctrl_values + gate_types[x] = 2 * num_ctrl_values return gate_types @@ -184,6 +184,7 @@ def tracking_name(base_class, z, base_params) -> str: base_name = base_class.tracking_name(**base_params) return f"({base_name})**{z}" + def zyz_resources(num_ctrl_wires, num_work_wires): """The zyz decomposition of a controlled unitary from SU(2) defined in https://arxiv.org/pdf/quant-ph/9503016""" @@ -207,7 +208,7 @@ def zyz_resources(num_ctrl_wires, num_work_wires): # Lemma 7.9 cry = re.ResourceCRY.resource_rep() crz = re.ResourceCRZ.resource_rep() - multix = re.ResourceMultiControlledX.resource_rep(num_ctrl_wires-1, 0, num_work_wires) + multix = re.ResourceMultiControlledX.resource_rep(num_ctrl_wires - 1, 0, num_work_wires) gate_types[cry] = 2 gate_types[crz] = 3 diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index bf6e898547e..b5cf4f94c89 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -82,4 +82,4 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph @pytest.mark.parametrize("num_wires", range(10)) def test_tracking_name(self, num_wires): """Test that the tracking name is correct.""" - assert re.ResourceQFT.tracking_name(num_wires+1) == f"QFT({num_wires+1})" + assert re.ResourceQFT.tracking_name(num_wires + 1) == f"QFT({num_wires+1})" From 9a199c8674032645354acae63a52a4c01f8043fa Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 15:50:02 -0500 Subject: [PATCH 283/335] fix control value error --- .../labs/resource_estimation/ops/qubit/non_parametric_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index edda7448995..2644996be27 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -282,7 +282,7 @@ def controlled_resource_decomp( if num_ctrl_wires == 1 and num_ctrl_values == 0: return re.ResourceCZ.resources(**kwargs) - if num_ctrl_wires == 2 and num_ctrl_wires == 2: + if num_ctrl_wires == 2 and num_ctrl_wires == 0: return re.ResourceCCZ.resources(**kwargs) raise re.ResourcesNotDefined From 27b436c6698da919d81dffb5eb90f13f2b6732df Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 29 Nov 2024 16:00:54 -0500 Subject: [PATCH 284/335] update CY --- .../resource_estimation/ops/op_math/controlled_ops.py | 4 +++- .../resource_estimation/ops/qubit/non_parametric_ops.py | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index c6d1dd629da..4d91ea11d96 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -99,9 +99,11 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: cnot = re.ResourceCNOT.resource_rep() s = re.ResourceS.resource_rep() + s_dag = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) gate_types[cnot] = 1 - gate_types[s] = 2 # Assuming S^dagg ~ S in cost! + gate_types[s] = 1 + gate_types[s_dag] = 1 return gate_types diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 2644996be27..97b030f1559 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -204,9 +204,11 @@ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires, if num_ctrl_wires == 2 and num_ctrl_values == 0: return {re.ResourceToffoli.resource_rep(): 1} - return {re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs - ): 1} + return { + re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + ): 1 + } @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: From a00b55cd5660b281b1b258e301ff1abe91ee6eff Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 19:21:33 -0500 Subject: [PATCH 285/335] Added tests and docs --- .../ops/op_math/controlled_ops.py | 90 ++-- .../ops/op_math/test_controlled_ops.py | 390 ++++++++++++++++-- 2 files changed, 431 insertions(+), 49 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 685e155b63c..a2f8d749010 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -24,7 +24,8 @@ class ResourceCH(qml.CH, re.ResourceOperator): r"""Resource class for CH gate. Resources: - The resources are derived from the following identities: + The resources are derived from the following identities (as presented in this + `blog post `_): .. math:: @@ -38,9 +39,6 @@ class ResourceCH(qml.CH, re.ResourceOperator): """ - # TODO: Reference this: - # https://quantumcomputing.stackexchange.com/questions/15734/how-to-construct-a-controlled-hadamard-gate-using-single-qubit-gates-and-control - @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} @@ -83,7 +81,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: s = re.ResourceS.resource_rep() gate_types[cnot] = 1 - gate_types[s] = 2 # Assuming S^dagg ~ S in cost! + gate_types[s] = 1 + 3 # S^dagg = 3*S in cost return gate_types @@ -252,7 +250,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types[h] = 3 gate_types[s] = 1 gate_types[cz] = 1 - gate_types[t] = 4 + gate_types[t] = 2 + 2 * (7) # T^dagg = 7*T in cost return gate_types @@ -298,24 +296,56 @@ class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): Resources: The resources are obtained from (table 3.) the paper `Polylogarithmic-depth controlled-NOT gates - without ancilla qubits `_. The resources are - estimated using the following formulas. + without ancilla qubits `_. Specifically, the + resources are given by the following rules: + + * If there is only one control qubit, treat the resources as a :code:`CNOT` gate. + + * If there are two control qubits, treat the resources as a :code:`Toffoli` gate. - If the number of control qubits is 0 + * If there are three control qubits, the resources are two :code:`CNOT` gates and + one :code:`Toffoli` gate. + + * If there are more than three control qubits (:math:`n`), the resources are given by + :math:`36n - 111` :code:`CNOT` gates. """ + # TODO: There is a more efficient resource decomposition, need to update this based on the paper. + @staticmethod def _resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs ) -> Dict[re.CompressedResourceOp, int]: - raise re.ResourcesNotDefined + gate_types = {} + cnot = re.ResourceCNOT.resource_rep() + toffoli = re.ResourceToffoli.resource_rep() + + if num_ctrl_values: + x = re.ResourceX.resource_rep() + gate_types[x] = num_ctrl_values * 2 + + if num_ctrl_wires == 1: + gate_types[cnot] = 1 + return gate_types + + if num_ctrl_wires == 2: + gate_types[toffoli] = 1 + return gate_types + + if num_ctrl_wires == 3: + gate_types[cnot] = 2 + gate_types[toffoli] = 1 + return gate_types + + gate_types[cnot] = 36 * num_ctrl_wires - 111 # Barenco 1995 + return gate_types def resource_params(self) -> dict: num_control = len(self.hyperparameters["control_wires"]) num_work_wires = len(self.hyperparameters["work_wires"]) - num_control_values = len([val for val in self.hyperparameters["control_values"] if val]) + num_control_values = len([val for val in self.hyperparameters["control_values"] if not val]) return { "num_ctrl_wires": num_control, @@ -443,7 +473,23 @@ class ResourceCRot(qml.CRot, re.ResourceOperator): r"""Resource class for CRot Resources: - TODO: Add a source for resources! + The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. The resources are derived with the following identities: + + .. math:: + + \begin{align} + \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}, \\ + \hat{RY}(- \theta) = \hat{X} \dot \hat{RY}(\theta) \dot \hat{X}. + \end{align} + + This identity is applied along with some clever choices for the angle values to combine rotation; + the final circuit takes the form: + + .. code-block:: bash + + ctrl: ─────╭●─────────╭●─────────┤ + trgt: ──RZ─╰X──RZ──RY─╰X──RY──RZ─┤ """ @@ -453,7 +499,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: cnot = re.ResourceCNOT.resource_rep() rz = re.ResourceRZ.resource_rep() - ry = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 3 @@ -472,20 +518,12 @@ def resource_rep(cls) -> re.CompressedResourceOp: class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for the ControlledPhaseShift gate. - Resources: - The resources come from the following identity expressing Controlled Phase Shift - as a product of Phase Shifts and CNOTs. - - - .. math:: + Resources: + The resources are derived using the fact that a :code:`PhaseShift` gate is identical to + the :code:`RZ` gate up to some global phase. Furthermore, a controlled global phase simplifies + to a :code:`PhaseShift` gate. This gives rise to the following identity: - CR_\phi(\phi) = \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & 1 & 0 & 0 \\ - 0 & 0 & 1 & 0 \\ - 0 & 0 & 0 & e^{i\phi} - \end{bmatrix} = - (R_\phi(\phi/2) \otimes I) \cdot CNOT \cdot (I \otimes R_\phi(-\phi/2)) \cdot CNOT \cdot (I \otimes R_\phi(\phi/2)) + .. math:: CR_\phi(\phi) = (R_\phi(\phi/2) \otimes I) \cdot CNOT \cdot (I \otimes R_\phi(-\phi/2)) \cdot CNOT \cdot (I \otimes R_\phi(\phi/2)) """ diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 07d95ef8c9e..be307582389 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -21,7 +21,372 @@ # pylint: disable=no-self-use, use-implicit-booleaness-not-comparison -class TestControlledPhaseShift: +class TestResourceCH: + """Test the ResourceCH operation""" + + op = re.ResourceCH(wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceRY.resource_rep(): 2, + re.ResourceHadamard.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 1, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCH, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCY: + """Test the ResourceCY operation""" + + op = re.ResourceCY(wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceS.resource_rep(): 4, + re.ResourceCNOT.resource_rep(): 1, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCY, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCZ: + """Test the ResourceCZ operation""" + + op = re.ResourceCZ(wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceHadamard.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 1, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCZ, {}) + assert self.op.resource_rep() == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCSWAP: + """Test the ResourceCSWAP operation""" + + op = re.ResourceCSWAP(wires=[0, 1, 2]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + expected_resources = { + re.ResourceToffoli.resource_rep(): 1, + re.ResourceCNOT.resource_rep(): 2, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCSWAP, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCCZ: + """Test the ResourceCZZ operation""" + + op = re.ResourceCCZ(wires=[0, 1, 2]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + expected_resources = { + re.ResourceHadamard.resource_rep(): 2, + re.ResourceToffoli.resource_rep(): 1, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCCZ, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCNOT: + """Test ResourceCNOT operation""" + + op = re.ResourceCNOT([0, 1]) + + def test_resources(self): + """Test that the resources method is not implemented""" + with pytest.raises(re.ResourcesNotDefined): + self.op.resources() + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected = re.CompressedResourceOp(re.ResourceCNOT, {}) + assert self.op.resource_rep() == expected + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceToffoli: + """Test the ResourceToffoli operation""" + + op = re.ResourceToffoli(wires=[0, 1, 2]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceS.resource_rep(): 1, + re.ResourceT.resource_rep(): 16, + re.ResourceCZ.resource_rep(): 1, + re.ResourceCNOT.resource_rep(): 9, + re.ResourceHadamard.resource_rep(): 3, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceToffoli, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceMultiControlledX: + """Test the ResourceMultiControlledX operation""" + + res_ops = ( + re.ResourceMultiControlledX(control_wires=[0], wires=["t"], control_values=[1]), + re.ResourceMultiControlledX(control_wires=[0, 1], wires=["t"], control_values=[1, 1]), + re.ResourceMultiControlledX(control_wires=[0, 1, 2], wires=["t"], control_values=[1, 1, 1]), + re.ResourceMultiControlledX( + control_wires=[0, 1, 2, 3, 4], wires=["t"], control_values=[1, 1, 1, 1, 1] + ), + re.ResourceMultiControlledX( + control_wires=[0], wires=["t"], control_values=[0], work_wires=["w1"] + ), + re.ResourceMultiControlledX( + control_wires=[0, 1], wires=["t"], control_values=[1, 0], work_wires=["w1", "w2"] + ), + re.ResourceMultiControlledX(control_wires=[0, 1, 2], wires=["t"], control_values=[0, 0, 1]), + re.ResourceMultiControlledX( + control_wires=[0, 1, 2, 3, 4], + wires=["t"], + control_values=[1, 0, 0, 1, 0], + work_wires=["w1"], + ), + ) + + res_params = ( + (1, 0, 0), + (2, 0, 0), + (3, 0, 0), + (5, 0, 0), + (1, 1, 1), + (2, 1, 2), + (3, 2, 0), + (5, 3, 1), + ) + + expected_resources = ( + {re.ResourceCNOT.resource_rep(): 1}, + {re.ResourceToffoli.resource_rep(): 1}, + { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceToffoli.resource_rep(): 1, + }, + {re.ResourceCNOT.resource_rep(): 69}, + { + re.ResourceX.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 1, + }, + { + re.ResourceX.resource_rep(): 2, + re.ResourceToffoli.resource_rep(): 1, + }, + { + re.ResourceX.resource_rep(): 4, + re.ResourceCNOT.resource_rep(): 2, + re.ResourceToffoli.resource_rep(): 1, + }, + { + re.ResourceX.resource_rep(): 6, + re.ResourceCNOT.resource_rep(): 69, + }, + ) + + @staticmethod + def _prep_params(num_control, num_control_values, num_work_wires): + return { + "num_ctrl_wires": num_control, + "num_ctrl_values": num_control_values, + "num_work_wires": num_work_wires, + } + + @pytest.mark.parametrize("params, expected_res", zip(res_params, expected_resources)) + def test_resources(self, params, expected_res): + """Test that the resources method produces the expected resources.""" + op_resource_params = self._prep_params(*params) + assert re.ResourceMultiControlledX.resources(**op_resource_params) == expected_res + + @pytest.mark.parametrize("op, params", zip(res_ops, res_params)) + def test_resource_rep(self, op, params): + """Test the resource_rep produces the correct compressed representation.""" + op_resource_params = self._prep_params(*params) + expected_rep = re.CompressedResourceOp(re.ResourceMultiControlledX, op_resource_params) + assert op.resource_rep(**op.resource_params()) == expected_rep + + @pytest.mark.parametrize("op, params", zip(res_ops, res_params)) + def test_resource_params(self, op, params): + """Test that the resource_params are produced as expected.""" + expected_params = self._prep_params(*params) + assert op.resource_params() == expected_params + + +class TestResourceCRX: + """Test the ResourceCRX operation""" + + op = re.ResourceCRX(phi=1.23, wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceRZ.resource_rep(): 2, + re.ResourceHadamard.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCRX, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCRY: + """Test the ResourceCRY operation""" + + op = re.ResourceCRY(phi=1.23, wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceRY.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCRY, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCRZ: + """Test the ResourceCRZ operation""" + + op = re.ResourceCRZ(phi=1.23, wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + + expected_resources = { + re.ResourceRZ.resource_rep(): 2, + re.ResourceCNOT.resource_rep(): 2, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCRZ, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceCRot: + """Test the ResourceCRot operation""" + + op = re.ResourceCRot(0.1, 0.2, 0.3, wires=[0, 1]) + + def test_resources(self): + """Test that the resources method produces the expected resources.""" + expected_resources = { + re.ResourceRY.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 3, + re.ResourceCNOT.resource_rep(): 2, + } + assert self.op.resources(**self.op.resource_params()) == expected_resources + + def test_resource_rep(self): + """Test the resource_rep produces the correct compressed representation.""" + expected_rep = re.CompressedResourceOp(re.ResourceCRot, {}) + assert self.op.resource_rep(**self.op.resource_params()) == expected_rep + + def test_resource_params(self): + """Test that the resource_params are produced as expected.""" + expected_params = dict() + assert self.op.resource_params() == expected_params + + +class TestResourceControlledPhaseShift: """Test ResourceControlledPhaseShift""" params = [(1.2, [0, 1]), (2.4, [2, 3])] @@ -37,7 +402,7 @@ def test_resources(self, phi, wires): re.CompressedResourceOp(re.ResourceRZ, {}): 3, } - assert op.resources() == expected + assert op.resources(**op.resource_params()) == expected @pytest.mark.parametrize("phi, wires", params) def test_resource_params(self, phi, wires): @@ -80,24 +445,3 @@ def test_resources_from_rep(self, phi, wires): op_compressed_rep_type = op_compressed_rep.op_type assert op_compressed_rep_type.resources(**op_resource_params) == expected - - -class TestCNOT: - """Test ResourceCNOT""" - - def test_resources(self): - """Test that the resources method is not implemented""" - op = re.ResourceCNOT([0, 1]) - with pytest.raises(re.ResourcesNotDefined): - op.resources() - - def test_resource_rep(self): - """Test the compressed representation""" - op = re.ResourceCNOT([0, 1]) - expected = re.CompressedResourceOp(re.ResourceCNOT, {}) - assert op.resource_rep() == expected - - def test_resource_params(self): - """Test that the resource params are correct""" - op = re.ResourceCNOT([0, 1]) - assert op.resource_params() == {} From 5e787177358f3b5c4d10c85a252535ecdf77331b Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 19:25:40 -0500 Subject: [PATCH 286/335] changelog --- doc/releases/changelog-dev.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 328c5401e73..0df04c08877 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -184,6 +184,9 @@ * Added `ResourceOperator` classes for QFT and all operators in QFT's decomposition. [(#6447)](https://github.com/PennyLaneAI/pennylane/pull/6447) +* Added native `ResourceOperator` subclasses for each of the controlled operators. + [(#6579)](https://github.com/PennyLaneAI/pennylane/pull/6579) +

Breaking changes 💔

* `qml.fourier.qnode_spectrum` no longer automatically converts pure numpy parameters to the From 29b9cb25d9f1c5efa77d9829f6ddcc0727e4056a Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 19:31:23 -0500 Subject: [PATCH 287/335] Update pennylane/ops/op_math/controlled_ops.py --- pennylane/ops/op_math/controlled_ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/ops/op_math/controlled_ops.py b/pennylane/ops/op_math/controlled_ops.py index 4d9d2c304fb..9dcebb6e231 100644 --- a/pennylane/ops/op_math/controlled_ops.py +++ b/pennylane/ops/op_math/controlled_ops.py @@ -1381,7 +1381,6 @@ def compute_decomposition(phi, wires): # pylint: disable=arguments-differ """ pi_half = qml.math.ones_like(phi) * (np.pi / 2) - return [ qml.RZ(pi_half, wires=wires[1]), qml.RY(phi / 2, wires=wires[1]), From ca9df5017bb602cc6cb237bf6d64030df63c00ca Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 29 Nov 2024 19:36:42 -0500 Subject: [PATCH 288/335] format --- .../ops/op_math/controlled_ops.py | 5 ++++- .../ops/op_math/test_controlled_ops.py | 22 +++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index a2f8d749010..1a3fc7b89a2 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -315,7 +315,10 @@ class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): @staticmethod def _resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + **kwargs, # pylint: disable=unused-argument ) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index be307582389..7465b945f26 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -43,7 +43,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -68,7 +68,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -93,7 +93,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -117,7 +117,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -141,7 +141,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -162,7 +162,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -190,7 +190,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -307,7 +307,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -332,7 +332,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -357,7 +357,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params @@ -382,7 +382,7 @@ def test_resource_rep(self): def test_resource_params(self): """Test that the resource_params are produced as expected.""" - expected_params = dict() + expected_params = {} assert self.op.resource_params() == expected_params From 83f1dff919e7735624f2c895bf7494471200b9e3 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 2 Dec 2024 09:59:16 -0500 Subject: [PATCH 289/335] Apply suggestions from code review --- .../resource_estimation/ops/op_math/controlled_ops.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 1a3fc7b89a2..ea936d4e9b9 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -81,7 +81,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: s = re.ResourceS.resource_rep() gate_types[cnot] = 1 - gate_types[s] = 1 + 3 # S^dagg = 3*S in cost + gate_types[s] = 1 + 3 # S^dagg = 3*S in cost TODO: Update with Adjoint(S) return gate_types @@ -250,7 +250,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types[h] = 3 gate_types[s] = 1 gate_types[cz] = 1 - gate_types[t] = 2 + 2 * (7) # T^dagg = 7*T in cost + gate_types[t] = 2 + 2 * (7) # T^dagg = 7*T in cost TODO: Update with Adjoint(T) return gate_types @@ -321,17 +321,17 @@ def _resource_decomp( **kwargs, # pylint: disable=unused-argument ) -> Dict[re.CompressedResourceOp, int]: gate_types = {} - cnot = re.ResourceCNOT.resource_rep() - toffoli = re.ResourceToffoli.resource_rep() if num_ctrl_values: x = re.ResourceX.resource_rep() gate_types[x] = num_ctrl_values * 2 + cnot = re.ResourceCNOT.resource_rep() if num_ctrl_wires == 1: gate_types[cnot] = 1 return gate_types + toffoli = re.ResourceToffoli.resource_rep() if num_ctrl_wires == 2: gate_types[toffoli] = 1 return gate_types From ce84834438ffb6abd5b5917c7e92fd3cdeb59ab1 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 2 Dec 2024 11:41:00 -0500 Subject: [PATCH 290/335] Format --- .../ops/op_math/controlled_ops.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index ea936d4e9b9..10c1d81df22 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -30,11 +30,10 @@ class ResourceCH(qml.CH, re.ResourceOperator): .. math:: \begin{align} - \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \dot \hat{Z} \dot \hat{R}_{y}(\frac{-\pi}{4}), \\ + \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \dot \hat{Z} \dot \hat{R}_{y}(\frac{-\pi}{4}) \\ \hat{Z} &= \hat{H} \dot \hat{X} \dot \hat{H} \end{align} - We can control on the Pauli-X gate to obtain our controlled Hadamard gate. """ @@ -67,7 +66,7 @@ class ResourceCY(qml.CY, re.ResourceOperator): Resources: The resources are derived from the following identity: - .. math:: \hat{Y} = \hat{S} \dot \hat{X} \dot \hat{S}^{\dagger}. + .. math:: \hat{Y} = \hat{S} \dot \hat{X} \dot \hat{S}^{\dagger} We can control on the Pauli-X gate to obtain our controlled-Y gate. @@ -99,7 +98,7 @@ class ResourceCZ(qml.CZ, re.ResourceOperator): Resources: The resources are derived from the following identity: - .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H}. + .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H} We can control on the Pauli-X gate to obtain our controlled-Z gate. @@ -170,7 +169,7 @@ class ResourceCCZ(qml.CCZ, re.ResourceOperator): Resources: The resources are derived from the following identity: - .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H}. + .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H} We replace the Pauli-X gate with a Toffoli gate to obtain our control-control-Z gate. """ @@ -380,7 +379,7 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): .. math:: \begin{align} - \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}, \\ + \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X} \\ \hat{X} &= \hat{H} \dot \hat{Z} \dot \hat{H} \end{align} @@ -417,7 +416,7 @@ class ResourceCRY(qml.CRY, re.ResourceOperator): The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. The resources are derived with the following identity: - .. math:: \hat{RY}(- \theta) = \hat{X} \dot \hat{RY}(\theta) \dot \hat{X}. + .. math:: \hat{RY}(- \theta) = \hat{X} \dot \hat{RY}(\theta) \dot \hat{X} """ @@ -448,7 +447,7 @@ class ResourceCRZ(qml.CRZ, re.ResourceOperator): The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. The resources are derived from the following identity: - .. math:: \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}. + .. math:: \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X} """ From 725f8b888527670ecad079dfad8416a3b92f906a Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 11:59:08 -0500 Subject: [PATCH 291/335] fix tests --- .../resource_estimation/ops/op_math/controlled_ops.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 75a67df89bc..93e2975f603 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -556,13 +556,13 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} + h = re.ResourceHadamard.resource_rep() rz = re.ResourceRZ.resource_rep() - ry = re.ResourceRY.resource_rep() cnot = re.ResourceCNOT.resource_rep() gate_types[cnot] = 2 gate_types[rz] = 2 - gate_types[ry] = 2 + gate_types[h] = 2 return gate_types @@ -657,10 +657,10 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: gate_types = {} cnot = re.ResourceCNOT.resource_rep() - phase = re.ResourcePhaseShift.resource_rep() + rz = re.ResourceRZ.resource_rep() gate_types[cnot] = 2 - gate_types[phase] = 2 + gate_types[rz] = 2 return gate_types From a0e73f97cc58e92bda226f91c4605127942ecf67 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 12:05:51 -0500 Subject: [PATCH 292/335] fix test --- .../resource_estimation/ops/op_math/test_controlled_ops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 998da4eadf6..f6efa3f5b70 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -56,8 +56,9 @@ def test_resources(self): """Test that the resources method produces the expected resources.""" expected_resources = { - re.ResourceS.resource_rep(): 4, + re.ResourceS.resource_rep(): 1, re.ResourceCNOT.resource_rep(): 1, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1, } assert self.op.resources(**self.op.resource_params()) == expected_resources From c181add30259a8a5bd6e229eb9e95d9c5196f698 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 2 Dec 2024 12:16:53 -0500 Subject: [PATCH 293/335] update docstring and foramt --- .../ops/op_math/controlled_ops.py | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 10c1d81df22..29f2df8730d 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -30,8 +30,8 @@ class ResourceCH(qml.CH, re.ResourceOperator): .. math:: \begin{align} - \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \dot \hat{Z} \dot \hat{R}_{y}(\frac{-\pi}{4}) \\ - \hat{Z} &= \hat{H} \dot \hat{X} \dot \hat{H} + \hat{H} &= \hat{R}_{y}(\frac{\pi}{4}) \cdot \hat{Z} \cdot \hat{R}_{y}(\frac{-\pi}{4}), \\ + \hat{Z} &= \hat{H} \cdot \hat{X} \cdot \hat{H}. \end{align} We can control on the Pauli-X gate to obtain our controlled Hadamard gate. @@ -66,7 +66,7 @@ class ResourceCY(qml.CY, re.ResourceOperator): Resources: The resources are derived from the following identity: - .. math:: \hat{Y} = \hat{S} \dot \hat{X} \dot \hat{S}^{\dagger} + .. math:: \hat{Y} = \hat{S} \cdot \hat{X} \cdot \hat{S}^{\dagger}. We can control on the Pauli-X gate to obtain our controlled-Y gate. @@ -98,7 +98,7 @@ class ResourceCZ(qml.CZ, re.ResourceOperator): Resources: The resources are derived from the following identity: - .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H} + .. math:: \hat{Z} = \hat{H} \cdot \hat{X} \cdot \hat{H}. We can control on the Pauli-X gate to obtain our controlled-Z gate. @@ -169,7 +169,7 @@ class ResourceCCZ(qml.CCZ, re.ResourceOperator): Resources: The resources are derived from the following identity: - .. math:: \hat{Z} = \hat{H} \dot \hat{X} \dot \hat{H} + .. math:: \hat{Z} = \hat{H} \cdot \hat{X} \cdot \hat{H}. We replace the Pauli-X gate with a Toffoli gate to obtain our control-control-Z gate. """ @@ -374,17 +374,16 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): Resources: - The resources are derived from the following identities: + The resources are based from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_ in combination with the following identity: .. math:: - \begin{align} - \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X} \\ - \hat{X} &= \hat{H} \dot \hat{Z} \dot \hat{H} - \end{align} + \hat{RX} &= \hat{H} \cdot \hat{RZ} \cdot \hat{H}. - The expression for controlled-RZ gates is used as defined in (figure 1b.) the paper - `T-count and T-depth of any multi-qubit unitary `_. + Using the identity above, we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated + by :code:`Hadamard` gates. The expression for controlled-RZ gates is used as defined in the + reference above. """ @staticmethod @@ -413,10 +412,14 @@ class ResourceCRY(qml.CRY, re.ResourceOperator): r"""Resource class for CRY Resources: - The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + The resources are based from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. The resources are derived with the following identity: - .. math:: \hat{RY}(- \theta) = \hat{X} \dot \hat{RY}(\theta) \dot \hat{X} + .. math:: \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}. + + By replacing the :code:`X` gates with :code:`CNOT` gates, we obtain a controlled-version of this + identity. Thus we are able to constructively or destructively interfere the gates based on the value + of the control qubit. """ @@ -445,9 +448,13 @@ class ResourceCRZ(qml.CRZ, re.ResourceOperator): Resources: The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit - unitary `_. The resources are derived from the following identity: + unitary `_. They are derived from the following identity: + + .. math:: \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}. - .. math:: \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X} + By replacing the :code:`X` gates with :code:`CNOT` gates, we obtain a controlled-version of this + identity. Thus we are able to constructively or destructively interfere the gates based on the value + of the control qubit. """ @@ -481,8 +488,8 @@ class ResourceCRot(qml.CRot, re.ResourceOperator): .. math:: \begin{align} - \hat{RZ}(- \theta) = \hat{X} \dot \hat{RZ}(\theta) \dot \hat{X}, \\ - \hat{RY}(- \theta) = \hat{X} \dot \hat{RY}(\theta) \dot \hat{X}. + \hat{RZ}(- \theta) = \hat{X} \cdot \hat{RZ}(\theta) \cdot \hat{X}, \\ + \hat{RY}(- \theta) = \hat{X} \cdot \hat{RY}(\theta) \cdot \hat{X}. \end{align} This identity is applied along with some clever choices for the angle values to combine rotation; From 5d79d3b0e354330a8c0c1b832295767f5191ee28 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 2 Dec 2024 12:18:15 -0500 Subject: [PATCH 294/335] Apply suggestions from code review --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 29f2df8730d..b5c880b8abe 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -340,7 +340,7 @@ def _resource_decomp( gate_types[toffoli] = 1 return gate_types - gate_types[cnot] = 36 * num_ctrl_wires - 111 # Barenco 1995 + gate_types[cnot] = 36 * num_ctrl_wires - 111 return gate_types def resource_params(self) -> dict: From 27adf314dc0349192b840eb3e0fad0a2969b5557 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 2 Dec 2024 12:44:25 -0500 Subject: [PATCH 295/335] fix docs --- .../resource_estimation/ops/op_math/controlled_ops.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index b5c880b8abe..9aa35d26202 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -373,17 +373,14 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): r"""Resource class for CRX Resources: - The resources are based from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit - unitary `_ in combination with the following identity: + unitary `_. In combination with the following identity: - .. math:: + .. math:: \hat{RX} &= \hat{H} \cdot \hat{RZ} \cdot \hat{H}, - \hat{RX} &= \hat{H} \cdot \hat{RZ} \cdot \hat{H}. + we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated by :code:`Hadamard` gates. + The expression for controlled-RZ gates is used as defined in the reference above. - Using the identity above, we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated - by :code:`Hadamard` gates. The expression for controlled-RZ gates is used as defined in the - reference above. """ @staticmethod From 1f0e85beb6ed9ce6926ee0bd1eebfb5caa1697ec Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 2 Dec 2024 13:15:20 -0500 Subject: [PATCH 296/335] fix formatting --- .../resource_estimation/ops/op_math/controlled_ops.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 9aa35d26202..f526257dcfb 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -376,7 +376,7 @@ class ResourceCRX(qml.CRX, re.ResourceOperator): The resources are based from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. In combination with the following identity: - .. math:: \hat{RX} &= \hat{H} \cdot \hat{RZ} \cdot \hat{H}, + .. math:: \hat{RX} = \hat{H} \cdot \hat{RZ} \cdot \hat{H}, we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated by :code:`Hadamard` gates. The expression for controlled-RZ gates is used as defined in the reference above. @@ -485,13 +485,13 @@ class ResourceCRot(qml.CRot, re.ResourceOperator): .. math:: \begin{align} - \hat{RZ}(- \theta) = \hat{X} \cdot \hat{RZ}(\theta) \cdot \hat{X}, \\ - \hat{RY}(- \theta) = \hat{X} \cdot \hat{RY}(\theta) \cdot \hat{X}. + \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}, \\ + \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}. \end{align} This identity is applied along with some clever choices for the angle values to combine rotation; the final circuit takes the form: - + .. code-block:: bash ctrl: ─────╭●─────────╭●─────────┤ From 5f0df9521288e2dff48a44818424e6676bab5478 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 14:06:57 -0500 Subject: [PATCH 297/335] some updates --- .../ops/op_math/controlled_ops.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 93e2975f603..a5f80b9bfe6 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -70,7 +70,7 @@ def controlled_resource_decomp( ) -> Dict[re.CompressedResourceOp, int]: return { re.ResourceControlled.resource_rep( - re.ResourceHadamard, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + re.ResourceHadamard, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires ): 1 } @@ -122,7 +122,7 @@ def controlled_resource_decomp( ) -> Dict[re.CompressedResourceOp, int]: return { re.ResourceControlled.resource_rep( - re.ResourceY, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + re.ResourceY, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires ): 1 } @@ -170,12 +170,12 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: return {re.ResourceCCZ.resource_rep(): 1} return { re.ResourceControlled.resource_rep( - re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + re.ResourceZ, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires ): 1 } @@ -232,7 +232,7 @@ def controlled_resource_decomp( ) -> Dict[re.CompressedResourceOp, int]: return { re.ResourceControlled.resource_rep( - re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + re.ResourceSWAP, {}, num_ctrl_wires + 1, num_ctrl_values, num_work_wires ): 1 } @@ -281,7 +281,7 @@ def controlled_resource_decomp( ) -> Dict[re.CompressedResourceOp, int]: return { re.ResourceControlled.resource_rep( - re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + re.ResourceZ, {}, num_ctrl_wires + 2, num_ctrl_values, num_work_wires ): 1 } @@ -317,12 +317,12 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( cls, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 1: + if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: return {re.ResourceToffoli.resource_rep(): 1} return { re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires + 1, num_ctrl_values + 1, num_work_wires + num_ctrl_wires + 1, num_ctrl_values, num_work_wires ): 1 } From 922a89e85375d812023b2b8201289fe078145810 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 15:04:29 -0500 Subject: [PATCH 298/335] __repr__ returns tracking name --- pennylane/labs/resource_estimation/resource_container.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index e834bee2a93..f4fbc684dfc 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -71,10 +71,7 @@ def __eq__(self, other: object) -> bool: return (self.op_type == other.op_type) and (self.params == other.params) def __repr__(self) -> str: - op_type_str = self._name + "(" - params_str = ", ".join([f"{key}={self.params[key]}" for key in self.params]) + ")" - - return op_type_str + params_str + return self._name @dataclass From 1766820c385dd5f185576f573a0614f592bba27e Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 15:13:59 -0500 Subject: [PATCH 299/335] update tests --- .../tests/resource_estimation/test_resource_container.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index 6b62a5571d8..55e5c6a6882 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -67,10 +67,10 @@ class TestCompressedResourceOp: ) compressed_op_reprs = ( - "DummyX(num_wires=1)", - "DummyQFT(num_wires=5)", - "DummyQSVT(num_wires=3, num_angles=5)", - "DummyTrotterProduct(Hamiltonian=X(0) + -1 * Y(1) + 0.5 * (Z(0) @ Z(1)), num_steps=5, order=2)", + "DummyX", + "DummyQFT", + "DummyQSVT", + "DummyTrotterProduct", ) @pytest.mark.parametrize("name, op_type, parameters", compressed_ops_and_params_lst) From 7684deb93a19663a078aa31b558fdc7a445efaec Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:03:04 -0500 Subject: [PATCH 300/335] remove TODOs --- .../labs/resource_estimation/__init__.py | 7 +- .../labs/resource_estimation/ops/__init__.py | 3 - .../ops/qubit/qchem_ops.py | 79 ------------------- 3 files changed, 1 insertion(+), 88 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index d278963ea56..fb0be519b9c 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -62,8 +62,6 @@ ~ResourceCY ~ResourceCZ ~ResourceDoubleExcitation - ~ResourceDoubleExcitationMinus - ~ResourceDoubleExcitationPlus ~ResourceFermionicSWAP ~ResourceGlobalPhase ~ResourceHadamard @@ -75,6 +73,7 @@ ~ResourceMultiRZ ~ResourceOrbitalRotation ~ResourceMultiControlledX + ~ResourcePauliRot ~ResourcePhaseShift ~ResourcePSWAP ~ResourceRot @@ -82,7 +81,6 @@ ~ResourceRY ~ResourceRZ ~ResourceS - ~ResourceSingleExcitation ~ResourceSingleExcitationMinus ~ResourceSingleExcitationPlus ~ResourceSWAP @@ -143,8 +141,6 @@ ResourceCY, ResourceCZ, ResourceDoubleExcitation, - ResourceDoubleExcitationMinus, - ResourceDoubleExcitationPlus, ResourceFermionicSWAP, ResourceGlobalPhase, ResourceHadamard, @@ -164,7 +160,6 @@ ResourceRY, ResourceRZ, ResourceS, - ResourceSingleExcitation, ResourceSingleExcitationMinus, ResourceSingleExcitationPlus, ResourceSWAP, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index 166b87973a6..c107dcb409d 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -20,8 +20,6 @@ from .qubit import ( ResourceDoubleExcitation, - ResourceDoubleExcitationMinus, - ResourceDoubleExcitationPlus, ResourceFermionicSWAP, ResourceHadamard, ResourceIsingXX, @@ -38,7 +36,6 @@ ResourceRY, ResourceRZ, ResourceS, - ResourceSingleExcitation, ResourceSingleExcitationMinus, ResourceSingleExcitationPlus, ResourceSWAP, diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 47a29dc5e0a..8d8c24adce9 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -16,34 +16,6 @@ # pylint: disable=arguments-differ -class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): - r"""Resource class for the SingleExcitation gate. - - Resources: - The resources are obtained by decomposing the following matrix into fundamental gates. - - .. math:: U(\phi) = \begin{bmatrix} - 1 & 0 & 0 & 0 \\ - 0 & \cos(\phi/2) & -\sin(\phi/2) & 0 \\ - 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ - 0 & 0 & 0 & 1 - \end{bmatrix}. - - """ - - @staticmethod - def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_ops branch""" - raise re.ResourcesNotDefined - - def resource_params(self): - return {} - - @classmethod - def resource_rep(cls, **kwargs): - return re.CompressedResourceOp(cls, {}) - - class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperator): r"""Resource class for the SingleExcitationMinus gate. @@ -152,57 +124,6 @@ def resource_rep(cls, **kwargs): return re.CompressedResourceOp(cls, {}) -class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator): - r"""Resource class for the DoubleExcitationMinus gate. - - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - - &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ - &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ - &|x\rangle \rightarrow e^{-i\phi/2} |x\rangle, - """ - - @staticmethod - def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" - - def resource_params(self): - return {} - - @classmethod - def resource_rep(cls, **kwargs): - return re.CompressedResourceOp(cls, {}) - - -class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator): - r"""Resource class for the DoubleExcitationPlus gate. - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - - &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle - \sin(\phi/2) |1100\rangle\\ - &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle + \sin(\phi/2) |0011\rangle\\ - &|x\rangle \rightarrow e^{i\phi/2} |x\rangle, - """ - - @staticmethod - def _resource_decomp(*args, **kwargs): - """TODO: implement in resource_symbolic_op branch""" - raise re.ResourcesNotDefined - - def resource_params(self): - return {} - - @classmethod - def resource_rep(cls, **kwargs): - return re.CompressedResourceOp(cls, {}) - class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): r"""Resource class for the OrbitalRotation gate. From 79ccec6c6bb356b19024b1c4c42bb328b1de552e Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:19:36 -0500 Subject: [PATCH 301/335] remove tests --- .../ops/qubit/test_qchem_ops.py | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index c3c11a014af..c8c6cc70156 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -18,22 +18,6 @@ # pylint: disable=use-implicit-booleaness-not-comparison,no-self-use -# TODO: implement in resource_symbolic_ops branch -class TestSingleExcitation: - """Tests for the ResourceSingleExcitation class.""" - - def test_resources(self): - """Test that the resources are correct.""" - - def test_resource_params(self): - """Test that the resource params are correct.""" - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" - class TestSingleExcitationMinus: """Tests for the ResourceSingleExcitationMinus class.""" @@ -147,38 +131,6 @@ def test_resources_from_rep(self): assert op_resource_type.resources(**op_resource_params) == expected -# TODO: add tests in resource_symbolic_ops branch -class TestDoubleExcitationMinus: - """Tests for the ResourceDoubleExcitationMinus class.""" - - def test_resources(self): - """Test that the resources are correct.""" - - def test_resource_params(self): - """Test that the resource params are correct.""" - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" - - -# TODO: add tests in resource_symbolic_ops branch -class TestDoubleExcitationPlus: - """Tests for the ResourceDoubleExcitationPlus class.""" - - def test_resources(self): - """Test that the resources are correct.""" - - def test_resource_params(self): - """Test that the resource params are correct.""" - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" class TestOribatlRotation: From 19798e9a1d002abb018f31e564122e9684d69b65 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:27:26 -0500 Subject: [PATCH 302/335] remove orbital --- .../ops/qubit/qchem_ops.py | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 8d8c24adce9..580f4980c28 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -125,34 +125,6 @@ def resource_rep(cls, **kwargs): -class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): - r"""Resource class for the OrbitalRotation gate. - - Resources: - The resources are obtained by decomposing the following mapping into fundamental gates. - - .. math:: - &|\Phi_{0}\rangle = \cos(\phi/2)|\Phi_{0}\rangle - \sin(\phi/2)|\Phi_{1}\rangle\\ - &|\Phi_{1}\rangle = \cos(\phi/2)|\Phi_{0}\rangle + \sin(\phi/2)|\Phi_{1}\rangle, - """ - - @staticmethod - def _resource_decomp(**kwargs): - fermionic_swap = re.ResourceFermionicSWAP.resource_rep(**kwargs) - single_excitation = re.ResourceSingleExcitation.resource_rep(**kwargs) - - gate_types = {} - gate_types[fermionic_swap] = 2 - gate_types[single_excitation] = 2 - - return gate_types - - def resource_params(self): - return {} - - @classmethod - def resource_rep(cls, **kwargs): - return re.CompressedResourceOp(cls, {}) class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): From 226b70e62f13ef26a02e9fd7fcdf2127442bffd0 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:39:48 -0500 Subject: [PATCH 303/335] fix tests after merge --- .../ops/qubit/qchem_ops.py | 27 +++++++++++++++++++ .../ops/op_math/test_controlled_ops.py | 3 ++- .../ops/qubit/test_qchem_ops.py | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index f34bab9de85..7b226500fb9 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -242,7 +242,34 @@ def resource_params(self): def resource_rep(cls, **kwargs): return re.CompressedResourceOp(cls, {}) +class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): + r"""Resource class for the OrbitalRotation gate. + Resources: + The resources are obtained by decomposing the following mapping into fundamental gates. + + .. math:: + &|\Phi_{0}\rangle = \cos(\phi/2)|\Phi_{0}\rangle - \sin(\phi/2)|\Phi_{1}\rangle\\ + &|\Phi_{1}\rangle = \cos(\phi/2)|\Phi_{0}\rangle + \sin(\phi/2)|\Phi_{1}\rangle, + """ + + @staticmethod + def _resource_decomp(**kwargs): + fermionic_swap = re.ResourceFermionicSWAP.resource_rep(**kwargs) + single_excitation = re.ResourceSingleExcitation.resource_rep(**kwargs) + + gate_types = {} + gate_types[fermionic_swap] = 2 + gate_types[single_excitation] = 2 + + return gate_types + + def resource_params(self): + return {} + + @classmethod + def resource_rep(cls, **kwargs): + return re.CompressedResourceOp(cls, {}) class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index b0d5c947d32..7ca553f0ace 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -177,7 +177,8 @@ def test_resources(self): expected_resources = { re.ResourceS.resource_rep(): 1, - re.ResourceT.resource_rep(): 16, + re.ResourceT.resource_rep(): 2, + re.ResourceAdjoint.resource_rep(re.ResourceT, {}): 2, re.ResourceCZ.resource_rep(): 1, re.ResourceCNOT.resource_rep(): 9, re.ResourceHadamard.resource_rep(): 3, diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index 34738d71f8e..ab37980e8d5 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -251,7 +251,7 @@ def test_resources_from_rep(self): assert op_resource_type.resources(**op_resource_params) == expected -class TestOribatlRotation: +class TestOrbitalRotation: """Tests for the ResourceOrbitalRotation class.""" def test_resources(self): From 66e63841812926a403d0fbd5ccbd7109c55f0e80 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:45:51 -0500 Subject: [PATCH 304/335] fix another test --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 00cdb8c0c32..31f53fd8680 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -360,7 +360,7 @@ def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: h = re.ResourceHadamard.resource_rep() s = re.ResourceS.resource_rep() cz = re.ResourceCZ.resource_rep() - t_dag = re.ResourceAdjoint(re.ResourceT, {}) + t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) gate_types[cnot] = 9 gate_types[h] = 3 From 81286ed0a21b827b570b2c7885db0ec3044cc8c3 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:46:57 -0500 Subject: [PATCH 305/335] remove orbital --- pennylane/labs/resource_estimation/__init__.py | 2 -- pennylane/labs/resource_estimation/ops/__init__.py | 1 - 2 files changed, 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/__init__.py b/pennylane/labs/resource_estimation/__init__.py index fb0be519b9c..d1e5f235466 100644 --- a/pennylane/labs/resource_estimation/__init__.py +++ b/pennylane/labs/resource_estimation/__init__.py @@ -71,7 +71,6 @@ ~ResourceIsingYY ~ResourceIsingZZ ~ResourceMultiRZ - ~ResourceOrbitalRotation ~ResourceMultiControlledX ~ResourcePauliRot ~ResourcePhaseShift @@ -151,7 +150,6 @@ ResourceIsingZZ, ResourceMultiControlledX, ResourceMultiRZ, - ResourceOrbitalRotation, ResourcePauliRot, ResourcePSWAP, ResourcePhaseShift, diff --git a/pennylane/labs/resource_estimation/ops/__init__.py b/pennylane/labs/resource_estimation/ops/__init__.py index c107dcb409d..14fd1c89402 100644 --- a/pennylane/labs/resource_estimation/ops/__init__.py +++ b/pennylane/labs/resource_estimation/ops/__init__.py @@ -27,7 +27,6 @@ ResourceIsingYY, ResourceIsingZZ, ResourceMultiRZ, - ResourceOrbitalRotation, ResourcePauliRot, ResourcePhaseShift, ResourcePSWAP, From 364b5cc5158f2f2ce5bc19ac6839e03a1bcefc54 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 16:47:53 -0500 Subject: [PATCH 306/335] remove orbit test --- .../ops/qubit/test_qchem_ops.py | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index c3c11a014af..0e9c05b5806 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -181,39 +181,6 @@ def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" -class TestOribatlRotation: - """Tests for the ResourceOrbitalRotation class.""" - - def test_resources(self): - """Test that the resources are correct.""" - expected = { - re.ResourceFermionicSWAP.resource_rep(): 2, - re.ResourceSingleExcitation.resource_rep(): 2, - } - assert re.ResourceOrbitalRotation.resources() == expected - - def test_resource_params(self): - """Test that the resource params are correct.""" - op = re.ResourceOrbitalRotation(0.5, wires=[0, 1, 3, 4]) - assert op.resource_params() == {} - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - expected = re.CompressedResourceOp(re.ResourceOrbitalRotation, {}) - assert re.ResourceOrbitalRotation.resource_rep() == expected - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" - op = re.ResourceOrbitalRotation(0.5, wires=[0, 1, 3, 4]) - expected = { - re.ResourceFermionicSWAP.resource_rep(): 2, - re.ResourceSingleExcitation.resource_rep(): 2, - } - op_compressed_rep = op.resource_rep_from_op() - op_resource_type = op_compressed_rep.op_type - op_resource_params = op_compressed_rep.params - assert op_resource_type.resources(**op_resource_params) == expected - class TestFermionicSWAP: """Tests for the ResourceFermionicSWAP class.""" From 76b9fce1b310956a3271a25d5e98e20088099d66 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 2 Dec 2024 17:15:54 -0500 Subject: [PATCH 307/335] review changes --- .../ops/qubit/parametric_ops_multi_qubit.py | 64 ++++++++++--------- .../ops/qubit/qchem_ops.py | 6 +- .../qubit/test_parametric_ops_multi_qubit.py | 3 +- .../ops/qubit/test_qchem_ops.py | 1 - 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 49ebae4e829..02a8231f92f 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -22,9 +22,9 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): r"""Resource class for the MultiRZ gate. Resources: - .. math:: - - MultiRZ(\theta) = \exp\left(-i \frac{\theta}{2} Z^{\otimes n}\right) + The resources come from Section VIII of "The Bravyi-Kitaev transformation for quantum computation + of electronic structure" (https://arxiv.org/pdf/1208.5986). See Figure 3 of that section for + an illustration. """ @staticmethod @@ -42,7 +42,7 @@ def resource_params(self): return {"num_wires": len(self.wires)} @classmethod - def resource_rep(cls, num_wires, **kwargs): + def resource_rep(cls, num_wires): return re.CompressedResourceOp(cls, {"num_wires": num_wires}) @@ -50,22 +50,23 @@ class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): r"""Resource class for the PauliRot gate. Resources: - .. math:: - - RP(\theta, P) = \exp\left(-i \frac{\theta}{2} P\right) + The resources come from Section VIII of "The Bravyi-Kitaev transformation for quantum computation + of electronic structure" (https://arxiv.org/pdf/1208.5986). See Figure 4 of that section for + an illustration. """ @staticmethod def _resource_decomp(pauli_word, **kwargs): if set(pauli_word) == {"I"}: - gp = re.ResourceGlobalPhase.resource_rep(**kwargs) + gp = re.ResourceGlobalPhase.resource_rep() return {gp: 1} active_wires = len(pauli_word.replace("I", "")) - h = re.ResourceHadamard.resource_rep(**kwargs) - rx = re.ResourceRX.resource_rep(**kwargs) - multi_rz = re.ResourceMultiRZ.resource_rep(active_wires, **kwargs) + h = re.ResourceHadamard.resource_rep() + rx = re.ResourceRX.resource_rep() + rz = re.ResourceRZ.resource_rep() + cnot = re.ResourceCNOT.resource_rep() h_count = 0 rx_count = 0 @@ -79,7 +80,8 @@ def _resource_decomp(pauli_word, **kwargs): gate_types = {} gate_types[h] = h_count gate_types[rx] = rx_count - gate_types[multi_rz] = 1 + gate_types[rz] = 1 + gate_types[cnot] = 2 * (active_wires - 1) return gate_types @@ -89,7 +91,7 @@ def resource_params(self): } @classmethod - def resource_rep(cls, pauli_word, **kwargs): + def resource_rep(cls, pauli_word): return re.CompressedResourceOp(cls, {"pauli_word": pauli_word}) @@ -110,8 +112,8 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - cnot = re.ResourceCNOT.resource_rep(**kwargs) - rx = re.ResourceRX.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep() + rx = re.ResourceRX.resource_rep() gate_types = {} gate_types[cnot] = 2 @@ -123,7 +125,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args, **kwargs): + def resource_rep(cls, *args): return re.CompressedResourceOp(cls, {}) @@ -144,8 +146,8 @@ class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - cy = re.ops.ResourceCY.resource_rep(**kwargs) - ry = re.ops.ResourceRY.resource_rep(**kwargs) + cy = re.ops.ResourceCY.resource_rep() + ry = re.ops.ResourceRY.resource_rep() gate_types = {} gate_types[cy] = 2 @@ -157,7 +159,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args, **kwargs): + def resource_rep(cls, *args): return re.CompressedResourceOp(cls, {}) @@ -178,10 +180,10 @@ class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - h = re.ResourceHadamard.resource_rep(**kwargs) - cy = re.ResourceCY.resource_rep(**kwargs) - ry = re.ResourceRY.resource_rep(**kwargs) - rx = re.ResourceRX.resource_rep(**kwargs) + h = re.ResourceHadamard.resource_rep() + cy = re.ResourceCY.resource_rep() + ry = re.ResourceRY.resource_rep() + rx = re.ResourceRX.resource_rep() gate_types = {} gate_types[h] = 2 @@ -195,7 +197,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args, **kwargs): + def resource_rep(cls, *args): return re.CompressedResourceOp(cls, {}) @@ -216,8 +218,8 @@ class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - cnot = re.ResourceCNOT.resource_rep(**kwargs) - rz = re.ResourceRZ.resource_rep(**kwargs) + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() gate_types = {} gate_types[cnot] = 2 @@ -229,7 +231,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args, **kwargs): + def resource_rep(cls, *args): return re.CompressedResourceOp(cls, {}) @@ -247,9 +249,9 @@ class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): @staticmethod def _resource_decomp(*args, **kwargs): - swap = re.ResourceSWAP.resource_rep(**kwargs) - cnot = re.ResourceCNOT.resource_rep(**kwargs) - phase = re.ResourcePhaseShift.resource_rep(**kwargs) + swap = re.ResourceSWAP.resource_rep() + cnot = re.ResourceCNOT.resource_rep() + phase = re.ResourcePhaseShift.resource_rep() gate_types = {} gate_types[swap] = 1 @@ -262,5 +264,5 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args, **kwargs): + def resource_rep(cls, *args): return re.CompressedResourceOp(cls, {}) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 580f4980c28..5431f098187 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -100,6 +100,9 @@ class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): &|0011\rangle \rightarrow \cos(\phi/2) |0011\rangle + \sin(\phi/2) |1100\rangle\\ &|1100\rangle \rightarrow \cos(\phi/2) |1100\rangle - \sin(\phi/2) |0011\rangle, + + For the source of this decomposition, see page 17 of + `"Local, Expressive, Quantum-Number-Preserving VQE Ansatze for Fermionic Systems" `_ . """ @staticmethod @@ -124,9 +127,6 @@ def resource_rep(cls, **kwargs): return re.CompressedResourceOp(cls, {}) - - - class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): r"""Resource class for the FermionicSWAP gate. diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py index 77c3665c91d..5a62444a70d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py @@ -91,7 +91,8 @@ def test_resources(self, pauli_word, expected_h_count, expected_rx_count): expected = { re.ResourceHadamard.resource_rep(): expected_h_count, re.ResourceRX.resource_rep(): expected_rx_count, - re.ResourceMultiRZ.resource_rep(active_wires): 1, + re.ResourceRZ.resource_rep(): 1, + re.ResourceCNOT.resource_rep(): 2 * (active_wires - 1), } assert re.ResourcePauliRot.resources(pauli_word) == expected diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index 0e9c05b5806..c9da2ed5361 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -181,7 +181,6 @@ def test_resources_from_rep(self): """Test that the resources can be obtained from the compressed representation.""" - class TestFermionicSWAP: """Tests for the ResourceFermionicSWAP class.""" From bbc0a848969d0c190f04fb4df96cde7f4ca09a50 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 08:52:38 -0500 Subject: [PATCH 308/335] adding circuit diagrams to docstrings --- .../ops/qubit/parametric_ops_multi_qubit.py | 35 +++++++++ .../ops/qubit/qchem_ops.py | 77 +++++++++++++------ 2 files changed, 88 insertions(+), 24 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 02a8231f92f..25a4a0c1155 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -108,6 +108,13 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. + + The circuit implementing this transformation is given by + + .. code-block:: bash + + 0: ─╭●──RX(0.10)─╭●─┤ + 1: ─╰X───────────╰X─┤ """ @staticmethod @@ -142,6 +149,13 @@ class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. + + The circuit implementing this transoformation is given by + + .. code-block: bash + + 0: ─╭●──RY(0.10)─╭●─┤ + 1: ─╰Y───────────╰Y─┤ """ @staticmethod @@ -176,6 +190,13 @@ class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): 0 & i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. + + The circuit implementing this gate is given by + + .. code-block:: bash + + 0: ──H─╭●──RY(0.05)──╭●──H─┤ + 1: ────╰Y──RX(-0.05)─╰Y────┤ """ @staticmethod @@ -214,6 +235,13 @@ class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): 0 & 0 & e^{i \phi / 2} & 0 \\ 0 & 0 & 0 & e^{-i \phi / 2} \end{bmatrix}. + + The circuit implmenting this transformation is given by + + .. code-block:: bash + + 0: ─╭●───────────╭●─┤ + 1: ─╰X──RZ(0.10)─╰X─┤ """ @staticmethod @@ -245,6 +273,13 @@ class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): 0 & e^{i \phi} & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. + + The circuit implementing this transformation is given by + + .. code-block:: bash + + 0: ─╭SWAP─╭●───────────╭●─┤ + 1: ─╰SWAP─╰X──Rϕ(0.10)─╰X─┤ """ @staticmethod diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 5431f098187..0b1ec0290db 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -28,14 +28,21 @@ class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperat 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ 0 & 0 & 0 & e^{-i\phi/2} \end{bmatrix}. + + The circuit implementing this transformation is given by + + .. code-block:: bash + + 0: ──X─╭Rϕ(-0.05)──X─╭●─────────╭●─╭RY(0.10)─╭●─┤ + 1: ──X─╰●──────────X─╰Rϕ(-0.05)─╰X─╰●────────╰X─┤ """ @staticmethod - def _resource_decomp(*args, **kwargs): - x = re.ResourceX.resource_rep(**kwargs) - ctrl_phase_shift = re.ResourceControlledPhaseShift.resource_rep(**kwargs) - cnot = re.ResourceCNOT.resource_rep(**kwargs) - cry = re.ResourceCRY.resource_rep(**kwargs) + def _resource_decomp(**kwargs): + x = re.ResourceX.resource_rep() + ctrl_phase_shift = re.ResourceControlledPhaseShift.resource_rep() + cnot = re.ResourceCNOT.resource_rep() + cry = re.ResourceCRY.resource_rep() gate_types = {} gate_types[x] = 4 @@ -49,7 +56,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) @@ -65,14 +72,21 @@ class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator 0 & \sin(\phi/2) & \cos(\phi/2) & 0 \\ 0 & 0 & 0 & e^{i\phi/2} \end{bmatrix}. + + The circuit implmementing this transformation is given by + + .. clode-block:: bash + + 0: ──X─╭Rϕ(0.05)──X─╭●────────╭●─╭RY(0.10)─╭●─┤ + 1: ──X─╰●─────────X─╰Rϕ(0.05)─╰X─╰●────────╰X─┤ """ @staticmethod - def _resource_decomp(*args, **kwargs): - x = re.ResourceX.resource_rep(**kwargs) - ctrl_phase_shift = re.ResourceControlledPhaseShift.resource_rep(**kwargs) - cnot = re.ResourceCNOT.resource_rep(**kwargs) - cry = re.ResourceCRY.resource_rep(**kwargs) + def _resource_decomp(**kwargs): + x = re.ResourceX.resource_rep() + ctrl_phase_shift = re.ResourceControlledPhaseShift.resource_rep() + cnot = re.ResourceCNOT.resource_rep() + cry = re.ResourceCRY.resource_rep() gate_types = {} gate_types[x] = 4 @@ -86,7 +100,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) @@ -103,14 +117,22 @@ class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): For the source of this decomposition, see page 17 of `"Local, Expressive, Quantum-Number-Preserving VQE Ansatze for Fermionic Systems" `_ . + + The circuit implementing this transformation is given by + + .. code-block:: bash + + 0: ────╭●──H─╭●──RY(-0.01)─╭●──RY(-0.01)─────────────────╭X──RY(0.01)────────╭●──RY(0.01)──╭●─╭X──H──╭●────┤ + 1: ────│─────╰X──RY(0.01)──│─────────────╭X──RY(0.01)─╭X─│───RY(-0.01)─╭X────│───RY(-0.01)─╰X─│──────│─────┤ + 2: ─╭●─╰X─╭●───────────────│─────────────│────────────╰●─╰●────────────│─────│────────────────╰●─────╰X─╭●─┤ + 3: ─╰X──H─╰X───────────────╰X──H─────────╰●────────────────────────────╰●──H─╰X──H──────────────────────╰X─┤ """ @staticmethod - def _resource_decomp(*args, **kwargs): - """See https://arxiv.org/abs/2104.05695""" - h = re.ResourceHadamard.resource_rep(**kwargs) - ry = re.ResourceRY.resource_rep(**kwargs) - cnot = re.ResourceCNOT.resource_rep(**kwargs) + def _resource_decomp(**kwargs): + h = re.ResourceHadamard.resource_rep() + ry = re.ResourceRY.resource_rep() + cnot = re.ResourceCNOT.resource_rep() gate_types = {} gate_types[h] = 6 @@ -123,7 +145,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) @@ -139,14 +161,21 @@ class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): 0 & -ie^{i \phi/2} \sin(\phi/2) & e^{i \phi/2} \cos(\phi/2) & 0 \\ 0 & 0 & 0 & e^{i \phi} \end{bmatrix}. + + The circuit implementing this transformation is given by + + .. clode-block:: bash + + 0: ──H─╭MultiRZ(0.05)──H──RX(1.57)─╭MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╭Exp(0.00+0.05j I)─┤ + 1: ──H─╰MultiRZ(0.05)──H──RX(1.57)─╰MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╰Exp(0.00+0.05j I)─┤ """ @staticmethod - def _resource_decomp(*args, **kwargs): - h = re.ResourceHadamard.resource_rep(**kwargs) - multi_rz = re.ResourceMultiRZ.resource_rep(num_wires=2, **kwargs) - rx = re.ResourceRX.resource_rep(**kwargs) - rz = re.ResourceRZ.resource_rep(**kwargs) + def _resource_decomp(**kwargs): + h = re.ResourceHadamard.resource_rep() + multi_rz = re.ResourceMultiRZ.resource_rep(num_wires=2) + rx = re.ResourceRX.resource_rep() + rz = re.ResourceRZ.resource_rep() phase = re.ResourceGlobalPhase.resource_rep() gate_types = {} @@ -162,5 +191,5 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) From 5cb415574a6917fd1ad9bc386fa812cc55565b1a Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:04:42 -0500 Subject: [PATCH 309/335] remove trailing whitespace --- .../ops/qubit/parametric_ops_multi_qubit.py | 20 +++++++++---------- .../ops/qubit/qchem_ops.py | 14 ++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 25a4a0c1155..29cf05d231d 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -113,8 +113,8 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): .. code-block:: bash - 0: ─╭●──RX(0.10)─╭●─┤ - 1: ─╰X───────────╰X─┤ + 0: ─╭●──RX(0.10)─╭●─┤ + 1: ─╰X───────────╰X─┤ """ @staticmethod @@ -154,8 +154,8 @@ class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): .. code-block: bash - 0: ─╭●──RY(0.10)─╭●─┤ - 1: ─╰Y───────────╰Y─┤ + 0: ─╭●──RY(0.10)─╭●─┤ + 1: ─╰Y───────────╰Y─┤ """ @staticmethod @@ -195,8 +195,8 @@ class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): .. code-block:: bash - 0: ──H─╭●──RY(0.05)──╭●──H─┤ - 1: ────╰Y──RX(-0.05)─╰Y────┤ + 0: ──H─╭●──RY(0.05)──╭●──H─┤ + 1: ────╰Y──RX(-0.05)─╰Y────┤ """ @staticmethod @@ -240,8 +240,8 @@ class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): .. code-block:: bash - 0: ─╭●───────────╭●─┤ - 1: ─╰X──RZ(0.10)─╰X─┤ + 0: ─╭●───────────╭●─┤ + 1: ─╰X──RZ(0.10)─╰X─┤ """ @staticmethod @@ -278,8 +278,8 @@ class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): .. code-block:: bash - 0: ─╭SWAP─╭●───────────╭●─┤ - 1: ─╰SWAP─╰X──Rϕ(0.10)─╰X─┤ + 0: ─╭SWAP─╭●───────────╭●─┤ + 1: ─╰SWAP─╰X──Rϕ(0.10)─╰X─┤ """ @staticmethod diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 0b1ec0290db..468518db31d 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -33,8 +33,8 @@ class ResourceSingleExcitationMinus(qml.SingleExcitationMinus, re.ResourceOperat .. code-block:: bash - 0: ──X─╭Rϕ(-0.05)──X─╭●─────────╭●─╭RY(0.10)─╭●─┤ - 1: ──X─╰●──────────X─╰Rϕ(-0.05)─╰X─╰●────────╰X─┤ + 0: ──X─╭Rϕ(-0.05)──X─╭●─────────╭●─╭RY(0.10)─╭●─┤ + 1: ──X─╰●──────────X─╰Rϕ(-0.05)─╰X─╰●────────╰X─┤ """ @staticmethod @@ -77,8 +77,8 @@ class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator .. clode-block:: bash - 0: ──X─╭Rϕ(0.05)──X─╭●────────╭●─╭RY(0.10)─╭●─┤ - 1: ──X─╰●─────────X─╰Rϕ(0.05)─╰X─╰●────────╰X─┤ + 0: ──X─╭Rϕ(0.05)──X─╭●────────╭●─╭RY(0.10)─╭●─┤ + 1: ──X─╰●─────────X─╰Rϕ(0.05)─╰X─╰●────────╰X─┤ """ @staticmethod @@ -124,7 +124,7 @@ class ResourceDoubleExcitation(qml.DoubleExcitation, re.ResourceOperator): 0: ────╭●──H─╭●──RY(-0.01)─╭●──RY(-0.01)─────────────────╭X──RY(0.01)────────╭●──RY(0.01)──╭●─╭X──H──╭●────┤ 1: ────│─────╰X──RY(0.01)──│─────────────╭X──RY(0.01)─╭X─│───RY(-0.01)─╭X────│───RY(-0.01)─╰X─│──────│─────┤ - 2: ─╭●─╰X─╭●───────────────│─────────────│────────────╰●─╰●────────────│─────│────────────────╰●─────╰X─╭●─┤ + 2: ─╭●─╰X─╭●───────────────│─────────────│────────────╰●─╰●────────────│─────│────────────────╰●─────╰X─╭●─┤ 3: ─╰X──H─╰X───────────────╰X──H─────────╰●────────────────────────────╰●──H─╰X──H──────────────────────╰X─┤ """ @@ -166,8 +166,8 @@ class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): .. clode-block:: bash - 0: ──H─╭MultiRZ(0.05)──H──RX(1.57)─╭MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╭Exp(0.00+0.05j I)─┤ - 1: ──H─╰MultiRZ(0.05)──H──RX(1.57)─╰MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╰Exp(0.00+0.05j I)─┤ + 0: ──H─╭MultiRZ(0.05)──H──RX(1.57)─╭MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╭Exp(0.00+0.05j I)─┤ + 1: ──H─╰MultiRZ(0.05)──H──RX(1.57)─╰MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╰Exp(0.00+0.05j I)─┤ """ @staticmethod From 1a1bd4dc84bb35b111c7a58334ac1fade3dabff3 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:21:31 -0500 Subject: [PATCH 310/335] doc fixes --- .../ops/qubit/parametric_ops_multi_qubit.py | 2 +- pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index 29cf05d231d..1b1c93e49b4 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -152,7 +152,7 @@ class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): The circuit implementing this transoformation is given by - .. code-block: bash + .. code-block:: bash 0: ─╭●──RY(0.10)─╭●─┤ 1: ─╰Y───────────╰Y─┤ diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 468518db31d..29f57f43286 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -75,7 +75,7 @@ class ResourceSingleExcitationPlus(qml.SingleExcitationPlus, re.ResourceOperator The circuit implmementing this transformation is given by - .. clode-block:: bash + .. code-block:: bash 0: ──X─╭Rϕ(0.05)──X─╭●────────╭●─╭RY(0.10)─╭●─┤ 1: ──X─╰●─────────X─╰Rϕ(0.05)─╰X─╰●────────╰X─┤ @@ -164,7 +164,7 @@ class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): The circuit implementing this transformation is given by - .. clode-block:: bash + .. code-block:: bash 0: ──H─╭MultiRZ(0.05)──H──RX(1.57)─╭MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╭Exp(0.00+0.05j I)─┤ 1: ──H─╰MultiRZ(0.05)──H──RX(1.57)─╰MultiRZ(0.05)──RX(-1.57)──RZ(0.05)─╰Exp(0.00+0.05j I)─┤ From 5e9af77b6478cede8470a83bd38d8d8189d0dc5a Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:24:26 -0500 Subject: [PATCH 311/335] changelog --- doc/releases/changelog-dev.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 9d04b90cdbf..574cb33747d 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -200,6 +200,9 @@ added `binary_mapping()` function to map `BoseWord` and `BoseSentence` to qubit * Added native `ResourceOperator` subclasses for each of the controlled operators. [(#6579)](https://github.com/PennyLaneAI/pennylane/pull/6579) +* Added native `ResourceOperator` subclasses for each of the multi qubit operators. + [(#6538)](https://github.com/PennyLaneAI/pennylane/pull/6538) +

Breaking changes 💔

* `qml.fourier.qnode_spectrum` no longer automatically converts pure numpy parameters to the From 8ef340c6b8082df9d40b9df7b7c55869da3ab213 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:33:36 -0500 Subject: [PATCH 312/335] Apply suggestions from code review Co-authored-by: Jay Soni --- pennylane/labs/resource_estimation/ops/op_math/symbolic.py | 7 +------ pennylane/labs/resource_estimation/resource_container.py | 3 +-- .../labs/resource_estimation/templates/subroutines.py | 3 --- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 136a9b55ad2..7d047c4ddd6 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -153,12 +153,7 @@ def _resource_decomp( except re.ResourcesNotDefined: pass - try: - return _scale_dict(base_class.resources(**base_params, **kwargs), z) - except re.ResourcesNotDefined: - pass - - return {base_class.resource_rep(): z} + return {base_class.resource_rep(**base_params, **kwargs): z} def resource_params(self) -> dict: return { diff --git a/pennylane/labs/resource_estimation/resource_container.py b/pennylane/labs/resource_estimation/resource_container.py index f4fbc684dfc..f9d633cb03a 100644 --- a/pennylane/labs/resource_estimation/resource_container.py +++ b/pennylane/labs/resource_estimation/resource_container.py @@ -336,8 +336,7 @@ def _scale_dict(dict1: defaultdict, scalar: int, in_place=False): return combined_dict -def _make_hashable(d: dict) -> tuple: +def _make_hashable(d) -> tuple: if isinstance(d, dict): return tuple((name, _make_hashable(value)) for name, value in d.items()) - return d diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 233ec1b5535..47edd221e57 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -57,6 +57,3 @@ def resource_rep(cls, num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} return CompressedResourceOp(cls, params) - @staticmethod - def tracking_name(num_wires) -> str: - return f"QFT({num_wires})" From 907aac4a2af017fbb91ef109f4b97aaae01f6587 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:36:36 -0500 Subject: [PATCH 313/335] remove tests from qchem --- .../ops/qubit/test_qchem_ops.py | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index c9da2ed5361..8406e54ea7b 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -18,23 +18,6 @@ # pylint: disable=use-implicit-booleaness-not-comparison,no-self-use -# TODO: implement in resource_symbolic_ops branch -class TestSingleExcitation: - """Tests for the ResourceSingleExcitation class.""" - - def test_resources(self): - """Test that the resources are correct.""" - - def test_resource_params(self): - """Test that the resource params are correct.""" - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" - - class TestSingleExcitationMinus: """Tests for the ResourceSingleExcitationMinus class.""" @@ -147,40 +130,6 @@ def test_resources_from_rep(self): assert op_resource_type.resources(**op_resource_params) == expected -# TODO: add tests in resource_symbolic_ops branch -class TestDoubleExcitationMinus: - """Tests for the ResourceDoubleExcitationMinus class.""" - - def test_resources(self): - """Test that the resources are correct.""" - - def test_resource_params(self): - """Test that the resource params are correct.""" - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" - - -# TODO: add tests in resource_symbolic_ops branch -class TestDoubleExcitationPlus: - """Tests for the ResourceDoubleExcitationPlus class.""" - - def test_resources(self): - """Test that the resources are correct.""" - - def test_resource_params(self): - """Test that the resource params are correct.""" - - def test_resource_rep(self): - """Test that the compressed representation is correct.""" - - def test_resources_from_rep(self): - """Test that the resources can be obtained from the compressed representation.""" - - class TestFermionicSWAP: """Tests for the ResourceFermionicSWAP class.""" From ae6fb972d33f29d66045b7eb3180e4a0cbc743b2 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:58:12 -0500 Subject: [PATCH 314/335] some review changes --- .../ops/op_math/symbolic.py | 42 +++---------------- .../ops/op_math/test_symbolic.py | 18 ++++---- .../templates/test_resource_qft.py | 2 +- .../test_resource_tracking.py | 2 +- 4 files changed, 16 insertions(+), 48 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 7d047c4ddd6..8da745c5d52 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -45,7 +45,7 @@ def resource_params(self) -> dict: return {"base_class": type(self.base), "base_params": self.base.resource_params()} @classmethod - def resource_rep(cls, base_class, base_params, **kwargs) -> re.CompressedResourceOp: + def resource_rep(cls, base_class, base_params) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {"base_class": base_class, "base_params": base_params}) @staticmethod @@ -153,7 +153,7 @@ def _resource_decomp( except re.ResourcesNotDefined: pass - return {base_class.resource_rep(**base_params, **kwargs): z} + return {base_class.resource_rep(**base_params): z} def resource_params(self) -> dict: return { @@ -163,50 +163,18 @@ def resource_params(self) -> dict: } @classmethod - def resource_rep(cls, base_class, z, base_params, **kwargs) -> re.CompressedResourceOp: + def resource_rep(cls, base_class, z, base_params) -> re.CompressedResourceOp: return re.CompressedResourceOp( cls, {"base_class": base_class, "z": z, "base_params": base_params} ) @classmethod def pow_resource_decomp( - cls, z0, base_class, z, base_params, **kwargs + cls, z0, base_class, z, base_params ) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(base_class, z0 * z, base_params): 1} @staticmethod def tracking_name(base_class, z, base_params) -> str: base_name = base_class.tracking_name(**base_params) - return f"({base_name})**{z}" - - -def zyz_resources(num_ctrl_wires, num_work_wires): - """The zyz decomposition of a controlled unitary from SU(2) - defined in https://arxiv.org/pdf/quant-ph/9503016""" - - gate_types = {} - - # Lemma 5.1 - if num_ctrl_wires == 1: - cnot = re.ResourceCNOT.resource_rep() - phase = re.ResourceGlobalPhase.resource_rep() - ry = re.ResourceRZ.resource_rep() - rz = re.ResourceRZ.resource_rep() - - gate_types[cnot] = 2 - gate_types[phase] = 1 - gate_types[ry] = 2 - gate_types[rz] = 3 - - return gate_types - - # Lemma 7.9 - cry = re.ResourceCRY.resource_rep() - crz = re.ResourceCRZ.resource_rep() - multix = re.ResourceMultiControlledX.resource_rep(num_ctrl_wires - 1, 0, num_work_wires) - - gate_types[cry] = 2 - gate_types[crz] = 3 - gate_types[multix] = 2 - - return gate_types + return f"Pow({base_name}, {z})" diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index 98d4f530781..dbd440f8d56 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -38,10 +38,10 @@ def test_resource_params(self): @pytest.mark.parametrize( "op, expected", [ - (re.ResourceAdjoint(re.ResourceQFT([0, 1])), "Adjoint(QFT(2))"), + (re.ResourceAdjoint(re.ResourceQFT([0, 1])), "Adjoint(QFT)"), ( re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1]))), - "Adjoint(Adjoint(QFT(2)))", + "Adjoint(Adjoint(QFT))", ), ], ) @@ -71,19 +71,19 @@ def test_resource_params(self): @pytest.mark.parametrize( "op, expected", [ - (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT(2),1,0,0)"), + (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT,1,0,0)"), ( re.ResourceControlled( re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), control_wires=[3], ), - "C(C(QFT(2),1,0,0),1,0,0)", + "C(C(QFT,1,0,0),1,0,0)", ), ( re.ResourceControlled( re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1] ), - "C(QFT(2),2,1,0)", + "C(QFT,2,1,0)", ), ( re.ResourceControlled( @@ -92,7 +92,7 @@ def test_resource_params(self): control_values=[0, 1], work_wires=[4], ), - "C(QFT(2),2,1,1)", + "C(QFT,2,1,1)", ), ], ) @@ -120,9 +120,9 @@ def test_resource_params(self): @pytest.mark.parametrize( "op, expected", [ - (re.ResourcePow(re.ResourceQFT([0, 1]), 2), "(QFT(2))**2"), - (re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), "(Adjoint(QFT(2)))**2"), - (re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), "((QFT(2))**2)**3"), + (re.ResourcePow(re.ResourceQFT([0, 1]), 2), "Pow(QFT, 2)"), + (re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), "Pow(Adjoint(QFT), 2)"), + (re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), "Pow(Pow(QFT, 2), 3)"), ], ) def test_tracking_name(self, op, expected): diff --git a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py index b5cf4f94c89..c177f645150 100644 --- a/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py +++ b/pennylane/labs/tests/resource_estimation/templates/test_resource_qft.py @@ -82,4 +82,4 @@ def test_resources_from_rep(self, num_wires, num_hadamard, num_swap, num_ctrl_ph @pytest.mark.parametrize("num_wires", range(10)) def test_tracking_name(self, num_wires): """Test that the tracking name is correct.""" - assert re.ResourceQFT.tracking_name(num_wires + 1) == f"QFT({num_wires+1})" + assert re.ResourceQFT.tracking_name(num_wires + 1) == "QFT" diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index 396f9149cb4..988ca4127f6 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -231,7 +231,7 @@ def test_clean_gate_counts(self): ) expected_clean_counts = defaultdict( - int, {"CNOT": 1, "Hadamard": 3, "QFT(3)": 4, "QFT(5)": 1} + int, {"CNOT": 1, "Hadamard": 3, "QFT": 5} ) assert _clean_gate_counts(gate_counts) == expected_clean_counts From e170b6f9103dce61da9ce7583d376a12c490b846 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 09:58:22 -0500 Subject: [PATCH 315/335] formatting --- pennylane/labs/resource_estimation/templates/subroutines.py | 1 - .../labs/tests/resource_estimation/test_resource_tracking.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pennylane/labs/resource_estimation/templates/subroutines.py b/pennylane/labs/resource_estimation/templates/subroutines.py index 47edd221e57..43514278e63 100644 --- a/pennylane/labs/resource_estimation/templates/subroutines.py +++ b/pennylane/labs/resource_estimation/templates/subroutines.py @@ -56,4 +56,3 @@ def resource_params(self) -> dict: def resource_rep(cls, num_wires) -> CompressedResourceOp: params = {"num_wires": num_wires} return CompressedResourceOp(cls, params) - diff --git a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py index 988ca4127f6..4ffd8af8ca8 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_tracking.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_tracking.py @@ -230,9 +230,7 @@ def test_clean_gate_counts(self): }, ) - expected_clean_counts = defaultdict( - int, {"CNOT": 1, "Hadamard": 3, "QFT": 5} - ) + expected_clean_counts = defaultdict(int, {"CNOT": 1, "Hadamard": 3, "QFT": 5}) assert _clean_gate_counts(gate_counts) == expected_clean_counts From 5268cd3e34a088a5b19b257c48712680208279da Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 10:05:53 -0500 Subject: [PATCH 316/335] add tracking_name_from_op --- pennylane/labs/resource_estimation/resource_operator.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index 01ea396440c..bcf50658543 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -122,10 +122,14 @@ def pow_resource_decomp(cls, z, *args, **kwargs) -> Dict[CompressedResourceOp, i raise ResourcesNotDefined @classmethod - def tracking_name(cls, *args, **kwargs) -> str: + def tracking_name(cls, *args) -> str: """Returns a name used to track the operator during resource estimation.""" return cls.__name__.replace("Resource", "") + def tracking_name_from_op(self) -> str: + """Returns the tracking name built with the operator's parameters.""" + return self.__class__.tracking_name(**self.resource_params()) + class ResourcesNotDefined(Exception): """Exception to be raised when a ``ResourceOperator`` does not implement _resource_decomp""" From 4624819117216f94535a37436de5a3717e86b515 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 13:14:40 -0500 Subject: [PATCH 317/335] tests --- .../resource_estimation/resource_operator.py | 2 +- .../ops/op_math/test_symbolic.py | 210 ++++++++++++------ .../test_resource_container.py | 38 +++- 3 files changed, 168 insertions(+), 82 deletions(-) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index bcf50658543..d81478d095a 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -122,7 +122,7 @@ def pow_resource_decomp(cls, z, *args, **kwargs) -> Dict[CompressedResourceOp, i raise ResourcesNotDefined @classmethod - def tracking_name(cls, *args) -> str: + def tracking_name(cls, *args, **kwargs) -> str: """Returns a name used to track the operator during resource estimation.""" return cls.__name__.replace("Resource", "") diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index dbd440f8d56..a7979b51273 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -25,106 +25,170 @@ class TestResourceAdjoint: """Tests for ResourceAdjoint""" - def test_resource_params(self): + adjoint_ops = [ + re.ResourceAdjoint(re.ResourceQFT([0, 1])), + re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1]))), + re.ResourceAdjoint(re.ResourcePow(re.ResourceX(0), 5)), + ] + + expected_params = [ + {"base_class": re.ResourceQFT, "base_params": {"num_wires": 2}}, + { + "base_class": re.ResourceAdjoint, + "base_params": {"base_class": re.ResourceQFT, "base_params": {"num_wires": 2}}, + }, + { + "base_class": re.ResourcePow, + "base_params": {"base_class": re.ResourceX, "base_params": {}, "z": 5}, + }, + ] + + @pytest.mark.parametrize("op, expected", zip(adjoint_ops, expected_params)) + def test_resource_params(self, op, expected): """Test that the resources are correct""" + assert op.resource_params() == expected - base = re.ResourceQFT(wires=[0, 1, 2]) - op = re.ResourceAdjoint(base=base) - assert op.resource_params() == { - "base_class": re.ResourceQFT, - "base_params": base.resource_params(), - } + expected_names = [ + "Adjoint(QFT)", + "Adjoint(Adjoint(QFT))", + "Adjoint(Pow(X, 5))", + ] - @pytest.mark.parametrize( - "op, expected", - [ - (re.ResourceAdjoint(re.ResourceQFT([0, 1])), "Adjoint(QFT)"), - ( - re.ResourceAdjoint(re.ResourceAdjoint(re.ResourceQFT([0, 1]))), - "Adjoint(Adjoint(QFT))", - ), - ], - ) + @pytest.mark.parametrize("op, expected", zip(adjoint_ops, expected_names)) def test_tracking_name(self, op, expected): """Test that the tracking name is correct""" - rep = op.resource_rep_from_op() - name = rep.op_type.tracking_name(**rep.params) + name = op.tracking_name_from_op() assert name == expected + @pytest.mark.parametrize("op", adjoint_ops) + def test_tracking(self, op): + """Test that adjoints can be tracked.""" + tracking_name = op.tracking_name_from_op() + + expected = re.Resources(gate_types={tracking_name: 1}) + gate_set = {tracking_name} + + assert re.get_resources(op, gate_set=gate_set) == expected + class TestResourceControlled: """Tests for ResourceControlled""" - def test_resource_params(self): - """Test that the resources are correct""" - - base = re.ResourceQFT(wires=[0, 1, 2]) - op = re.ResourceControlled(base=base, control_wires=[3]) - assert op.resource_params() == { + controlled_ops = [ + re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), + re.ResourceControlled( + re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), control_wires=[3] + ), + re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1]), + re.ResourceControlled( + re.ResourceAdjoint(re.ResourceQFT([0, 1])), + control_wires=[2, 3], + control_values=[0, 1], + work_wires=[4], + ), + ] + + expected_params = [ + { "base_class": re.ResourceQFT, - "base_params": base.resource_params(), + "base_params": {"num_wires": 2}, + "num_ctrl_wires": 1, + "num_ctrl_values": 0, + "num_work_wires": 0, + }, + { + "base_class": re.ResourceControlled, + "base_params": { + "base_class": re.ResourceQFT, + "base_params": {"num_wires": 2}, + "num_ctrl_wires": 1, + "num_ctrl_values": 0, + "num_work_wires": 0, + }, "num_ctrl_wires": 1, "num_ctrl_values": 0, "num_work_wires": 0, - } + }, + { + "base_class": re.ResourceQFT, + "base_params": {"num_wires": 2}, + "num_ctrl_wires": 2, + "num_ctrl_values": 1, + "num_work_wires": 0, + }, + { + "base_class": re.ResourceAdjoint, + "base_params": {"base_class": re.ResourceQFT, "base_params": {"num_wires": 2}}, + "num_ctrl_wires": 2, + "num_ctrl_values": 1, + "num_work_wires": 1, + }, + ] + + @pytest.mark.parametrize("op, expected", zip(controlled_ops, expected_params)) + def test_resource_params(self, op, expected): + """Test that the resources are correct""" + assert op.resource_params() == expected - @pytest.mark.parametrize( - "op, expected", - [ - (re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), "C(QFT,1,0,0)"), - ( - re.ResourceControlled( - re.ResourceControlled(re.ResourceQFT([0, 1]), control_wires=[2]), - control_wires=[3], - ), - "C(C(QFT,1,0,0),1,0,0)", - ), - ( - re.ResourceControlled( - re.ResourceQFT([0, 1]), control_wires=[2, 3], control_values=[0, 1] - ), - "C(QFT,2,1,0)", - ), - ( - re.ResourceControlled( - re.ResourceQFT([0, 1]), - control_wires=[2, 3], - control_values=[0, 1], - work_wires=[4], - ), - "C(QFT,2,1,1)", - ), - ], - ) + expected_names = [ + "C(QFT,1,0,0)", + "C(C(QFT,1,0,0),1,0,0)", + "C(QFT,2,1,0)", + "C(Adjoint(QFT),2,1,1)", + ] + + @pytest.mark.parametrize("op, expected", zip(controlled_ops, expected_names)) def test_tracking_name(self, op, expected): """Test that the tracking name is correct""" - rep = op.resource_rep_from_op() - name = rep.op_type.tracking_name(**rep.params) + name = op.tracking_name_from_op() assert name == expected + @pytest.mark.parametrize("op", controlled_ops) + def test_tracking(self, op): + """Test that adjoints can be tracked.""" + tracking_name = op.tracking_name_from_op() + + expected = re.Resources(gate_types={tracking_name: 1}) + gate_set = {tracking_name} + + assert re.get_resources(op, gate_set=gate_set) == expected + class TestResourcePow: """Tests for ResourcePow""" - def test_resource_params(self): + pow_ops = [ + re.ResourcePow(re.ResourceQFT([0, 1]), 2), + re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), + re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), + ] + + expected_params = [ + {"base_class": re.ResourceQFT, "base_params": {"num_wires": 2}, "z": 2}, + { + "base_class": re.ResourceAdjoint, + "base_params": {"base_class": re.ResourceQFT, "base_params": {"num_wires": 2}}, + "z": 2, + }, + { + "base_class": re.ResourcePow, + "base_params": {"base_class": re.ResourceQFT, "base_params": {"num_wires": 2}, "z": 2}, + "z": 3, + }, + ] + + @pytest.mark.parametrize("op, expected", zip(pow_ops, expected_params)) + def test_resource_params(self, op, expected): """Test that the resources are correct""" + assert op.resource_params() == expected - base = re.ResourceQFT(wires=[0, 1, 2]) - op = re.ResourcePow(base=base, z=5) - assert op.resource_params() == { - "base_class": re.ResourceQFT, - "z": 5, - "base_params": base.resource_params(), - } + expected_names = [ + "Pow(QFT, 2)", + "Pow(Adjoint(QFT), 2)", + "Pow(Pow(QFT, 2), 3)", + ] - @pytest.mark.parametrize( - "op, expected", - [ - (re.ResourcePow(re.ResourceQFT([0, 1]), 2), "Pow(QFT, 2)"), - (re.ResourcePow(re.ResourceAdjoint(re.ResourceQFT([0, 1])), 2), "Pow(Adjoint(QFT), 2)"), - (re.ResourcePow(re.ResourcePow(re.ResourceQFT([0, 1]), 2), 3), "Pow(Pow(QFT, 2), 3)"), - ], - ) + @pytest.mark.parametrize("op, expected", zip(pow_ops, expected_names)) def test_tracking_name(self, op, expected): """Test that the tracking name is correct""" rep = op.resource_rep_from_op() diff --git a/pennylane/labs/tests/resource_estimation/test_resource_container.py b/pennylane/labs/tests/resource_estimation/test_resource_container.py index 55e5c6a6882..0240ba44253 100644 --- a/pennylane/labs/tests/resource_estimation/test_resource_container.py +++ b/pennylane/labs/tests/resource_estimation/test_resource_container.py @@ -51,19 +51,25 @@ class ResourceDummyTrotterProduct(ResourceOperator): """Dummy testing class representing TrotterProduct gate""" +class ResourceDummyAdjoint(ResourceOperator): + """Dummy testing class representing the Adjoint symbolic operator""" + + class TestCompressedResourceOp: """Testing the methods and attributes of the CompressedResourceOp class""" test_hamiltonian = qml.dot([1, -1, 0.5], [qml.X(0), qml.Y(1), qml.Z(0) @ qml.Z(1)]) compressed_ops_and_params_lst = ( - ("DummyX", ResourceDummyX, {"num_wires": 1}), - ("DummyQFT", ResourceDummyQFT, {"num_wires": 5}), - ("DummyQSVT", ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}), + ("DummyX", ResourceDummyX, {"num_wires": 1}, None), + ("DummyQFT", ResourceDummyQFT, {"num_wires": 5}, None), + ("DummyQSVT", ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}, None), ( "DummyTrotterProduct", ResourceDummyTrotterProduct, {"Hamiltonian": test_hamiltonian, "num_steps": 5, "order": 2}, + None, ), + ("X", ResourceDummyX, {"num_wires": 1}, "X"), ) compressed_op_reprs = ( @@ -71,12 +77,13 @@ class TestCompressedResourceOp: "DummyQFT", "DummyQSVT", "DummyTrotterProduct", + "X", ) - @pytest.mark.parametrize("name, op_type, parameters", compressed_ops_and_params_lst) - def test_init(self, name, op_type, parameters): + @pytest.mark.parametrize("name, op_type, parameters, name_param", compressed_ops_and_params_lst) + def test_init(self, name, op_type, parameters, name_param): """Test that we can correctly instantiate CompressedResourceOp""" - cr_op = CompressedResourceOp(op_type, parameters) + cr_op = CompressedResourceOp(op_type, parameters, name=name_param) assert cr_op._name == name assert cr_op.op_type is op_type @@ -93,6 +100,21 @@ def test_hash(self): assert hash(CmprssedQSVT1) == hash(CmprssedQSVT2) # compare identical instance assert hash(CmprssedQSVT1) != hash(Other) + # test dictionary as parameter + CmprssedAdjoint1 = CompressedResourceOp( + ResourceDummyAdjoint, {"base_class": ResourceDummyQFT, "base_params": {"num_wires": 1}} + ) + CmprssedAdjoint2 = CompressedResourceOp( + ResourceDummyAdjoint, {"base_class": ResourceDummyQFT, "base_params": {"num_wires": 1}} + ) + Other = CompressedResourceOp( + ResourceDummyAdjoint, {"base_class": ResourceDummyQFT, "base_params": {"num_wires": 2}} + ) + + assert hash(CmprssedAdjoint1) == hash(CmprssedAdjoint1) + assert hash(CmprssedAdjoint1) == hash(CmprssedAdjoint2) + assert hash(CmprssedAdjoint1) != hash(Other) + def test_equality(self): """Test that the equality methods behaves as expected""" CmprssedQSVT1 = CompressedResourceOp(ResourceDummyQSVT, {"num_wires": 3, "num_angles": 5}) @@ -107,8 +129,8 @@ def test_equality(self): @pytest.mark.parametrize("args, repr", zip(compressed_ops_and_params_lst, compressed_op_reprs)) def test_repr(self, args, repr): """Test that the repr method behaves as expected.""" - _, op_type, parameters = args - cr_op = CompressedResourceOp(op_type, parameters) + _, op_type, parameters, name_param = args + cr_op = CompressedResourceOp(op_type, parameters, name=name_param) assert str(cr_op) == repr From 3018ddc5fd2eca3efe71c8804bde825ed18845fa Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 13:34:10 -0500 Subject: [PATCH 318/335] pylint --- pennylane/labs/resource_estimation/resource_operator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index d81478d095a..f1cece1df65 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -20,6 +20,7 @@ if TYPE_CHECKING: from pennylane.labs.resource_estimation import CompressedResourceOp +# pylint: disable=unused-argument class ResourceOperator(ABC): r"""Abstract class that defines the methods a PennyLane Operator From 84840264ae269cae9a6ce61f8699ccb436f36cc7 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 13:42:56 -0500 Subject: [PATCH 319/335] one more test --- .../resource_estimation/ops/op_math/test_symbolic.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py index a7979b51273..f4233476d1e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_symbolic.py @@ -195,6 +195,16 @@ def test_tracking_name(self, op, expected): name = rep.op_type.tracking_name(**rep.params) assert name == expected + @pytest.mark.parametrize("op", pow_ops) + def test_tracking(self, op): + """Test that adjoints can be tracked.""" + tracking_name = op.tracking_name_from_op() + + expected = re.Resources(gate_types={tracking_name: 1}) + gate_set = {tracking_name} + + assert re.get_resources(op, gate_set=gate_set) == expected + @pytest.mark.parametrize( "nested_op, expected_op", [ From 851aa912d5dbdf85b7c4ca37f7f3c11ca9057d85 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 14:24:36 -0500 Subject: [PATCH 320/335] chagelog --- doc/releases/changelog-dev.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 574cb33747d..c9d41b10854 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -203,6 +203,9 @@ added `binary_mapping()` function to map `BoseWord` and `BoseSentence` to qubit * Added native `ResourceOperator` subclasses for each of the multi qubit operators. [(#6538)](https://github.com/PennyLaneAI/pennylane/pull/6538) +* Added native `ResourceOperator` subclasses for Adjoint, Controlled, and Pow. + [(#6592)](https://github.com/PennyLaneAI/pennylane/pull/6592) +

Breaking changes 💔

* `qml.fourier.qnode_spectrum` no longer automatically converts pure numpy parameters to the From 46ba719f2df5eed3193c031db06ca23d631b37e3 Mon Sep 17 00:00:00 2001 From: Will Date: Tue, 3 Dec 2024 14:33:57 -0500 Subject: [PATCH 321/335] formatting --- .../labs/resource_estimation/ops/op_math/controlled_ops.py | 2 +- pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py | 4 +++- pennylane/labs/resource_estimation/resource_operator.py | 1 + .../tests/resource_estimation/ops/qubit/test_qchem_ops.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 31f53fd8680..6ca8000de7d 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -17,7 +17,7 @@ import pennylane as qml import pennylane.labs.resource_estimation as re -# pylint: disable=arguments-differ,too-many-ancestors +# pylint: disable=arguments-differ,too-many-ancestors,too-many-arguments class ResourceCH(qml.CH, re.ResourceOperator): diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index ccc8234cc52..5719b47181b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -194,6 +194,7 @@ def resource_params(self): def resource_rep(cls): return re.CompressedResourceOp(cls, {}) + class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperator): r"""Resource class for the DoubleExcitationMinus gate. @@ -266,6 +267,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return re.CompressedResourceOp(cls, {}) + class ResourceOrbitalRotation(qml.OrbitalRotation, re.ResourceOperator): r"""Resource class for the OrbitalRotation gate. @@ -295,7 +297,7 @@ def resource_params(self): def resource_rep(cls, **kwargs): return re.CompressedResourceOp(cls, {}) - + class ResourceFermionicSWAP(qml.FermionicSWAP, re.ResourceOperator): r"""Resource class for the FermionicSWAP gate. diff --git a/pennylane/labs/resource_estimation/resource_operator.py b/pennylane/labs/resource_estimation/resource_operator.py index f1cece1df65..eec72f6191b 100644 --- a/pennylane/labs/resource_estimation/resource_operator.py +++ b/pennylane/labs/resource_estimation/resource_operator.py @@ -22,6 +22,7 @@ # pylint: disable=unused-argument + class ResourceOperator(ABC): r"""Abstract class that defines the methods a PennyLane Operator must implement in order to be used for resource estimation. diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py index 19ee82acf38..3a0690049cf 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_qchem_ops.py @@ -175,7 +175,7 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected - + class TestDoubleExcitationMinus: """Tests for the ResourceDoubleExcitationMinus class.""" From c39ffe1ca650154d5a5a37c8dd8e09e444c5247a Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 4 Dec 2024 16:54:35 -0500 Subject: [PATCH 322/335] controlled S and T --- .../ops/op_math/controlled_ops.py | 2 +- .../ops/qubit/non_parametric_ops.py | 28 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 6ca8000de7d..a491faa5413 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -787,7 +787,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: return cls.resources(**kwargs) @staticmethod diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 17051f66657..1c5f8ee2db6 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -35,12 +35,12 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: return {re.ResourceCH.resource_rep(): 1} @@ -74,6 +74,13 @@ def resource_rep(cls) -> re.CompressedResourceOp: def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 3} + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): + if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: + return {re.ResourceControlledPhaseShift.resource_rep(): 1} + + raise re.ResourcesNotDefined + @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): z % 4} @@ -130,12 +137,12 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires, **kwargs + num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: return {re.ResourceCSWAP.resource_rep(): 1} @@ -143,7 +150,7 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined @classmethod - def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): z % 2} @@ -162,12 +169,19 @@ def resource_rep(cls) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) @classmethod - def adjoint_resource_decomp(cls, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: """Resources obtained from the identity T^8 = I.""" return {cls.resource_rep(): 7} + @staticmethod + def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): + if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: + return {re.ResourceControlledPhaseShift.resource_rep(): 1} + + raise re.ResourcesNotDefined + @classmethod - def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: """Resources obtained from the identity T^8 = I.""" return {cls.resource_rep(): z % 8} From 34c2a2086c877de9881cc5cc3602c1c4e74da2d1 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 4 Dec 2024 17:02:17 -0500 Subject: [PATCH 323/335] fix tests --- .../ops/op_math/controlled_ops.py | 6 +++--- .../ops/op_math/test_controlled_ops.py | 12 ++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index a491faa5413..e2c4f923525 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -788,7 +788,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(**kwargs) + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( @@ -801,5 +801,5 @@ def controlled_resource_decomp( } @classmethod - def pow_resource_decomp(cls, z, **kwargs) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(**kwargs) + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 7ca553f0ace..8c7114657a2 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -453,22 +453,18 @@ def test_adjoint_decomp(self, phi, wires): """Test that the adjoint resources are correct.""" op = re.ResourceControlledPhaseShift(phi, wires) - rep = op.resource_rep_from_op() - res = rep.op_type.resources(**rep.params) - adjoint_res = rep.op_type.adjoint_resource_decomp(**rep.params) + adjoint = re.ResourceAdjoint(op) - assert res == adjoint_res + assert re.get_resources(op) == re.get_resources(adjoint) @pytest.mark.parametrize("phi, wires", params) def test_pow_decomp(self, phi, wires): """Test that the adjoint resources are correct.""" op = re.ResourceControlledPhaseShift(phi, wires) - rep = op.resource_rep_from_op() - res = rep.op_type.resources(**rep.params) - adjoint_res = rep.op_type.pow_resource_decomp(2, **rep.params) + pow = re.ResourcePow(op, 2) - assert res == adjoint_res + assert re.get_resources(op) == re.get_resources(pow) class TestCNOT: From 2294a6e573aa2dfe2eeeb9526f3dffb2e868a7b9 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 10 Dec 2024 12:20:39 -0500 Subject: [PATCH 324/335] fixing resource methods --- .../labs/resource_estimation/ops/identity.py | 10 ++++----- .../ops/op_math/controlled_ops.py | 22 +++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index b6bcb1c25dd..a8b0f52d201 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -39,11 +39,11 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: return {} @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp(num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0) -> Dict[re.CompressedResourceOp, int]: return {} @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def pow_resource_decomp(z) -> Dict[re.CompressedResourceOp, int]: return {} @@ -66,9 +66,9 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: return {} @staticmethod - def controlled_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} + def controlled_resource_decomp(num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0) -> Dict[re.CompressedResourceOp, int]: + return {re.ResourcePhaseShift.resource_rep(): 1} @staticmethod - def pow_resource_decomp() -> Dict[re.CompressedResourceOp, int]: + def pow_resource_decomp(z) -> Dict[re.CompressedResourceOp, int]: return {} diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index e2c4f923525..53d8760ae84 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -75,7 +75,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceCY(qml.CY, re.ResourceOperator): @@ -127,7 +127,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceCZ(qml.CZ, re.ResourceOperator): @@ -180,7 +180,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceCSWAP(qml.CSWAP, re.ResourceOperator): @@ -237,7 +237,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceCCZ(qml.CCZ, re.ResourceOperator): @@ -286,7 +286,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceCNOT(qml.CNOT, re.ResourceOperator): @@ -327,7 +327,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceToffoli(qml.Toffoli, re.ResourceOperator): @@ -423,7 +423,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(): 1} class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): @@ -532,7 +532,7 @@ def controlled_resource_decomp( def pow_resource_decomp( cls, z, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): z % 2} + return {} if z%2 == 0 else {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): 1} class ResourceCRX(qml.CRX, re.ResourceOperator): @@ -585,7 +585,7 @@ def controlled_resource_decomp( } @classmethod - def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} @@ -754,6 +754,10 @@ def controlled_resource_decomp( ): 1 } + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceControlledPhaseShift(qml.ControlledPhaseShift, re.ResourceOperator): r"""Resource class for the ControlledPhaseShift gate. From 990cfaac6fc053b0c28c4cf3c830b5637ed5eec1 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 10 Dec 2024 12:21:49 -0500 Subject: [PATCH 325/335] format --- .../labs/resource_estimation/ops/identity.py | 8 ++++++-- .../ops/op_math/controlled_ops.py | 20 +++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index a8b0f52d201..fb4ed5189d5 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -39,7 +39,9 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: return {} @staticmethod - def controlled_resource_decomp(num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0 + ) -> Dict[re.CompressedResourceOp, int]: return {} @staticmethod @@ -66,7 +68,9 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: return {} @staticmethod - def controlled_resource_decomp(num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0) -> Dict[re.CompressedResourceOp, int]: + def controlled_resource_decomp( + num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0 + ) -> Dict[re.CompressedResourceOp, int]: return {re.ResourcePhaseShift.resource_rep(): 1} @staticmethod diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 53d8760ae84..3b86ec5148d 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -75,7 +75,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceCY(qml.CY, re.ResourceOperator): @@ -127,7 +127,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceCZ(qml.CZ, re.ResourceOperator): @@ -180,7 +180,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceCSWAP(qml.CSWAP, re.ResourceOperator): @@ -237,7 +237,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceCCZ(qml.CCZ, re.ResourceOperator): @@ -286,7 +286,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceCNOT(qml.CNOT, re.ResourceOperator): @@ -327,7 +327,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceToffoli(qml.Toffoli, re.ResourceOperator): @@ -423,7 +423,7 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(): 1} + return {} if z % 2 == 0 else {cls.resource_rep(): 1} class ResourceMultiControlledX(qml.MultiControlledX, re.ResourceOperator): @@ -532,7 +532,11 @@ def controlled_resource_decomp( def pow_resource_decomp( cls, z, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return {} if z%2 == 0 else {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): 1} + return ( + {} + if z % 2 == 0 + else {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): 1} + ) class ResourceCRX(qml.CRX, re.ResourceOperator): From 17da0d0cb857f5968053191ee128dad5904128be Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Tue, 10 Dec 2024 14:58:45 -0500 Subject: [PATCH 326/335] fix globalphase decomp --- pennylane/labs/resource_estimation/ops/identity.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index fb4ed5189d5..82bb992c3e6 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -71,7 +71,9 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0 ) -> Dict[re.CompressedResourceOp, int]: - return {re.ResourcePhaseShift.resource_rep(): 1} + if num_ctrl_wires == 1: + return {re.ResourcePhaseShift.resource_rep(): 1} + raise re.ResourcesNotDefined @staticmethod def pow_resource_decomp(z) -> Dict[re.CompressedResourceOp, int]: From 13efda915991de85c2c199b4eccad4f2368a1da4 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 12 Dec 2024 14:25:33 -0500 Subject: [PATCH 327/335] modified resources --- .../labs/resource_estimation/ops/identity.py | 6 +- .../ops/op_math/controlled_ops.py | 4 +- .../ops/op_math/symbolic.py | 1 - .../ops/qubit/non_parametric_ops.py | 28 +- .../ops/qubit/parametric_ops_multi_qubit.py | 294 +++++++++++++++++- .../ops/qubit/parametric_ops_single_qubit.py | 7 +- .../resource_estimation/resource_tracking.py | 1 - 7 files changed, 312 insertions(+), 29 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 82bb992c3e6..6c95c3fd6d9 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -40,7 +40,7 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp( - num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0 + num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: return {} @@ -69,9 +69,9 @@ def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp( - num_ctrl_wires=1, num_ctrl_values=0, num_work_wires=0 + num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1: + if num_ctrl_values == 0: return {re.ResourcePhaseShift.resource_rep(): 1} raise re.ResourcesNotDefined diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 3b86ec5148d..2c152dd0354 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -393,10 +393,12 @@ def textbook_resource_decomp() -> Dict[re.CompressedResourceOp, int]: cnot = re.ResourceCNOT.resource_rep() t = re.ResourceT.resource_rep() h = re.ResourceHadamard.resource_rep() + t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) gate_types[cnot] = 6 gate_types[h] = 2 - gate_types[t] = 7 + gate_types[t] = 4 + gate_types[t_dag] = 3 return gate_types diff --git a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py index 7be7f64ec5d..29f417ea963 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/symbolic.py +++ b/pennylane/labs/resource_estimation/ops/op_math/symbolic.py @@ -16,7 +16,6 @@ from typing import Dict import pennylane.labs.resource_estimation as re -from pennylane.labs.resource_estimation.resource_container import _combine_dict, _scale_dict from pennylane.ops.op_math.adjoint import AdjointOperation from pennylane.ops.op_math.controlled import ControlledOp from pennylane.ops.op_math.pow import PowOperation diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 1c5f8ee2db6..1d29f2db011 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -49,7 +49,9 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + if z % 2 == 0: + return {} + return {cls.resource_rep(): 1} class ResourceS(qml.S, re.ResourceOperator): @@ -83,7 +85,9 @@ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 4} + if (mod_4 := z % 4) == 0: + return {} + return {cls.resource_rep(): mod_4} class ResourceSWAP(qml.SWAP, re.ResourceOperator): @@ -151,7 +155,9 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + if z % 2 == 0: + return {} + return {cls.resource_rep(): 1} class ResourceT(qml.T, re.ResourceOperator): @@ -183,7 +189,9 @@ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: """Resources obtained from the identity T^8 = I.""" - return {cls.resource_rep(): z % 8} + if (mod_8 := z % 8) == 0: + return {} + return {cls.resource_rep(): mod_8} class ResourceX(qml.X, re.ResourceOperator): @@ -226,7 +234,9 @@ def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + if z % 2 == 0: + return {} + return {cls.resource_rep(): 1} class ResourceY(qml.Y, re.ResourceOperator): @@ -265,7 +275,9 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + if z % 2 == 0: + return {} + return {cls.resource_rep(): 1} class ResourceZ(qml.Z, re.ResourceOperator): @@ -305,4 +317,6 @@ def controlled_resource_decomp( @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): z % 2} + if z % 2 == 0: + return {} + return {cls.resource_rep(): 1} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index acfbb755da2..e9446c2ef5b 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. r"""Resource operators for parametric multi qubit operations.""" +from typing import Dict + import pennylane as qml import pennylane.labs.resource_estimation as re @@ -31,8 +33,8 @@ class ResourceMultiRZ(qml.MultiRZ, re.ResourceOperator): @staticmethod def _resource_decomp(num_wires, **kwargs): - cnot = re.CompressedResourceOp(re.ResourceCNOT, {}) - rz = re.CompressedResourceOp(re.ResourceRZ, {}) + cnot = re.ResourceCNOT.resource_rep() + rz = re.ResourceRZ.resource_rep() gate_types = {} gate_types[cnot] = 2 * (num_wires - 1) @@ -47,6 +49,39 @@ def resource_params(self): def resource_rep(cls, num_wires): return re.CompressedResourceOp(cls, {"num_wires": num_wires}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + num_wires, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + cnot = re.ResourceCNOT.resource_rep() + ctrl_rz = re.ResourceControlled.resource_rep( + base_class=re.ResourceRZ, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types = {} + gate_types[cnot] = 2 * (num_wires - 1) + gate_types[ctrl_rz] = 1 + + return gate_types + + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): r"""Resource class for the PauliRot gate. @@ -79,8 +114,9 @@ def _resource_decomp(pauli_word, **kwargs): active_wires = len(pauli_word.replace("I", "")) h = re.ResourceHadamard.resource_rep() - s = re.ResourceS.resource_rep() # TODO: add Adjoint(S) in the symbolic PRs + s = re.ResourceS.resource_rep() rz = re.ResourceRZ.resource_rep() + s_dagg = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) cnot = re.ResourceCNOT.resource_rep() h_count = 0 @@ -95,7 +131,8 @@ def _resource_decomp(pauli_word, **kwargs): gate_types = {} gate_types[h] = h_count - gate_types[s] = s_count + (3 * s_count) # S^dagg = 3*S in cost + gate_types[s] = s_count + gate_types[s_dagg] = s_count gate_types[rz] = 1 gate_types[cnot] = 2 * (active_wires - 1) @@ -110,6 +147,66 @@ def resource_params(self): def resource_rep(cls, pauli_word): return re.CompressedResourceOp(cls, {"pauli_word": pauli_word}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + pauli_word, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + if set(pauli_word) == {"I"}: + ctrl_gp = re.ResourceControlled.resource_rep( + re.ResourceGlobalPhase, + {}, + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) + return {ctrl_gp: 1} + + active_wires = len(pauli_word.replace("I", "")) + + h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep() + s_dagg = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) + cnot = re.ResourceCNOT.resource_rep() + ctrl_rz = re.ResourceControlled.resource_rep( + base_class=re.ResourceRZ, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + h_count = 0 + s_count = 0 + + for gate in pauli_word: + if gate == "X": + h_count += 2 + if gate == "Y": + h_count += 2 + s_count += 1 + + gate_types = {} + gate_types[h] = h_count + gate_types[s] = s_count + gate_types[s_dagg] = s_count + gate_types[ctrl_rz] = 1 + gate_types[cnot] = 2 * (active_wires - 1) + + return gate_types + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): r"""Resource class for the IsingXX gate. @@ -134,7 +231,7 @@ class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): cnot = re.ResourceCNOT.resource_rep() rx = re.ResourceRX.resource_rep() @@ -148,9 +245,40 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + cnot = re.ResourceCNOT.resource_rep() + ctrl_rx = re.ResourceControlled.resource_rep( + base_class=re.ResourceRX, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types = {} + gate_types[cnot] = 2 + gate_types[ctrl_rx] = 1 + + return gate_types + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): r"""Resource class for the IsingYY gate. @@ -175,7 +303,7 @@ class ResourceIsingYY(qml.IsingYY, re.ResourceOperator): """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): cy = re.ops.ResourceCY.resource_rep() ry = re.ops.ResourceRY.resource_rep() @@ -189,9 +317,40 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + cy = re.ops.ResourceCY.resource_rep() + ctrl_ry = re.ResourceControlled.resource_rep( + base_class=re.ResourceRY, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types = {} + gate_types[cy] = 2 + gate_types[ctrl_ry] = 1 + + return gate_types + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): r"""Resource class for the IsingXY gate. @@ -216,7 +375,7 @@ class ResourceIsingXY(qml.IsingXY, re.ResourceOperator): """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): h = re.ResourceHadamard.resource_rep() cy = re.ResourceCY.resource_rep() ry = re.ResourceRY.resource_rep() @@ -234,9 +393,50 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + h = re.ResourceHadamard.resource_rep() + cy = re.ResourceCY.resource_rep() + ctrl_rx = re.ResourceControlled.resource_rep( + base_class=re.ResourceRX, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + ctrl_ry = re.ResourceControlled.resource_rep( + base_class=re.ResourceRY, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types = {} + gate_types[h] = 2 + gate_types[cy] = 2 + gate_types[ctrl_ry] = 1 + gate_types[ctrl_rx] = 1 + + return gate_types + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): r"""Resource class for the IsingZZ gate. @@ -261,7 +461,7 @@ class ResourceIsingZZ(qml.IsingZZ, re.ResourceOperator): """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): cnot = re.ResourceCNOT.resource_rep() rz = re.ResourceRZ.resource_rep() @@ -275,9 +475,40 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + cnot = re.ResourceCNOT.resource_rep() + ctrl_rz = re.ResourceControlled.resource_rep( + base_class=re.ResourceRZ, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types = {} + gate_types[cnot] = 2 + gate_types[ctrl_rz] = 1 + + return gate_types + raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): r"""Resource class for the PSWAP gate. @@ -299,7 +530,7 @@ class ResourcePSWAP(qml.PSWAP, re.ResourceOperator): """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): swap = re.ResourceSWAP.resource_rep() cnot = re.ResourceCNOT.resource_rep() phase = re.ResourcePhaseShift.resource_rep() @@ -315,5 +546,40 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, *args): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) + + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} + + @staticmethod + def controlled_resource_decomp( + num_ctrl_wires, + num_ctrl_values, + num_work_wires, + ) -> Dict[re.CompressedResourceOp, int]: + if num_ctrl_values == 0: + cnot = re.ResourceCNOT.resource_rep() + ctrl_swap = re.ResourceControlled.resource_rep( + base_class=re.ResourceSWAP, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + ctrl_ps = re.ResourceControlled.resource_rep( + base_class=re.ResourcePhaseShift, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types = {} + gate_types[ctrl_swap] = 1 + gate_types[cnot] = 2 + gate_types[ctrl_ps] = 1 + return gate_types + + raise re.ResourcesNotDefined diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index b79a643d021..360d23ea272 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -186,11 +186,10 @@ class ResourceRot(qml.Rot, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: - rx = ResourceRX.resource_rep() ry = ResourceRY.resource_rep() rz = ResourceRZ.resource_rep() - gate_types = {rx: 1, ry: 1, rz: 1} + gate_types = {ry: 1, rz: 2} return gate_types def resource_params(self): @@ -212,3 +211,7 @@ def controlled_resource_decomp( return {re.ResourceCRot.resource_rep(): 1} raise re.ResourcesNotDefined + + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} diff --git a/pennylane/labs/resource_estimation/resource_tracking.py b/pennylane/labs/resource_estimation/resource_tracking.py index de93d687901..a682dc910bf 100644 --- a/pennylane/labs/resource_estimation/resource_tracking.py +++ b/pennylane/labs/resource_estimation/resource_tracking.py @@ -54,7 +54,6 @@ "Toffoli", } - # parameters for further configuration of the decompositions resource_config = { "error_rx": 10e-3, From c7b8a418688f2e093cbc2135684bedd930ee4947 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 12 Dec 2024 15:59:44 -0500 Subject: [PATCH 328/335] fix qchem ops --- .../resource_estimation/ops/qubit/qchem_ops.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py index 7fba43e9517..3d76830b747 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/qchem_ops.py @@ -32,7 +32,7 @@ class ResourceSingleExcitation(qml.SingleExcitation, re.ResourceOperator): """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): t_dag = re.ResourceAdjoint.resource_rep(re.ResourceT, {}) h = re.ResourceHadamard.resource_rep() s = re.ResourceS.resource_rep() @@ -58,7 +58,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) @@ -210,7 +210,7 @@ class ResourceDoubleExcitationMinus(qml.DoubleExcitationMinus, re.ResourceOperat """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): phase = re.ResourceGlobalPhase.resource_rep() double = re.ResourceDoubleExcitation.resource_rep() ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) @@ -228,7 +228,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) @@ -246,7 +246,7 @@ class ResourceDoubleExcitationPlus(qml.DoubleExcitationPlus, re.ResourceOperator """ @staticmethod - def _resource_decomp(*args, **kwargs): + def _resource_decomp(**kwargs): phase = re.ResourceGlobalPhase.resource_rep() double = re.ResourceDoubleExcitation.resource_rep() ctrl_z = re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 1, 0) @@ -264,7 +264,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) @@ -294,7 +294,7 @@ def resource_params(self): return {} @classmethod - def resource_rep(cls, **kwargs): + def resource_rep(cls): return re.CompressedResourceOp(cls, {}) From af3e7559827ffa9b97b3c31c74709ee1f554d285 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 13 Dec 2024 14:25:43 -0500 Subject: [PATCH 329/335] added tests for identity and controlled ops --- .../ops/op_math/controlled_ops.py | 16 +- .../ops/op_math/test_controlled_ops.py | 700 ++++++++++++++++++ .../resource_estimation/ops/test_identity.py | 111 +++ 3 files changed, 818 insertions(+), 9 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 2c152dd0354..9dd856437e0 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -510,7 +510,7 @@ def resource_rep( def adjoint_resource_decomp( cls, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return cls.resources(num_ctrl_wires, num_ctrl_values, num_work_wires) + return {cls.resource_rep(num_ctrl_wires, num_ctrl_values, num_work_wires): 1} @classmethod def controlled_resource_decomp( @@ -522,13 +522,11 @@ def controlled_resource_decomp( num_ctrl_values, num_work_wires, ) -> Dict[re.CompressedResourceOp, int]: - return { - cls.resource_rep( - outer_num_ctrl_wires + num_ctrl_wires, - outer_num_ctrl_values + num_ctrl_values, - outer_num_work_wires + num_work_wires, - ): 1 - } + return cls.resources( + outer_num_ctrl_wires + num_ctrl_wires, + outer_num_ctrl_values + num_ctrl_values, + outer_num_work_wires + num_work_wires, + ) @classmethod def pow_resource_decomp( @@ -748,7 +746,7 @@ def resource_rep(cls) -> re.CompressedResourceOp: @classmethod def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return cls.resources() + return {cls.resource_rep(): 1} @staticmethod def controlled_resource_decomp( diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 8c7114657a2..558f1dfe588 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -46,6 +46,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceHadamard, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceHadamard, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceHadamard, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCY: """Test the ResourceCY operation""" @@ -72,6 +135,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceY, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceY, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceY, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCZ: """Test the ResourceCZ operation""" @@ -97,6 +223,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceCCZ.resource_rep(): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCSWAP: """Test the ResourceCSWAP operation""" @@ -121,6 +310,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceSWAP, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceSWAP, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceSWAP, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCCZ: """Test the ResourceCZZ operation""" @@ -145,6 +397,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 3, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 4, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceZ, {}, 5, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCNOT: """Test ResourceCNOT operation""" @@ -286,6 +601,94 @@ def test_resource_params(self, op, params): expected_params = self._prep_params(*params) assert op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + op = re.ResourceMultiControlledX( + control_wires=[0, 1, 2, 3, 4], + wires=["t"], + control_values=[1, 0, 0, 1, 0], + work_wires=["w1"], + ) + + expected_res = {op.resource_rep(**op.resource_params()): 1} + op2 = re.ResourceAdjoint(op) + + assert op.adjoint_resource_decomp(**op.resource_params()) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceToffoli.resource_rep(): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["work1"], + { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceToffoli.resource_rep(): 1, + }, + ), + ( + ["c1", "c2", "c3", "c4"], + [1, 0, 0, 1], + ["work1", "work2"], + { + re.ResourceX.resource_rep(): 4, + re.ResourceCNOT.resource_rep(): 69, + }, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + op = re.ResourceMultiControlledX(control_wires=[0], wires=["t"], control_values=[1]) + + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params() + ) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {re.ResourceMultiControlledX.resource_rep(5, 3, 1): 1}), + (2, {}), + (5, {re.ResourceMultiControlledX.resource_rep(5, 3, 1): 1}), + (6, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op = re.ResourceMultiControlledX( + control_wires=[0, 1, 2, 3, 4], + wires=["t"], + control_values=[1, 0, 0, 1, 0], + work_wires=["w1"], + ) + + op2 = re.ResourcePow(op, z) + + assert op.pow_resource_decomp(z, **op.resource_params()) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCRX: """Test the ResourceCRX operation""" @@ -312,6 +715,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceRX, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceRX, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceRX, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {op.resource_rep(): 1}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCRY: """Test the ResourceCRY operation""" @@ -337,6 +803,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceRY, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceRY, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceRY, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {op.resource_rep(): 1}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCRZ: """Test the ResourceCRZ operation""" @@ -362,6 +891,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {op.resource_rep(): 1}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceCRot: """Test the ResourceCRot operation""" @@ -387,6 +979,69 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceRot, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceRot, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourceRot, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {op.resource_rep(): 1}), + (5, {op.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceControlledPhaseShift: """Test ResourceControlledPhaseShift""" @@ -466,6 +1121,51 @@ def test_pow_decomp(self, phi, wires): assert re.get_resources(op) == re.get_resources(pow) + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 2, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceControlled.resource_rep(re.ResourcePhaseShift, {}, 4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize("phi, wires", params) + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled( + self, phi, wires, ctrl_wires, ctrl_values, work_wires, expected_res + ): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceControlledPhaseShift(phi, wires) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + class TestCNOT: """Test ResourceCNOT""" diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py index c1cf7d04528..6e635df4a9e 100644 --- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -14,6 +14,8 @@ """ Tests for identity resource operators """ +import pytest + import pennylane.labs.resource_estimation as re # pylint: disable=no-self-use,use-implicit-booleaness-not-comparison @@ -47,6 +49,54 @@ def test_resources_from_rep(self): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + op = re.ResourceIdentity(0) + op2 = re.ResourceAdjoint(op) + assert op.adjoint_resource_decomp() == {} + assert op2.resources(**op2.resource_params()) == {} + + identity_ctrl_data = ( + ([1], [1], [], {}), + ([1, 2], [1, 1], ["w1"], {}), + ([1, 2, 3], [1, 0, 0], ["w1", "w2"], {}), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", identity_ctrl_data + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_work_wires = len(work_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + + op = re.ResourceIdentity(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + identity_pow_data = ( + (1, {}), + (2, {}), + (5, {}), + ) + + @pytest.mark.parametrize("z, expected_res", identity_pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op = re.ResourceIdentity(0) + assert op.pow_resource_decomp(z) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res + class TestGlobalPhase: """Test ResourceGlobalPhase""" @@ -75,3 +125,64 @@ def test_resources_from_rep(self): op_resource_type = op_compressed_rep.op_type op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + op = re.ResourceGlobalPhase(0.1, wires=0) + op2 = re.ResourceAdjoint(op) + assert op.adjoint_resource_decomp() == {} + assert op2.resources(**op2.resource_params()) == {} + + globalphase_ctrl_data = ( + ([1], [1], [], {re.ResourcePhaseShift.resource_rep(): 1}), + ([1, 2], [1, 1], ["w1"], {re.ResourcePhaseShift.resource_rep(): 1}), + ( + [1, 2, 3], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 3, 0, 2): 1, + re.ResourceX.resource_rep(): 4, + }, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", globalphase_ctrl_data + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceGlobalPhase(0.1, wires=0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + if num_ctrl_values == 0: + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + else: + with pytest.raises(re.ResourcesNotDefined): + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + + assert op2.resources(**op2.resource_params()) == expected_res + + globalphase_pow_data = ( + (1, {}), + (2, {}), + (5, {}), + ) + + @pytest.mark.parametrize("z, expected_res", globalphase_pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op = re.ResourceGlobalPhase(0.1, wires=0) + assert op.pow_resource_decomp(z) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res From c3a54259cd92e34ae81ffc01bb48fb002e47565a Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 13 Dec 2024 15:03:56 -0500 Subject: [PATCH 330/335] finish tests for controlled --- .../ops/op_math/controlled_ops.py | 2 +- .../ops/op_math/test_controlled_ops.py | 186 ++++++++++++------ 2 files changed, 129 insertions(+), 59 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py index 9dd856437e0..702dd2df1a0 100644 --- a/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py +++ b/pennylane/labs/resource_estimation/ops/op_math/controlled_ops.py @@ -419,7 +419,7 @@ def controlled_resource_decomp( ) -> Dict[re.CompressedResourceOp, int]: return { re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires + 2, num_ctrl_values + 2, num_work_wires + num_ctrl_wires + 2, num_ctrl_values, num_work_wires ): 1 } diff --git a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py index 558f1dfe588..2b367cb219f 100644 --- a/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/op_math/test_controlled_ops.py @@ -481,6 +481,70 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceToffoli.resource_rep(): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceMultiControlledX.resource_rep(3, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceMultiControlledX.resource_rep(4, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + (8, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceToffoli: """Test the ResourceToffoli operation""" @@ -510,6 +574,70 @@ def test_resource_params(self): expected_params = {} assert self.op.resource_params() == expected_params + def test_resource_adjoint(self): + """Test that the adjoint resources are as expected""" + expected_res = {self.op.resource_rep(): 1} + op2 = re.ResourceAdjoint(self.op) + + assert self.op.adjoint_resource_decomp() == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + + ctrl_data = ( + ( + ["c1"], + [1], + [], + {re.ResourceMultiControlledX.resource_rep(3, 0, 0): 1}, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + {re.ResourceMultiControlledX.resource_rep(4, 0, 1): 1}, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceMultiControlledX.resource_rep(5, 2, 2): 1}, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op2 = re.ResourceControlled( + self.op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + self.op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {op.resource_rep(): 1}), + (2, {}), + (5, {op.resource_rep(): 1}), + (8, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op2 = re.ResourcePow(self.op, z) + + assert self.op.pow_resource_decomp(z) == expected_res + assert op2.resources(**op2.resource_params()) == expected_res + class TestResourceMultiControlledX: """Test the ResourceMultiControlledX operation""" @@ -1165,61 +1293,3 @@ def test_resource_controlled( == expected_res ) assert op2.resources(**op2.resource_params()) == expected_res - - -class TestCNOT: - """Test ResourceCNOT""" - - def test_resources(self): - """Test that the resources method is not implemented""" - - op = re.ResourceCNOT([0, 1]) - with pytest.raises(re.ResourcesNotDefined): - op.resources() - - def test_resource_rep(self): - """Test the compressed representation""" - - op = re.ResourceCNOT([0, 1]) - expected = re.CompressedResourceOp(re.ResourceCNOT, {}) - assert op.resource_rep() == expected - - def test_resource_params(self): - """Test that the resource params are correct""" - op = re.ResourceCNOT([0, 1]) - assert op.resource_params() == {} - - def test_adjoint_decomp(self): - """Test that the adjoint decomposition is correct.""" - - expected = {re.ResourceCNOT.resource_rep(): 1} - assert re.ResourceCNOT.adjoint_resource_decomp() == expected - - cnot = re.ResourceCNOT([0, 1]) - cnot_dag = re.ResourceAdjoint(re.ResourceCNOT([0, 1])) - - r1 = re.get_resources(cnot) - r2 = re.get_resources(cnot_dag) - - assert r1 == r2 - - def test_controlled_decomp(self): - """Test that the controlled decomposition is correct.""" - - expected = {re.ResourceToffoli.resource_rep(): 1} - assert re.ResourceCNOT.controlled_resource_decomp(1, 0, 0) == expected - - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - - expected = {re.ResourceCNOT.resource_rep(): z % 2} - assert re.ResourceCNOT.pow_resource_decomp(z) == expected - - cnot = re.ResourceCNOT([0, 1]) - cnot_pow = re.ResourcePow(re.ResourceCNOT([0, 1]), z) - - r1 = re.get_resources(cnot) * (z % 2) - r2 = re.get_resources(cnot_pow) - - assert r1 == r2 From 416e8dfd0f4fd1027f54e9a969bd0d3be6847604 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 18 Dec 2024 14:13:31 -0500 Subject: [PATCH 331/335] fixing up the single qubit parameteric ops and tests --- .../labs/resource_estimation/ops/identity.py | 34 ++- .../ops/qubit/parametric_ops_single_qubit.py | 167 +++++++++-- .../ops/qubit/test_non_parametric_ops.py | 128 +++++---- .../qubit/test_parametric_ops_single_qubit.py | 260 +++++++++++++++++- .../resource_estimation/ops/test_identity.py | 37 +-- 5 files changed, 523 insertions(+), 103 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 6c95c3fd6d9..9f89c8653e1 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -65,16 +65,40 @@ def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: @staticmethod def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} + """The adjoint of a global phase is itself another global phase""" + return {re.ResourceGlobalPhase.resource_rep(): 1} @staticmethod def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_values == 0: - return {re.ResourcePhaseShift.resource_rep(): 1} - raise re.ResourcesNotDefined + """ + Resources: + The resources are generated from the identity that a global phase + controlled on a single qubit is equivalent to a local phase shift on that qubit. + + This idea can be generalized to a multi-qubit global phase by introducing one + 'clean' auxilliary qubit which gets reset at the end of the computation. In this + case, we apply sandwich the phase shift operation with two multi-controlled X gates. + """ + if num_ctrl_wires == 1: + gate_types = {re.ResourcePhaseShift.resource_rep(): 1} + + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + ps = re.ResourcePhaseShift.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + return {ps: 1, mcx: 2} @staticmethod def pow_resource_decomp(z) -> Dict[re.CompressedResourceOp, int]: - return {} + """Taking arbitrary powers of a global phase produces another global phase""" + return {re.ResourceGlobalPhase.resource_rep(): 1} diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 360d23ea272..1a0e4d59120 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -70,13 +70,28 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceControlledPhaseShift.resource_rep(): 1} - - raise re.ResourcesNotDefined + """ + The resources for a multi-controlled phase shift gate are generated using + the identity defined in (lemma 7.11) from https://arxiv.org/pdf/quant-ph/9503016. + """ + if num_ctrl_wires == 1: + gate_types = {re.ResourceControlledPhaseShift.resource_rep(): 1} + + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + c_ps = re.ResourceControlledPhaseShift.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {c_ps: 1, mcx: 2} @classmethod - def pow_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: return {cls.resource_rep(): 1} @@ -102,10 +117,41 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCRX.resource_rep(): 1} + """ + Resources: + The resources are taken from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. In combination with the following identity: + + .. math:: \hat{RX} = \hat{H} \cdot \hat{RZ} \cdot \hat{H}, + + we can express the :code:`CRX` gate as a :code:`CRZ` gate conjugated by :code:`Hadamard` gates. + The expression for controlled-RZ gates is used as defined in the reference above. By replacing + the :code:`X` gates with multi-controlled :code:`X` gates, we obtain a controlled-version + of that identity. + """ + if num_ctrl_wires == 1: + gate_types = {re.ResourceCRX.resource_rep(): 1} + + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + gate_types = {} + + h = re.ResourceHadamard.resource_rep() + rz = re.ResourceRZ.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) - raise re.ResourcesNotDefined + gate_types[mcx] = 2 + gate_types[rz] = 2 + gate_types[h] = 2 + + return gate_types @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -134,10 +180,33 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCRY.resource_rep(): 1} + """ + Resources: + The resources are taken from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. The resources are derived with the following identity: + + .. math:: \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}. + + By replacing the :code:`X` gates with multi-controlled :code:`X` gates, we obtain a controlled-version + of this identity. Thus we are able to constructively or destructively interfere the gates based on the + value of the control qubits. + """ + if num_ctrl_wires == 1: + gate_types = {re.ResourceCRY.resource_rep(): 1} + + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 - raise re.ResourcesNotDefined + return gate_types + + ry = re.ResourceRY.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + return {ry: 2, mcx: 2} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -171,10 +240,32 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCRZ.resource_rep(): 1} + """ + The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. They are derived from the following identity: + + .. math:: \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}. + + By replacing the :code:`X` gates with multi-controlled :code:`X` gates, we obtain a controlled-version of + this identity. Thus we are able to constructively or destructively interfere the gates based on the value + of the control qubits. + """ + if num_ctrl_wires == 1: + gate_types = {re.ResourceCRZ.resource_rep(): 1} - raise re.ResourcesNotDefined + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + rz = re.ResourceRZ.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + return {rz: 2, mcx: 2} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -207,10 +298,52 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCRot.resource_rep(): 1} + """ + Resources: + The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit + unitary `_. The resources are derived with the following identities: + + .. math:: + + \begin{align} + \hat{RZ}(\theta) = \hat{X} \cdot \hat{RZ}(- \theta) \cdot \hat{X}, \\ + \hat{RY}(\theta) = \hat{X} \cdot \hat{RY}(- \theta) \cdot \hat{X}. + \end{align} + + This identity is applied along with some clever choices for the angle values to combine rotation; + the final circuit takes the form: + + .. code-block:: bash + + ctrl: ─────╭●─────────╭●─────────┤ + trgt: ──RZ─╰X──RZ──RY─╰X──RY──RZ─┤ + + The :code:`CNOT` gates are replaced with multi-controlled X gates to generalize to the multi-controlled case. - raise re.ResourcesNotDefined + """ + if num_ctrl_wires == 1: + gate_types = {re.ResourceCRot.resource_rep(): 1} + + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + gate_types = {} + + rz = re.ResourceRZ.resource_rep() + ry = re.ResourceRY.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + + gate_types[mcx] = 2 + gate_types[rz] = 3 + gate_types[ry] = 2 + + return gate_types @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index ca888e4e8f1..b46b76a9bd7 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -59,19 +59,21 @@ def test_controlled_decomp(self): r2 = re.get_resources(ch) assert r1 == r2 - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): + pow_data = ( + (1, {re.ResourceHadamard.resource_rep(): 1}), + (2, {}), + (3, {re.ResourceHadamard.resource_rep(): 1}), + (4, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): """Test that the pow decomposition is correct.""" - expected = {re.ResourceHadamard.resource_rep(): z % 2} - assert re.ResourceHadamard.pow_resource_decomp(z) == expected - - h = re.ResourceHadamard(0) - pow_h = re.ResourcePow(re.ResourceHadamard(0), z) + op = re.ResourceHadamard(0) + assert op.pow_resource_decomp(z) == expected_res - r1 = re.get_resources(h) * (z % 2) - r2 = re.get_resources(pow_h) - - assert r1 == r2 + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res class TestSWAP: @@ -125,19 +127,21 @@ def test_controlled_decomp(self): r2 = re.get_resources(cswap) assert r1 == r2 - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceSWAP.resource_rep(): z % 2} - assert re.ResourceSWAP.pow_resource_decomp(z) == expected - - swap = re.ResourceSWAP([0, 1]) - pow_swap = re.ResourcePow(re.ResourceSWAP([0, 1]), z) + pow_data = ( + (1, {re.ResourceSWAP.resource_rep(): 1}), + (2, {}), + (3, {re.ResourceSWAP.resource_rep(): 1}), + (4, {}), + ) - r1 = re.get_resources(swap) * (z % 2) - r2 = re.get_resources(pow_swap) + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): + """Test that the pow decomposition is correct.""" + op = re.ResourceSWAP([0, 1]) + assert op.pow_resource_decomp(z) == expected_res - assert r1 == r2 + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res class TestS: @@ -182,19 +186,25 @@ def test_adjoint_decomposition(self): r2 = re.get_resources(s_dag) assert r1 == r2 - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): + pow_data = ( + (1, {re.ResourceS.resource_rep(): 1}), + (2, {re.ResourceS.resource_rep(): 2}), + (3, {re.ResourceS.resource_rep(): 3}), + (4, {}), + (7, {re.ResourceS.resource_rep(): 3}), + (8, {}), + (14, {re.ResourceS.resource_rep(): 2}), + (15, {re.ResourceS.resource_rep(): 3}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): """Test that the pow decomposition is correct.""" - expected = {re.ResourceS.resource_rep(): z % 4} - assert re.ResourceS.pow_resource_decomp(z) == expected - - s = re.ResourceS(0) - pow_s = re.ResourcePow(s, z) - - r1 = re.get_resources(s) * (z % 4) - r2 = re.get_resources(pow_s) + op = re.ResourceS(0) + assert op.pow_resource_decomp(z) == expected_res - assert r1 == r2 + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res class TestT: @@ -228,19 +238,25 @@ def test_adjoint_decomposition(self): r2 = re.get_resources(t_dag) assert r1 == r2 - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): + pow_data = ( + (1, {re.ResourceT.resource_rep(): 1}), + (2, {re.ResourceT.resource_rep(): 2}), + (3, {re.ResourceT.resource_rep(): 3}), + (7, {re.ResourceT.resource_rep(): 7}), + (8, {}), + (14, {re.ResourceT.resource_rep(): 6}), + (15, {re.ResourceT.resource_rep(): 7}), + (16, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): """Test that the pow decomposition is correct.""" - expected = {re.ResourceT.resource_rep(): z % 8} - assert re.ResourceT.pow_resource_decomp(z) == expected - - t = re.ResourceT(0) - pow_t = re.ResourcePow(t, z) - - r1 = re.get_resources(t) * (z % 8) - r2 = re.get_resources(pow_t) + op = re.ResourceT(0) + assert op.pow_resource_decomp(z) == expected_res - assert r1 == r2 + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res class TestX: @@ -293,16 +309,18 @@ def test_adjoint_decomposition(self): r2 = re.get_resources(x_dag) assert r1 == r2 - @pytest.mark.parametrize("z", list(range(10))) - def test_pow_decomp(self, z): - """Test that the pow decomposition is correct.""" - expected = {re.ResourceX.resource_rep(): z % 2} - assert re.ResourceX.pow_resource_decomp(z) == expected - - x = re.ResourceX(0) - pow_x = re.ResourcePow(x, z) + pow_data = ( + (1, {re.ResourceX.resource_rep(): 1}), + (2, {}), + (3, {re.ResourceX.resource_rep(): 1}), + (4, {}), + ) - r1 = re.get_resources(x) * (z % 2) - r2 = re.get_resources(pow_x) + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): + """Test that the pow decomposition is correct.""" + op = re.ResourceX(0) + assert op.pow_resource_decomp(z) == expected_res - assert r1 == r2 + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py index 35930036102..07199ab2a77 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_single_qubit.py @@ -14,6 +14,8 @@ """ Tests for parametric single qubit resource operators. """ +import copy + import pytest import pennylane.labs.resource_estimation as re @@ -41,6 +43,18 @@ class TestPauliRotation: params_classes = [re.ResourceRX, re.ResourceRY, re.ResourceRZ] params_errors = [10e-3, 10e-4, 10e-5] + params_ctrl_res = [ + { + re.ResourceHadamard.resource_rep(): 2, + re.ResourceRZ.resource_rep(): 2, + }, + { + re.ResourceRY.resource_rep(): 2, + }, + { + re.ResourceRZ.resource_rep(): 2, + }, + ] @pytest.mark.parametrize("resource_class", params_classes) @pytest.mark.parametrize("epsilon", params_errors) @@ -121,19 +135,24 @@ def test_pow_decomposition(self, resource_class, epsilon, z): assert r1 == r2 - params_classes = ( + params_ctrl_classes = ( (re.ResourceRX, re.ResourceCRX), (re.ResourceRY, re.ResourceCRY), (re.ResourceRZ, re.ResourceCRZ), ) - @pytest.mark.parametrize("resource_class, controlled_class", params_classes) + @pytest.mark.parametrize("resource_class, controlled_class", params_ctrl_classes) @pytest.mark.parametrize("epsilon", params_errors) - def test_controlled_decomposition(self, resource_class, controlled_class, epsilon): + def test_controlled_decomposition_single_control( + self, resource_class, controlled_class, epsilon + ): """Test that the controlled decompositions are correct.""" expected = {controlled_class.resource_rep(): 1} assert resource_class.controlled_resource_decomp(1, 0, 0) == expected + expected = {controlled_class.resource_rep(): 1, re.ResourceX.resource_rep(): 2} + assert resource_class.controlled_resource_decomp(1, 1, 0) == expected + op = resource_class(1.24, wires=0) c_op = re.ResourceControlled(op, control_wires=[1]) @@ -146,6 +165,51 @@ def test_controlled_decomposition(self, resource_class, controlled_class, epsilo assert r1 == r2 + ctrl_res_data = ( + ( + [1, 2], + [1, 1], + ["w1"], + {re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2}, + ), + ( + [1, 2], + [1, 0], + [], + {re.ResourceMultiControlledX.resource_rep(2, 1, 0): 2}, + ), + ( + [1, 2, 3], + [1, 0, 0], + ["w1", "w2"], + {re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2}, + ), + ) + + @pytest.mark.parametrize("resource_class, local_res", zip(params_classes, params_ctrl_res)) + @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, general_res", ctrl_res_data) + def test_controlled_decomposition_multi_controlled( + self, resource_class, local_res, ctrl_wires, ctrl_values, work_wires, general_res + ): + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = resource_class(1.23, wires=0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + expected_resources = copy.copy(local_res) + for k, v in general_res.items(): + expected_resources[k] = v + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_resources + ) + assert op2.resources(**op2.resource_params()) == expected_resources + class TestRot: """Test ResourceRot""" @@ -153,10 +217,9 @@ class TestRot: def test_resources(self): """Test the resources method""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) - rx = re.ResourceRX.resource_rep() ry = re.ResourceRY.resource_rep() rz = re.ResourceRZ.resource_rep() - expected = {rx: 1, ry: 1, rz: 1} + expected = {ry: 1, rz: 2} assert op.resources() == expected @@ -169,10 +232,9 @@ def test_resource_rep(self): def test_resources_from_rep(self): """Test that the resources can be obtained from the compact representation""" op = re.ResourceRot(0.1, 0.2, 0.3, wires=0) - rx = re.CompressedResourceOp(re.ResourceRX, {}) - ry = re.CompressedResourceOp(re.ResourceRY, {}) - rz = re.CompressedResourceOp(re.ResourceRZ, {}) - expected = {rx: 1, ry: 1, rz: 1} + ry = re.ResourceRY.resource_rep() + rz = re.ResourceRZ.resource_rep() + expected = {ry: 1, rz: 2} op_compressed_rep = op.resource_rep_from_op() op_resource_type = op_compressed_rep.op_type @@ -197,3 +259,183 @@ def test_adjoint_decomp(self): r2 = re.get_resources(dag) assert r1 == r2 + + ctrl_data = ( + ([1], [1], [], {re.ResourceCRot.resource_rep(): 1}), + ( + [1], + [0], + [], + { + re.ResourceCRot.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + [1, 2], + [1, 1], + ["w1"], + { + re.ResourceRZ.resource_rep(): 3, + re.ResourceRY.resource_rep(): 2, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), + ( + [1, 2, 3], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceRZ.resource_rep(): 3, + re.ResourceRY.resource_rep(): 2, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) + + @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceRot(1.24, 1.25, 1.26, wires=0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {re.ResourceRot.resource_rep(): 1}), + (2, {re.ResourceRot.resource_rep(): 1}), + (5, {re.ResourceRot.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op = re.ResourceRot(1.24, 1.25, 1.26, wires=0) + assert op.pow_resource_decomp(z) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res + + +class TestPhaseShift: + + def test_resources(self): + """Test the resources method""" + op = re.ResourcePhaseShift(0.1, wires=0) + rz = re.ResourceRZ.resource_rep() + global_phase = re.ResourceGlobalPhase.resource_rep() + + expected = {rz: 1, global_phase: 1} + + assert op.resources() == expected + + def test_resource_rep(self): + """Test the compressed representation""" + op = re.ResourcePhaseShift(0.1, wires=0) + expected = re.CompressedResourceOp(re.ResourcePhaseShift, {}) + assert op.resource_rep() == expected + + def test_resources_from_rep(self): + """Test that the resources can be obtained from the compact representation""" + op = re.ResourcePhaseShift(0.1, wires=0) + global_phase = re.ResourceGlobalPhase.resource_rep() + rz = re.ResourceRZ.resource_rep() + expected = {global_phase: 1, rz: 1} + + op_compressed_rep = op.resource_rep_from_op() + op_resource_type = op_compressed_rep.op_type + op_resource_params = op_compressed_rep.params + assert op_resource_type.resources(**op_resource_params) == expected + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourcePhaseShift(0.1, wires=0) + assert op.resource_params() == {} + + def test_adjoint_decomp(self): + """Test that the adjoint decomposition is correct""" + + expected = {re.ResourcePhaseShift.resource_rep(): 1} + assert re.ResourcePhaseShift.adjoint_resource_decomp() == expected + + op = re.ResourcePhaseShift(0.1, wires=0) + dag = re.ResourceAdjoint(op) + + r1 = re.get_resources(op) + r2 = re.get_resources(dag) + + assert r1 == r2 + + ctrl_data = ( + ([1], [1], [], {re.ResourceControlledPhaseShift.resource_rep(): 1}), + ( + [1], + [0], + [], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + [1, 2], + [1, 1], + ["w1"], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), + ( + [1, 2, 3], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) + + @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourcePhaseShift(0.1, wires=0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {re.ResourcePhaseShift.resource_rep(): 1}), + (2, {re.ResourcePhaseShift.resource_rep(): 1}), + (5, {re.ResourcePhaseShift.resource_rep(): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_resource_pow(self, z, expected_res): + """Test that the pow resources are as expected""" + op = re.ResourcePhaseShift(0.1, wires=0) + assert op.pow_resource_decomp(z) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py index 6e635df4a9e..d89d4143b63 100644 --- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -130,19 +130,27 @@ def test_resource_adjoint(self): """Test that the adjoint resources are as expected""" op = re.ResourceGlobalPhase(0.1, wires=0) op2 = re.ResourceAdjoint(op) - assert op.adjoint_resource_decomp() == {} - assert op2.resources(**op2.resource_params()) == {} + assert op.adjoint_resource_decomp() == {re.ResourceGlobalPhase.resource_rep(): 1} + assert op2.resources(**op2.resource_params()) == {re.ResourceGlobalPhase.resource_rep(): 1} globalphase_ctrl_data = ( ([1], [1], [], {re.ResourcePhaseShift.resource_rep(): 1}), - ([1, 2], [1, 1], ["w1"], {re.ResourcePhaseShift.resource_rep(): 1}), + ( + [1, 2], + [1, 1], + ["w1"], + { + re.ResourcePhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), ( [1, 2, 3], [1, 0, 0], ["w1", "w2"], { - re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 3, 0, 2): 1, - re.ResourceX.resource_rep(): 4, + re.ResourcePhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, }, ), ) @@ -161,21 +169,16 @@ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires ) - if num_ctrl_values == 0: - assert ( - op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) - == expected_res - ) - else: - with pytest.raises(re.ResourcesNotDefined): - op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) - + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) assert op2.resources(**op2.resource_params()) == expected_res globalphase_pow_data = ( - (1, {}), - (2, {}), - (5, {}), + (1, {re.ResourceGlobalPhase.resource_rep(): 1}), + (2, {re.ResourceGlobalPhase.resource_rep(): 1}), + (5, {re.ResourceGlobalPhase.resource_rep(): 1}), ) @pytest.mark.parametrize("z, expected_res", globalphase_pow_data) From da61e3ad5ebd75f8af33acaa7b23c8fc618fd445 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 18 Dec 2024 17:12:55 -0500 Subject: [PATCH 332/335] added multi-control decomps for non-parameteric ops and tests --- .../ops/qubit/non_parametric_ops.py | 147 ++++- .../ops/qubit/test_non_parametric_ops.py | 569 +++++++++++++++++- 2 files changed, 654 insertions(+), 62 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py index 1d29f2db011..9bd5bde072f 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py +++ b/pennylane/labs/resource_estimation/ops/qubit/non_parametric_ops.py @@ -42,10 +42,21 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCH.resource_rep(): 1} + if num_ctrl_wires == 1: + gate_types = {re.ResourceCH.resource_rep(): 1} - raise re.ResourcesNotDefined + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + ch = re.ResourceCH.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {ch: 1, mcx: 2} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -78,10 +89,21 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceControlledPhaseShift.resource_rep(): 1} + if num_ctrl_wires == 1: + gate_types = {re.ResourceControlledPhaseShift.resource_rep(): 1} - raise re.ResourcesNotDefined + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + cs = re.ResourceControlledPhaseShift.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {cs: 1, mcx: 2} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -148,10 +170,21 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCSWAP.resource_rep(): 1} + if num_ctrl_wires == 1: + gate_types = {re.ResourceCSWAP.resource_rep(): 1} - raise re.ResourcesNotDefined + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + cnot = re.ResourceCNOT.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {cnot: 2, mcx: 1} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -181,10 +214,21 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceControlledPhaseShift.resource_rep(): 1} + if num_ctrl_wires == 1: + gate_types = {re.ResourceControlledPhaseShift.resource_rep(): 1} - raise re.ResourcesNotDefined + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + ct = re.ResourceControlledPhaseShift.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {ct: 1, mcx: 2} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -221,16 +265,24 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: @staticmethod def controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires): - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCNOT.resource_rep(): 1} - if num_ctrl_wires == 2 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceToffoli.resource_rep(): 1} + if num_ctrl_wires > 2: + return { + re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires, num_ctrl_values, num_work_wires + ): 1 + } + + gate_types = {} + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 * num_ctrl_values - return { - re.ResourceMultiControlledX.resource_rep( - num_ctrl_wires, num_ctrl_values, num_work_wires - ): 1 - } + if num_ctrl_wires == 1: + gate_types[re.ResourceCNOT.resource_rep()] = 1 + + if num_ctrl_wires == 2: + gate_types[re.ResourceToffoli.resource_rep()] = 1 + + return gate_types @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -244,6 +296,19 @@ class ResourceY(qml.Y, re.ResourceOperator): @staticmethod def _resource_decomp(**kwargs) -> Dict[re.CompressedResourceOp, int]: + """ + The resources are defined using the identity: + + .. math:: + + \begin{align} + \hat{Y} &= \hat{S} \cdot \hat{X} \cdot \hat{S}^{\dagger}, \\ + \hat{X} &= \hat{H} \cdot \hat{Z} \cdot \hat{H}, \\ + \hat{Z} &= \hat{S}^{2}, \\ + \hat{S}^{\dagger} &= 3 \hat{S}. + \end{align} + + """ s = re.ResourceS.resource_rep() h = re.ResourceHadamard.resource_rep() @@ -268,10 +333,21 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCY.resource_rep(): 1} + if num_ctrl_wires == 1: + gate_types = {re.ResourceCY.resource_rep(): 1} - raise re.ResourcesNotDefined + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 + + return gate_types + + cy = re.ResourceCY.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {cy: 1, mcx: 2} @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: @@ -307,13 +383,26 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_wires == 1 and num_ctrl_values == 0 and num_work_wires == 0: - return {re.ResourceCZ.resource_rep(): 1} + if num_ctrl_wires > 2: + cz = re.ResourceCZ.resource_rep() + mcx = re.ResourceMultiControlledX.resource_rep( + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) + return {cz: 1, mcx: 2} - if num_ctrl_wires == 2 and num_ctrl_wires == 0 and num_work_wires == 0: - return {re.ResourceCCZ.resource_rep(): 1} + gate_types = {} + if num_ctrl_wires == 1: + gate_types[re.ResourceCZ.resource_rep()] = 1 - raise re.ResourcesNotDefined + if num_ctrl_wires == 2: + gate_types[re.ResourceCCZ.resource_rep()] = 1 + + if num_ctrl_values: + gate_types[re.ResourceX.resource_rep()] = 2 * num_ctrl_values + + return gate_types @classmethod def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index b46b76a9bd7..9c85b6acb6f 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -47,17 +47,64 @@ def test_adjoint_decomp(self): assert re.get_resources(h) == re.get_resources(h_dag) - def test_controlled_decomp(self): - """Test that the controlled decomposition is correct.""" - expected = {re.ResourceCH.resource_rep(): 1} - assert re.ResourceHadamard.controlled_resource_decomp(1, 0, 0) == expected + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceCH.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceCH.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceCH.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceCH.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) - controlled_h = re.ResourceControlled(re.ResourceHadamard(0), control_wires=[1]) - ch = re.ResourceCH([0, 1]) + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) - r1 = re.get_resources(controlled_h) - r2 = re.get_resources(ch) - assert r1 == r2 + op = re.ResourceHadamard(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res pow_data = ( (1, {re.ResourceHadamard.resource_rep(): 1}), @@ -115,17 +162,64 @@ def test_adjoint_decomp(self): assert re.get_resources(swap) == re.get_resources(swap_dag) - def test_controlled_decomp(self): - """Test that the controlled decomposition is correct.""" - expected = {re.ResourceCSWAP.resource_rep(): 1} - assert re.ResourceSWAP.controlled_resource_decomp(1, 0, 0) == expected + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceCSWAP.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceCSWAP.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 1, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceCNOT.resource_rep(): 2, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 1, + }, + ), + ) - controlled_swap = re.ResourceControlled(re.ResourceSWAP([0, 1]), control_wires=[2]) - cswap = re.ResourceCSWAP([0, 1, 2]) + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) - r1 = re.get_resources(controlled_swap) - r2 = re.get_resources(cswap) - assert r1 == r2 + op = re.ResourceSWAP([0, 1]) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res pow_data = ( (1, {re.ResourceSWAP.resource_rep(): 1}), @@ -206,6 +300,65 @@ def test_pow_decomp(self, z, expected_res): op2 = re.ResourcePow(op, z) assert op2.resources(**op2.resource_params()) == expected_res + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceS(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + class TestT: """Tests for ResourceT""" @@ -238,6 +391,65 @@ def test_adjoint_decomposition(self): r2 = re.get_resources(t_dag) assert r1 == r2 + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceControlledPhaseShift.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceT(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + pow_data = ( (1, {re.ResourceT.resource_rep(): 1}), (2, {re.ResourceT.resource_rep(): 2}), @@ -260,7 +472,7 @@ def test_pow_decomp(self, z, expected_res): class TestX: - """Tests for ResourceX""" + """Tests for the ResourceX gate""" def test_resources(self): """Test that ResourceT does not implement a decomposition""" @@ -280,22 +492,79 @@ def test_resource_rep(self): expected = re.CompressedResourceOp(re.ResourceX, {}) assert re.ResourceX.resource_rep() == expected - def test_single_controlled_resources(self): - """Test that the controlled_resource_decomp method dispatches correctly.""" - controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1]) - - cnot = re.ResourceCNOT([0, 1]) - assert re.get_resources(controlled_op) == re.get_resources(cnot) - - def test_double_controlled_resources(self): - """Test that the controlled_resource_decomp method dispatches correctly.""" - controlled_op = re.ResourceControlled(re.ResourceX(0), control_wires=[1, 2]) - expected_op = re.ResourceToffoli([0, 1, 2]) + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceCNOT.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceCNOT.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceToffoli.resource_rep(): 1, + }, + ), + ( + ["c1", "c2"], + [0, 0], + ["w1"], + { + re.ResourceToffoli.resource_rep(): 1, + re.ResourceX.resource_rep(): 4, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 1, + }, + ), + ( + ["c1", "c2", "c3", "c4"], + [1, 0, 0, 1], + ["w1", "w2"], + { + re.ResourceMultiControlledX.resource_rep(4, 2, 2): 1, + }, + ), + ) - r1 = re.get_resources(controlled_op) - r2 = re.get_resources(expected_op) + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) - assert r1 == r2 + op = re.ResourceX(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res def test_adjoint_decomposition(self): """Test that the adjoint resources are correct.""" @@ -324,3 +593,237 @@ def test_pow_decomp(self, z, expected_res): op2 = re.ResourcePow(op, z) assert op2.resources(**op2.resource_params()) == expected_res + + +class TestY: + """Tests for the ResourceY gate""" + + def test_resources(self): + """Test that ResourceT does not implement a decomposition""" + expected = { + re.ResourceS.resource_rep(): 6, + re.ResourceHadamard.resource_rep(): 2, + } + assert re.ResourceY.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceY(0) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compact representation is correct""" + expected = re.CompressedResourceOp(re.ResourceY, {}) + assert re.ResourceY.resource_rep() == expected + + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceCY.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceCY.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceCY.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 0, 1): 2, + }, + ), + ( + ["c1", "c2"], + [0, 0], + ["w1"], + { + re.ResourceCY.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(2, 2, 1): 2, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceCY.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceY(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceY.resource_rep(): 1} + assert re.ResourceY.adjoint_resource_decomp() == expected + + y = re.ResourceY(0) + y_dag = re.ResourceAdjoint(y) + + r1 = re.get_resources(y) + r2 = re.get_resources(y_dag) + assert r1 == r2 + + pow_data = ( + (1, {re.ResourceY.resource_rep(): 1}), + (2, {}), + (3, {re.ResourceY.resource_rep(): 1}), + (4, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): + """Test that the pow decomposition is correct.""" + op = re.ResourceY(0) + assert op.pow_resource_decomp(z) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res + + +class TestZ: + """Tests for the ResourceY gate""" + + def test_resources(self): + """Test that ResourceT does not implement a decomposition""" + expected = { + re.ResourceS.resource_rep(): 2, + } + assert re.ResourceZ.resources() == expected + + def test_resource_params(self): + """Test that the resource params are correct""" + op = re.ResourceZ(0) + assert op.resource_params() == {} + + def test_resource_rep(self): + """Test that the compact representation is correct""" + expected = re.CompressedResourceOp(re.ResourceZ, {}) + assert re.ResourceZ.resource_rep() == expected + + ctrl_data = ( + ( + ["c1"], + [1], + [], + { + re.ResourceCZ.resource_rep(): 1, + }, + ), + ( + ["c1"], + [0], + [], + { + re.ResourceCZ.resource_rep(): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + ["c1", "c2"], + [1, 1], + ["w1"], + { + re.ResourceCCZ.resource_rep(): 1, + }, + ), + ( + ["c1", "c2"], + [0, 0], + ["w1"], + { + re.ResourceCCZ.resource_rep(): 1, + re.ResourceX.resource_rep(): 4, + }, + ), + ( + ["c1", "c2", "c3"], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceCZ.resource_rep(): 1, + re.ResourceMultiControlledX.resource_rep(3, 2, 2): 2, + }, + ), + ) + + @pytest.mark.parametrize( + "ctrl_wires, ctrl_values, work_wires, expected_res", + ctrl_data, + ) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceZ(0) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp(num_ctrl_wires, num_ctrl_values, num_work_wires) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + def test_adjoint_decomposition(self): + """Test that the adjoint resources are correct.""" + expected = {re.ResourceZ.resource_rep(): 1} + assert re.ResourceZ.adjoint_resource_decomp() == expected + + z = re.ResourceZ(0) + z_dag = re.ResourceAdjoint(z) + + r1 = re.get_resources(z) + r2 = re.get_resources(z_dag) + assert r1 == r2 + + pow_data = ( + (1, {re.ResourceZ.resource_rep(): 1}), + (2, {}), + (3, {re.ResourceZ.resource_rep(): 1}), + (4, {}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): + """Test that the pow decomposition is correct.""" + op = re.ResourceZ(0) + assert op.pow_resource_decomp(z) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res From 51115aa133a43f4c562a62e424315b7a2a246d50 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Wed, 18 Dec 2024 17:14:26 -0500 Subject: [PATCH 333/335] fixed docstrings --- .../ops/qubit/parametric_ops_single_qubit.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py index 1a0e4d59120..9acd9f3a11c 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_single_qubit.py @@ -70,7 +70,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - """ + r""" The resources for a multi-controlled phase shift gate are generated using the identity defined in (lemma 7.11) from https://arxiv.org/pdf/quant-ph/9503016. """ @@ -117,7 +117,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - """ + r""" Resources: The resources are taken from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. In combination with the following identity: @@ -180,7 +180,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - """ + r""" Resources: The resources are taken from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. The resources are derived with the following identity: @@ -240,7 +240,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - """ + r""" The resources are obtained from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. They are derived from the following identity: @@ -298,7 +298,7 @@ def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: def controlled_resource_decomp( num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - """ + r""" Resources: The resources are derived from (in figure 1b.) the paper `T-count and T-depth of any multi-qubit unitary `_. The resources are derived with the following identities: From 40b693d52b0788dc4667666011749febd50f082d Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 19 Dec 2024 09:58:27 -0500 Subject: [PATCH 334/335] finished adding tests --- .../ops/qubit/parametric_ops_multi_qubit.py | 89 +++---- .../qubit/test_parametric_ops_multi_qubit.py | 243 +++++++++++++++++- 2 files changed, 287 insertions(+), 45 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py index e9446c2ef5b..56c0bb88716 100644 --- a/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py +++ b/pennylane/labs/resource_estimation/ops/qubit/parametric_ops_multi_qubit.py @@ -50,8 +50,8 @@ def resource_rep(cls, num_wires): return re.CompressedResourceOp(cls, {"num_wires": num_wires}) @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} + def adjoint_resource_decomp(cls, num_wires) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(num_wires=num_wires): 1} @staticmethod def controlled_resource_decomp( @@ -79,8 +79,8 @@ def controlled_resource_decomp( raise re.ResourcesNotDefined @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} + def pow_resource_decomp(cls, z, num_wires) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(num_wires=num_wires): 1} class ResourcePauliRot(qml.PauliRot, re.ResourceOperator): @@ -148,8 +148,8 @@ def resource_rep(cls, pauli_word): return re.CompressedResourceOp(cls, {"pauli_word": pauli_word}) @classmethod - def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} + def adjoint_resource_decomp(cls, pauli_word) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(pauli_word=pauli_word): 1} @staticmethod def controlled_resource_decomp( @@ -158,54 +158,55 @@ def controlled_resource_decomp( num_work_wires, pauli_word, ) -> Dict[re.CompressedResourceOp, int]: - if num_ctrl_values == 0: - if set(pauli_word) == {"I"}: - ctrl_gp = re.ResourceControlled.resource_rep( - re.ResourceGlobalPhase, - {}, - num_ctrl_wires, - num_ctrl_values, - num_work_wires, - ) - return {ctrl_gp: 1} - - active_wires = len(pauli_word.replace("I", "")) - - h = re.ResourceHadamard.resource_rep() - s = re.ResourceS.resource_rep() - s_dagg = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) - cnot = re.ResourceCNOT.resource_rep() - ctrl_rz = re.ResourceControlled.resource_rep( - base_class=re.ResourceRZ, - base_params={}, - num_ctrl_wires=num_ctrl_wires, - num_ctrl_values=num_ctrl_values, - num_work_wires=num_work_wires, + if set(pauli_word) == {"I"}: + ctrl_gp = re.ResourceControlled.resource_rep( + re.ResourceGlobalPhase, + {}, + num_ctrl_wires, + num_ctrl_values, + num_work_wires, ) + return {ctrl_gp: 1} + + active_wires = len(pauli_word.replace("I", "")) + + h = re.ResourceHadamard.resource_rep() + s = re.ResourceS.resource_rep() + s_dagg = re.ResourceAdjoint.resource_rep(re.ResourceS, {}) + cnot = re.ResourceCNOT.resource_rep() + ctrl_rz = re.ResourceControlled.resource_rep( + base_class=re.ResourceRZ, + base_params={}, + num_ctrl_wires=num_ctrl_wires, + num_ctrl_values=num_ctrl_values, + num_work_wires=num_work_wires, + ) - h_count = 0 - s_count = 0 + h_count = 0 + s_count = 0 - for gate in pauli_word: - if gate == "X": - h_count += 2 - if gate == "Y": - h_count += 2 - s_count += 1 + for gate in pauli_word: + if gate == "X": + h_count += 2 + if gate == "Y": + h_count += 2 + s_count += 1 - gate_types = {} + gate_types = {} + if h_count: gate_types[h] = h_count + if s_count: gate_types[s] = s_count gate_types[s_dagg] = s_count - gate_types[ctrl_rz] = 1 - gate_types[cnot] = 2 * (active_wires - 1) - return gate_types - raise re.ResourcesNotDefined + gate_types[ctrl_rz] = 1 + gate_types[cnot] = 2 * (active_wires - 1) + + return gate_types @classmethod - def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: - return {cls.resource_rep(): 1} + def pow_resource_decomp(cls, z, pauli_word) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(pauli_word=pauli_word): 1} class ResourceIsingXX(qml.IsingXX, re.ResourceOperator): diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py index 2d47dbd5a3e..bf7e9f3263d 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_parametric_ops_multi_qubit.py @@ -58,6 +58,102 @@ def test_resources_from_rep(self, num_wires): op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + @pytest.mark.parametrize("num_wires", range(1, 5)) + def test_adjoint_decomp(self, num_wires): + """Test that the adjoint decomposition is correct.""" + expected = {re.ResourceMultiRZ.resource_rep(num_wires=num_wires): 1} + assert re.ResourceMultiRZ.adjoint_resource_decomp(num_wires=num_wires) == expected + + multi_rz = re.ResourceMultiRZ(0.123, wires=range(num_wires)) + multi_rz_dag = re.ResourceAdjoint(multi_rz) + + assert re.get_resources(multi_rz) == re.get_resources(multi_rz_dag) + + ctrl_data = ( + ( + [1], + [1], + [], + { + re.ResourceCNOT.resource_rep(): 4, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 0, 0): 1, + }, + ), + ( + [1], + [0], + [], + { + re.ResourceControlled.resource_rep( + re.ResourceMultiRZ, {"num_wires": 3}, 1, 0, 0 + ): 1, + re.ResourceX.resource_rep(): 2, + }, + ), + ( + [1, 2], + [1, 1], + ["w1"], + { + re.ResourceCNOT.resource_rep(): 4, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 1): 1, + }, + ), + ( + [1, 2, 3], + [1, 0, 0], + ["w1", "w2"], + { + re.ResourceControlled.resource_rep( + re.ResourceMultiRZ, {"num_wires": 3}, 3, 0, 2 + ): 1, + re.ResourceX.resource_rep(): 4, + }, + ), + ) + + @pytest.mark.parametrize("ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data) + def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected_res): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourceMultiRZ(1.24, wires=range(5, 8)) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + if num_ctrl_values != 0: + with pytest.raises(re.ResourcesNotDefined): + op.controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params() + ) + else: + assert ( + op.controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params() + ) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + pow_data = ( + (1, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}), + (2, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}), + (3, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}), + (4, {re.ResourceMultiRZ.resource_rep(num_wires=4): 1}), + ) + + @pytest.mark.parametrize("z, expected_res", pow_data) + def test_pow_decomp(self, z, expected_res): + """Test that the pow decomposition is correct.""" + op = re.ResourceMultiRZ(1.23, wires=range(4)) + assert op.pow_resource_decomp(z, **op.resource_params()) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res + class TestPauliRot: """Test the ResourcePauliRot class.""" @@ -90,7 +186,8 @@ def test_resources(self, pauli_word, expected_h_count, expected_s_count): else: expected = { re.ResourceHadamard.resource_rep(): expected_h_count, - re.ResourceS.resource_rep(): 4 * expected_s_count, + re.ResourceS.resource_rep(): expected_s_count, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): expected_s_count, re.ResourceRZ.resource_rep(): 1, re.ResourceCNOT.resource_rep(): 2 * (active_wires - 1), } @@ -117,6 +214,150 @@ def test_resources_from_rep(self, pauli_word, expected_h_count, expected_rx_coun op_resource_params = op_compressed_rep.params assert op_resource_type.resources(**op_resource_params) == expected + @pytest.mark.parametrize("pauli_word", pauli_words) + def test_adjoint_decomp(self, pauli_word): + """Test that the adjoint decomposition is correct.""" + expected = {re.ResourcePauliRot.resource_rep(pauli_word=pauli_word): 1} + assert re.ResourcePauliRot.adjoint_resource_decomp(pauli_word=pauli_word) == expected + + op = re.ResourcePauliRot(theta=0.5, pauli_word=pauli_word, wires=range(len(pauli_word))) + op_dag = re.ResourceAdjoint(op) + + assert re.get_resources(op) == re.get_resources(op_dag) + + ctrl_data = ( + ( + "XXX", + [1], + [1], + [], + { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 0, 0): 1, + re.ResourceCNOT.resource_rep(): 4, + }, + ), + ( + "XXX", + [1], + [0], + [], + { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 1, 0): 1, + re.ResourceCNOT.resource_rep(): 4, + }, + ), + ( + "XXX", + [1, 2], + [1, 1], + ["w1"], + { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 1): 1, + re.ResourceCNOT.resource_rep(): 4, + }, + ), + ( + "XIYIZIX", + [1], + [1], + [], + { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceS.resource_rep(): 1, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 0, 0): 1, + re.ResourceCNOT.resource_rep(): 6, + }, + ), + ( + "XIYIZIX", + [1], + [0], + [], + { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceS.resource_rep(): 1, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 1, 1, 0): 1, + re.ResourceCNOT.resource_rep(): 6, + }, + ), + ( + "XIYIZIX", + [1, 2], + [1, 1], + ["w1"], + { + re.ResourceHadamard.resource_rep(): 6, + re.ResourceS.resource_rep(): 1, + re.ResourceAdjoint.resource_rep(re.ResourceS, {}): 1, + re.ResourceControlled.resource_rep(re.ResourceRZ, {}, 2, 0, 1): 1, + re.ResourceCNOT.resource_rep(): 6, + }, + ), + ( + "III", + [1], + [1], + [], + {re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 1, 0, 0): 1}, + ), + ( + "III", + [1], + [0], + [], + {re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 1, 1, 0): 1}, + ), + ( + "III", + [1, 2], + [1, 1], + ["w1"], + {re.ResourceControlled.resource_rep(re.ResourceGlobalPhase, {}, 2, 0, 1): 1}, + ), + ) + + @pytest.mark.parametrize( + "pauli_word, ctrl_wires, ctrl_values, work_wires, expected_res", ctrl_data + ) + def test_resource_controlled( + self, ctrl_wires, ctrl_values, work_wires, pauli_word, expected_res + ): + """Test that the controlled resources are as expected""" + num_ctrl_wires = len(ctrl_wires) + num_ctrl_values = len([v for v in ctrl_values if not v]) + num_work_wires = len(work_wires) + + op = re.ResourcePauliRot( + 1.24, pauli_word, wires=list(f"wire_{i}" for i in range(len(pauli_word))) + ) + op2 = re.ResourceControlled( + op, control_wires=ctrl_wires, control_values=ctrl_values, work_wires=work_wires + ) + + assert ( + op.controlled_resource_decomp( + num_ctrl_wires, num_ctrl_values, num_work_wires, **op.resource_params() + ) + == expected_res + ) + assert op2.resources(**op2.resource_params()) == expected_res + + @pytest.mark.parametrize("z", range(1, 5)) + @pytest.mark.parametrize("pauli_word", pauli_words) + def test_pow_decomp(self, z, pauli_word): + """Test that the pow decomposition is correct.""" + op = re.ResourcePauliRot(theta=0.5, pauli_word=pauli_word, wires=range(len(pauli_word))) + expected_res = {re.ResourcePauliRot.resource_rep(pauli_word=pauli_word): 1} + assert op.pow_resource_decomp(z, **op.resource_params()) == expected_res + + op2 = re.ResourcePow(op, z) + assert op2.resources(**op2.resource_params()) == expected_res + class TestIsingXX: """Test the IsingXX class.""" From 79ed9ed3e1d48e33fbfc3767afa9eb9406ae40b5 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Thu, 19 Dec 2024 10:35:00 -0500 Subject: [PATCH 335/335] address comments --- .../labs/resource_estimation/ops/identity.py | 18 +++++++++--------- .../ops/qubit/test_non_parametric_ops.py | 1 - .../resource_estimation/ops/test_identity.py | 16 ++++++++-------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/pennylane/labs/resource_estimation/ops/identity.py b/pennylane/labs/resource_estimation/ops/identity.py index 9f89c8653e1..602c7a232b3 100644 --- a/pennylane/labs/resource_estimation/ops/identity.py +++ b/pennylane/labs/resource_estimation/ops/identity.py @@ -34,19 +34,19 @@ def resource_params(self) -> dict: def resource_rep(cls, **kwargs) -> re.CompressedResourceOp: return re.CompressedResourceOp(cls, {}) - @staticmethod - def adjoint_resource_decomp() -> Dict[re.CompressedResourceOp, int]: - return {} + @classmethod + def adjoint_resource_decomp(cls) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} - @staticmethod + @classmethod def controlled_resource_decomp( - num_ctrl_wires, num_ctrl_values, num_work_wires + cls, num_ctrl_wires, num_ctrl_values, num_work_wires ) -> Dict[re.CompressedResourceOp, int]: - return {} + return {cls.resource_rep(): 1} - @staticmethod - def pow_resource_decomp(z) -> Dict[re.CompressedResourceOp, int]: - return {} + @classmethod + def pow_resource_decomp(cls, z) -> Dict[re.CompressedResourceOp, int]: + return {cls.resource_rep(): 1} class ResourceGlobalPhase(qml.GlobalPhase, re.ResourceOperator): diff --git a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py index 9c85b6acb6f..d883fb2dd1b 100644 --- a/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py +++ b/pennylane/labs/tests/resource_estimation/ops/qubit/test_non_parametric_ops.py @@ -364,7 +364,6 @@ class TestT: """Tests for ResourceT""" def test_resources(self): - """Test that ResourceT does not implement a decomposition""" op = re.ResourceT(0) with pytest.raises(re.ResourcesNotDefined): op.resources() diff --git a/pennylane/labs/tests/resource_estimation/ops/test_identity.py b/pennylane/labs/tests/resource_estimation/ops/test_identity.py index d89d4143b63..f909d06bb26 100644 --- a/pennylane/labs/tests/resource_estimation/ops/test_identity.py +++ b/pennylane/labs/tests/resource_estimation/ops/test_identity.py @@ -53,13 +53,13 @@ def test_resource_adjoint(self): """Test that the adjoint resources are as expected""" op = re.ResourceIdentity(0) op2 = re.ResourceAdjoint(op) - assert op.adjoint_resource_decomp() == {} - assert op2.resources(**op2.resource_params()) == {} + assert op.adjoint_resource_decomp() == {re.ResourceIdentity.resource_rep(): 1} + assert op2.resources(**op2.resource_params()) == {re.ResourceIdentity.resource_rep(): 1} identity_ctrl_data = ( - ([1], [1], [], {}), - ([1, 2], [1, 1], ["w1"], {}), - ([1, 2, 3], [1, 0, 0], ["w1", "w2"], {}), + ([1], [1], [], {re.ResourceIdentity.resource_rep(): 1}), + ([1, 2], [1, 1], ["w1"], {re.ResourceIdentity.resource_rep(): 1}), + ([1, 2, 3], [1, 0, 0], ["w1", "w2"], {re.ResourceIdentity.resource_rep(): 1}), ) @pytest.mark.parametrize( @@ -83,9 +83,9 @@ def test_resource_controlled(self, ctrl_wires, ctrl_values, work_wires, expected assert op2.resources(**op2.resource_params()) == expected_res identity_pow_data = ( - (1, {}), - (2, {}), - (5, {}), + (1, {re.ResourceIdentity.resource_rep(): 1}), + (2, {re.ResourceIdentity.resource_rep(): 1}), + (5, {re.ResourceIdentity.resource_rep(): 1}), ) @pytest.mark.parametrize("z, expected_res", identity_pow_data)