-
Notifications
You must be signed in to change notification settings - Fork 547
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
Custom boolean functions for supporting noise models #5674
base: master
Are you sure you want to change the base?
Conversation
Hello. You may have forgotten to update the changelog!
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #5674 +/- ##
==========================================
- Coverage 99.68% 99.67% -0.01%
==========================================
Files 415 417 +2
Lines 38898 38748 -150
==========================================
- Hits 38774 38623 -151
- Misses 124 125 +1 ☔ View full report in Codecov by Sentry. |
Thanks @obliviateandsurrender! Would you mind linking the corresponding SC story? |
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.
Thanks @obliviateandsurrender!
.. seealso:: Users are advised to use :func:`~.wires_eq` for a functional construction. | ||
""" | ||
|
||
def __init__(self, wires): |
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.
I might be misremembering, but I thought I saw you show in the meeting the input argument of WiresEq
being an operation?
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.
Oh it's in the wires_in
and wires_eq
function below 🤔 Why do we need operation input?
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.
This is just an additional support to make the constructor functions more flexible and easy to use for the users. Especially in a scenario where they are trying to automate building noise models based on some set of operations they have in their pipeline, then they can still make these conditional without having to extract wires manually.
pennylane/noise/conditionals.py
Outdated
ops (str, Operation, Union(list[str, Operation])): string | ||
representation or instance of the operation. |
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.
Here I was expecting op_in([qml.RX, qml.Hadamard])
. Is this approach challenging/not advisable?
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.
I think I just forgot to mention it, but doing that is possible as well! I'll add it to the docs. 😁
>>> func = qml.noise.partial_wires(qml.RX(1.2, [12])) | ||
>>> func(2) | ||
qml.RX(1.2, wires=[2]) |
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.
I'm confused, so we originally have an instantiated RX
on wire 12
but it gets mapped to wire 2?
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.
Yes. We had an operation that was RX(1.2, wires=12)
. When we wrap it with a partial_wires
, we can instantiate that operation with the new input wires, in this case, 2
.
>>> func = qml.noise.partial_wires(qml.RX(1.2, [12])) | ||
>>> func(2) | ||
qml.RX(1.2, wires=[2]) | ||
>>> func(qml.RY(1.0, ["wires"])) |
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.
🤔 Do we need to support calling on operations, rather than just wires?
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 makes it easier to use this method and is kinda intuitive as well since we want to initialize the wrapped operation based on the wires of an encountered operation.
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.
Looking good so far. I added a few suggestions to clean up the code, along with some clarification questions. The two big questions I have:
- The only use of the bitwise and condition properties so far is to determine what the
repr
of the BooleanFunction is going to be. If they are just being used as flags, then I can suggest two alternatives that might be cleaner and more space efficient:
-
Update the base
repr
method to always returnBooleanFn(self.fn.name)
, and in each of the sub classes, just overwrite therepr
method directly (as you have done for thestr
method. You can also drop thename
kwarg. -
Update the base
repr
method to returnBooleanFn(self.fn.name)
ifname is None
else returnname
. The only time name is provided is when the sub classes are conditionals or bitwise operators. In the case that a user provides a name, we probably want to display that anyways.
- Regarding
OpEq
, it doesn't seem to me that we need it? It's just a special case ofOpIn
when the list of operations is length 1. Also it seems to be coded to allow passing in multiple operators both as the reference list and as the items being compared, but this doesn't make sense to me because when we use this conditional in a circuit, we are going to iterate over the gates in the circuit one at a time anyways? So the item being compared will always be a single operation. Furthermore, comparing multiple lists on both sides can be easily achieved by using the map function, so it's probably not worth it? Happy to discuss offline if I am missing something here.
Look forward to seeing this get in!
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #5674 +/- ##
==========================================
- Coverage 99.67% 99.67% -0.01%
==========================================
Files 416 418 +2
Lines 38686 38543 -143
==========================================
- Hits 38562 38418 -144
- Misses 124 125 +1 ☔ View full report in Codecov by Sentry. |
Not really. I do use them for that, but that is not the intended purpose for them. These properties check the existence of underlying respective attributes that store expressions that BooleanFn uses for evaluation. For the
Yes, it is a special case of
|
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.
Looks good to go 💯. A left a few comments, also would suggest adding some tests for wires_in and wires_eq comparing with operations (specifically with operations of multiple wires like CNOT or some template)
Good job!
pennylane/noise/conditionals.py
Outdated
iters = val if isinstance(val, (list, tuple, set, Wires)) else getattr(val, "wires", [val]) | ||
try: | ||
wires = set().union(*((getattr(w, "wires", None) or Wires(w)).tolist() for w in iters)) | ||
except TypeError: |
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.
Why do you expect a TypeError here?
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.
TypeError
happens when inputs are classes. Added an additional WireError
for unhashable input w
to Wires, as well.
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.
Looks great, good job! Here are a few minor comments.
I've been also doing a lot of tricky testing and I see it's bulletproof 😎
self._cond = set(wires) | ||
self.condition = self._cond |
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.
Why do you do this instead of self.condition = set(wires)
?
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.
No specific reason, just consistency with the Op
classes. I essentially use cond
as a private attribute for the conditionals, which I use in the computation later, whereas the condition
attribute is a user-facing attribute that I don't access later on.
One may give an instance of :class:`Operation <pennylane.operation.Operation>` | ||
for the ``operation`` argument: | ||
>>> func = qml.noise.partial_wires(qml.RX(1.2, [12])) |
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.
Probably not super important, but it does not work with some templates:
func = qml.noise.partial_wires(qml.Qubitization(qml.X(0), control = [1]))
It would be the same with any template that has not "wires" directly
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.
Yeah, it will work with operations (and templates) that take in "wires" directly. It will be too much effort to generalize it IMO as each corner case would be different. Moreover, if a template doesn't take wires
directly, there shouldn't be a need to use this.
Context: We aim to have a custom boolean function that could serve as a building block for creating noise models.
Description of the Change:
BooleanFn
to work with arbitraryargs
andkwargs
.NoiseConditionals
as a class that helps buildBooleanFn
required for creating noise models.wire/op_eq/in
to enhance the building experience for the aboveBooleanFn
.Benefits:
Possible Drawbacks:
Related GitHub Issues: [sc-63080]