-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Parameter instances can compare equal while having different hash values #11654
Comments
Doesn't something like 1. need to happen in any case because of the Python data model violation? I'm having trouble imagining a case where implementing 1. would be worse than the current situation. Of course, this doesn't imply that 1. alone is the optimal route here. |
Sorry, I should have started 2 by saying "Add name to the equality test and rework the pulse code" (edited it to say that now). The choice is either make both eq and hash use only UUID or make them both use UUID and name. |
Ah okay, that makes more sense, thanks for clarifying. |
Ah yeah, that's not ideal about the pulse code. I'd also lean in favour of 2, since it's the documented behaviour and imo the less surprising version of equality. With the public |
One thing that is interesting is that the pulse code actually suffers from this bug. The
However, an important use case of parameter referencing is broken as demonstrated by this code: from qiskit import pulse
from qiskit.circuit import Parameter
f = Parameter("f")
with pulse.build() as sched:
pulse.set_frequency(f, pulse.DriveChannel(0))
scoped_f = next(iter(sched.scoped_parameters()))
print(f"{f=}")
print(f"{scoped_f=}")
print(f"{sched=}")
print(f"assign f to 5: {sched.assign_parameters({f: 5}, inplace=False)}")
print(f"assign scoped_f to 5: {sched.assign_parameters({scoped_f: 5}, inplace=False)}") which prints
Because the parameter assignment code uses dicts and sets to manage the parameters, the fact that
To be fair to the pulse code, the documentation I referenced above was only added in 0.45.0 in #10875. I am not sure if there is anything else that says that a The problem with the pulse code is that its tests all create a schedule with parameters and then check the scoped parameters for equality with the original parameters. None of them tests try to assign a value to a schedule using the scoped parameter. Otherwise, this issue would have been caught when reviewing #10875. Given the need to align |
I thought of one way to fix the pulse code with equality testing name and UUID. We could add a new |
I'm always a bit nervous around introducing subclasses on objects that weren't intended to have them, because it introduces an extra layer that can get out of sync. For example here, it'd be easy for us to have the For #10875 - yeah, I wasn't blaming Pulse, just trying to find the most consistent path. I hadn't realised the name/UUID equality doc got added in that PR, but I/we should have spotted the |
Indeed I was trying to introduce another parameter class (not subclass) designed for the pulse module when we investigated the design of the calibration module in Qiskit Experiments. Pulse, or calibration template, doesn't allow name conflict within the same context, and the name must be a unique property in this domain while circuit does allow the name conflict. This made me think the machinery with UUID was not necessary in the pulse domain. Unfortunately, we didn't adopt the idea, because of an issue in the parameter linking between a circuit and pulse program via the pulse gate framework. I think the nature of pulse parameter comes from the conventional design of the calibration software -- we start from a configuration object where a plan text stores all system information. Since we have several conventional names, e.g. "amp" representing the hight of the waveform, we usually append the scope information to the parameter name, and the scope usually means the pulse kind and in which channel it is played. We preferred the convenience of parameter link, and decided to push the complexity arising from the representation difference to the With the refactoring in the pulse feature branch, I'd like to support the qe-compiler Anyways, I'm also fine with dropping the scope feature from the |
Thanks, @nkanazawa1989. Yes, I agree with the direction you outline. To summarize, I think Let's assume that we accept the code currently in #11652 (
@nkanazawa1989 Do you prefer 1? |
Yes. I can quickly approve a deprecation PR to 0.46 branch if you can prepare before the deadline. |
As noted in Qiskit#11654, parameter scoping does not work properly beginning with Qiskit 0.45.0. The problem is that the `Parameter` name is now used for the hash value and so the parameter produced with the scoped name is not equivalent to the original parameter and does not substitute for it when using parameter assignment. Since the scoped parameter mechanism was a convenience feature and not widely used, it is simply deprecated rather than made to work with parameter assignment.
I opened #11654 to deprecate the scoping code. Next I will open a PR to |
Anything that misses rc1 should really trigger us to create an rc2 (though in practice I don't think it will unless it's critical), so it really should be a strategy of last resort. I can briefly check a pulse PR before Naoki comes back online, but it'll be best if he does final review once he's back. After that, #11652 should be trivial and I'll throw it in the merge queue. |
hehe I'm still online. Just approved but #11691 still needs lint fix. @jakelishman can you take over and approve it? The PR looks fine with me. |
np, thanks Naoki - yeah, I'll do it in a bit. |
The `scoped_parameters` and `search_parameters` methods have been removed from the `ScheduleBlock` class. These methods returned `Parameter` objects that partially linked to the parameters in the `ScheduleBlock` instance but assigning values using these objects did not work correctly. Users should use `ScheduleBlock.parameters` instead and iterate through `ScheduleBlock.references` and compare to the `Schedule.parameters` attributes of the subreferences when needing to distinguish which subroutine a parameter is used in. See Qiskit#11654 for more information.
The `scoped_parameters` and `search_parameters` methods have been removed from the `ScheduleBlock` class. These methods returned `Parameter` objects that partially linked to the parameters in the `ScheduleBlock` instance but assigning values using these objects did not work correctly. Users should use `ScheduleBlock.parameters` instead and iterate through `ScheduleBlock.references` and compare to the `Schedule.parameters` attributes of the subreferences when needing to distinguish which subroutine a parameter is used in. See Qiskit#11654 for more information.
* Remove pulse scoped_parameters and search_parameters The `scoped_parameters` and `search_parameters` methods have been removed from the `ScheduleBlock` class. These methods returned `Parameter` objects that partially linked to the parameters in the `ScheduleBlock` instance but assigning values using these objects did not work correctly. Users should use `ScheduleBlock.parameters` instead and iterate through `ScheduleBlock.references` and compare to the `Schedule.parameters` attributes of the subreferences when needing to distinguish which subroutine a parameter is used in. See #11654 for more information. * Lint and sphinx clean up
* Deprecate pulse reference parameter scoping As noted in #11654, parameter scoping does not work properly beginning with Qiskit 0.45.0. The problem is that the `Parameter` name is now used for the hash value and so the parameter produced with the scoped name is not equivalent to the original parameter and does not substitute for it when using parameter assignment. Since the scoped parameter mechanism was a convenience feature and not widely used, it is simply deprecated rather than made to work with parameter assignment. * black * Update releasenotes/notes/deprecate-pulse-parameter-scoping-6b6b50a394b57937.yaml Co-authored-by: Jake Lishman <jake@binhbar.com> * Move warning to documentation --------- Co-authored-by: Jake Lishman <jake@binhbar.com> Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Environment
What is happening?
Currently, two
Parameter
instances can compare equal while not having the same hash value. This condition violates the Python data model which is documented as requiring:How can we reproduce the issue?
What should happen?
Here there is a decision to make. Parameters are essentially a tuple of a name string and a Python UUID. Currently,
__eq__
uses only the UUID while__hash__
uses the name and the UUID. The two methods should use the same data.In #11647 (comment), @jakelishman mentioned that the documentation states that the name is required for equality, so I created #11652 to add the name to the equality check (I think @jakelishman was referring to "Setting the
uuid
field when creating two parameters to the same thing (along with the same name) allows them to be equal." in theParameter.__init__
docstring). However, in the pulse module, there is code that makes use of the fact that parameter names are not part of the equality test by redefining the name:qiskit/qiskit/pulse/schedule.py
Lines 882 to 912 in ce02f06
So we have to decide which way to go:
name
from the hash calculation so that only the UUID matters for Parameter instances. In practice, this is probably fine since the UUID's should always be unique.name
to the equality test and rework the pulse code. It is just using the renaming for convenience of labeling parameters. Perhaps the labeling could be done differently (scoped_parameters
could return a dict with the keys being the scoped parameter names?). I don't seescoped_parameters
used anywhere here or qiskit-experiments currently.I lean toward option 2. I think a key question is how tied is
Parameter
toParameterExpression
(technically it is a direct subclass so very tied 🙂). IfParameter
just needs to be a variable that floats around through code objects and can later be assigned a value, allowing different names seems fine. However, if it is important that aParameter
can always be used in an expression and then later substituted for a different value, allowing different names is a problem becauseParameterExpression
maps the names directly to sympy/symengine symbols and wants to substitute them using the sympy/symengine mechanism. IfParameter
s are being passed around and manipulated, you could end up callingassign
with aParameter
instance with a different name from the one that was used to create the underlying symbolic expression of aParameterExpression
. Or you could say that both use cases are allowed -- when doing simple value substitution renamingParameter
s is okay; when doing symbolic expressions, you have to be careful not to reuse UUID's with different names.@nkanazawa1989 What do you think as the author of the scoped_parameters code?
Any suggestions?
No response
The text was updated successfully, but these errors were encountered: