-
Notifications
You must be signed in to change notification settings - Fork 616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add resource methods to some templates (TrotterProduct, Exp, StatePrep, QPE) #6677
base: resource_symbolic_decomps
Are you sure you want to change the base?
Changes from all commits
f00d911
772a2d3
8e38d39
aa6a3cb
01c403f
808cde8
b08c0e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -15,10 +15,15 @@ | |||||||||
from collections import defaultdict | ||||||||||
from typing import Dict | ||||||||||
|
||||||||||
import pennylane as qml | ||||||||||
import pennylane.labs.resource_estimation as re | ||||||||||
from pennylane import math | ||||||||||
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.exp import Exp | ||||||||||
from pennylane.ops.op_math.pow import PowOperation | ||||||||||
from pennylane.ops.op_math.sprod import SProd | ||||||||||
|
||||||||||
# pylint: disable=too-many-ancestors,arguments-differ,protected-access,too-many-arguments,too-many-positional-arguments | ||||||||||
|
||||||||||
|
@@ -189,3 +194,142 @@ def pow_resource_decomp( | |||||||||
def tracking_name(base_class, z, base_params) -> str: | ||||||||||
base_name = base_class.tracking_name(**base_params) | ||||||||||
return f"Pow({base_name}, {z})" | ||||||||||
|
||||||||||
|
||||||||||
class ResourceExp(Exp, re.ResourceOperator): | ||||||||||
"""Resource class for Exp""" | ||||||||||
|
||||||||||
@staticmethod | ||||||||||
def _resource_decomp(base_class, coeff, num_steps, **kwargs): | ||||||||||
|
||||||||||
while isinstance(base_class, SProd): | ||||||||||
coeff *= base_class.scalar | ||||||||||
base_class = base_class.base | ||||||||||
Comment on lines
+205
to
+207
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be handled in Also |
||||||||||
|
||||||||||
# Custom exponential operator resources: | ||||||||||
if isinstance(base_class, re.ResourceOperator): | ||||||||||
try: | ||||||||||
return base_class.exp_resource_decomp(coeff, num_steps, **kwargs) | ||||||||||
except re.ResourcesNotDefined: | ||||||||||
pass | ||||||||||
Comment on lines
+209
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the other symbolic classes we assume |
||||||||||
|
||||||||||
# PauliRot resource decomp: | ||||||||||
if (pauli_sentence := base_class.pauli_rep) and math.real(coeff) == 0: | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||
if qml.pauli.is_pauli_word(base_class): | ||||||||||
num_wires = len(base_class.wires) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, |
||||||||||
pauli_word = tuple(pauli_sentence.keys())[0] # only one term in the sum | ||||||||||
return _resources_from_pauli_word(pauli_word, num_wires) | ||||||||||
|
||||||||||
scalar = num_steps or 1 # 1st-order Trotter-Suzuki with 'num_steps' trotter steps: | ||||||||||
return _scale_dict( | ||||||||||
_resources_from_pauli_sentence(pauli_sentence), scalar=scalar, in_place=True | ||||||||||
) | ||||||||||
|
||||||||||
raise re.ResourcesNotDefined | ||||||||||
|
||||||||||
def resource_params(self): | ||||||||||
return { | ||||||||||
"base_class": self.base, | ||||||||||
"coeff": self.scalar, | ||||||||||
"num_steps": self.num_steps, | ||||||||||
} | ||||||||||
Comment on lines
+230
to
+235
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two comments here.
|
||||||||||
|
||||||||||
@classmethod | ||||||||||
def resource_rep(cls, base_class, coeff, num_steps, **kwargs): | ||||||||||
name = f"Exp({base_class.__class__.__name__}, {coeff}, num_steps={num_steps})".replace( | ||||||||||
Comment on lines
+238
to
+239
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets use the tracking name here. This change will require
|
||||||||||
"Resource", "" | ||||||||||
) | ||||||||||
return re.CompressedResourceOp( | ||||||||||
cls, {"base_class": base_class, "coeff": coeff, "num_steps": num_steps}, name=name | ||||||||||
) | ||||||||||
|
||||||||||
@classmethod | ||||||||||
def pow_resource_decomp( | ||||||||||
cls, z0, base_class, coeff, num_steps | ||||||||||
) -> Dict[re.CompressedResourceOp, int]: | ||||||||||
return {cls.resource_rep(base_class, z0 * coeff, num_steps): 1} | ||||||||||
|
||||||||||
@classmethod | ||||||||||
def controlled_resource_decomp( | ||||||||||
cls, | ||||||||||
num_ctrl_wires, | ||||||||||
num_ctrl_values, | ||||||||||
num_work_wires, | ||||||||||
base_class, | ||||||||||
coeff, | ||||||||||
num_steps, | ||||||||||
) -> Dict[re.CompressedResourceOp, int]: | ||||||||||
"""The controlled exponential decomposition of a Pauli hamiltonian is symmetric, thus we only need to control | ||||||||||
on the RZ gate in the middle.""" | ||||||||||
if (p_rep := base_class.pauli_rep) and math.real(coeff) == 0: | ||||||||||
|
||||||||||
if qml.pauli.is_pauli_word(base_class) and len(p_rep) > 1: | ||||||||||
base_gate_types = cls.resources(base_class, coeff, num_steps) | ||||||||||
|
||||||||||
rz_counts = base_gate_types.pop(re.ResourceRZ.resource_rep()) | ||||||||||
ctrl_rz = re.ResourceControlled.resource_rep( | ||||||||||
re.ResourceRZ, {}, num_ctrl_wires, num_ctrl_values, num_work_wires | ||||||||||
) | ||||||||||
|
||||||||||
base_gate_types[ctrl_rz] = rz_counts | ||||||||||
return base_gate_types | ||||||||||
|
||||||||||
raise re.ResourcesNotDefined | ||||||||||
|
||||||||||
|
||||||||||
def _resources_from_pauli_word(pauli_word, num_wires): | ||||||||||
pauli_string = "".join((str(v) for v in pauli_word.values())) | ||||||||||
len_str = len(pauli_string) | ||||||||||
|
||||||||||
if len_str == 0: | ||||||||||
return {} # Identity operation has no resources. | ||||||||||
Comment on lines
+284
to
+285
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
For more complete tracking. |
||||||||||
|
||||||||||
if len_str == 1: | ||||||||||
if pauli_string == "X": | ||||||||||
return {re.CompressedResourceOp(re.ResourceRX, {}): 1} | ||||||||||
if pauli_string == "Y": | ||||||||||
return {re.CompressedResourceOp(re.ResourceRY, {}): 1} | ||||||||||
if pauli_string == "Z": | ||||||||||
return {re.CompressedResourceOp(re.ResourceRZ, {}): 1} | ||||||||||
|
||||||||||
counter = {"X": 0, "Y": 0, "Z": 0} | ||||||||||
for c in pauli_string: | ||||||||||
counter[c] += 1 | ||||||||||
|
||||||||||
num_x = counter["X"] | ||||||||||
num_y = counter["Y"] | ||||||||||
|
||||||||||
s = re.CompressedResourceOp(re.ResourceS, {}) | ||||||||||
h = re.CompressedResourceOp(re.ResourceHadamard, {}) | ||||||||||
rz = re.CompressedResourceOp(re.ResourceRZ, {}) | ||||||||||
cnot = re.CompressedResourceOp(re.ResourceCNOT, {}) | ||||||||||
Comment on lines
+287
to
+305
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change the calls to |
||||||||||
|
||||||||||
gate_types = {} | ||||||||||
gate_types[rz] = 1 | ||||||||||
gate_types[s] = 2 * num_y | ||||||||||
gate_types[h] = 2 * (num_x + num_y) | ||||||||||
gate_types[cnot] = 2 * (num_wires - 1) | ||||||||||
|
||||||||||
return gate_types | ||||||||||
|
||||||||||
|
||||||||||
def _resources_from_pauli_sentence(pauli_sentence): | ||||||||||
gate_types = defaultdict(int) | ||||||||||
rx = re.CompressedResourceOp(re.ResourceRX, {}) | ||||||||||
ry = re.CompressedResourceOp(re.ResourceRY, {}) | ||||||||||
rz = re.CompressedResourceOp(re.ResourceRZ, {}) | ||||||||||
Comment on lines
+316
to
+320
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. Let's use |
||||||||||
|
||||||||||
for pauli_word in iter(pauli_sentence.keys()): | ||||||||||
num_wires = len(pauli_word.wires) | ||||||||||
|
||||||||||
if num_wires == 1: | ||||||||||
pauli_string = "".join((str(v) for v in pauli_word.values())) | ||||||||||
op_type = {"Z": rz, "X": rx, "Y": ry}[pauli_string] | ||||||||||
gate_types[op_type] += 1 | ||||||||||
|
||||||||||
continue | ||||||||||
|
||||||||||
pw_gates = _resources_from_pauli_word(pauli_word, num_wires) | ||||||||||
_ = _combine_dict(gate_types, pw_gates, in_place=True) | ||||||||||
|
||||||||||
return gate_types |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,6 +123,13 @@ 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 exp_resource_decomp( | ||
cls, scalar, num_steps, *args, **kwargs | ||
) -> Dict[CompressedResourceOp, int]: | ||
"""Returns a compressed representation for the resources of the exponentiated operator""" | ||
raise ResourcesNotDefined | ||
Comment on lines
+126
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this be defined? |
||
|
||
@classmethod | ||
def tracking_name(cls, *args, **kwargs) -> str: | ||
"""Returns a name used to track the operator during resource estimation.""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like
base_class
can be of multiple types. A type annotation likebase_class: Union[....]
would be helpful.