-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Created ControlValues for controlled gates/operations, fix for #4512 #4714
Conversation
NoureldinYosri
commented
Nov 30, 2021
•
edited
Loading
edited
- created control_values.py which contains the ControlValues class.
- FreeVars and ConstrainedVars classes are provided for ease of use.
- while the basic idea of ControlValues integrating it inside the code base was challening
- the old way of using control_values assumed it's a tuple of tuples of ints and was used as thus (comparasion, hashing, slicing, fomatting, conditioning, and loops), the ControlValues class had to provide these functionalities
- the trickiest part to get right was the support for formatting!
- fixes Controlled gate construction cannot construct some cases #4512
383ec2b
to
4a4bab9
Compare
…mlib#4512 quantumlib#4714 - created control_values.py which contains the ControlValues class. - FreeVars and ConstrainedVars classes are provided for ease of use. - while the basic idea of ControlValues integrating it inside the code base was challening - the old way of using control_values assumed it's a tuple of tuples of ints and was used as thus (comparasion, hashing, slicing, fomatting, conditioning, and loops), the ControlValues class had to provide these functionalities - the trickiest part to get right was the support for formatting! - I'll create a follow up PR for unit tests for control_values.py
194915a
to
0c6f81e
Compare
Thanks @NoureldinYosri for opening up the PR. Unit tests need to be added for every new code / modification or else the coverage check won't pass and we can't approve any PRs with failing tests. Please add the unit tests (and fix other failing tests) and then we can do a review for the PR. You can look at https://github.com/quantumlib/Cirq/blob/master/docs/dev/development.md#continuous-integration-and-local-testing to learn more about how to run the integration tests locally. |
2763fdd
to
26ec3b7
Compare
0dc16b4
to
fe8494b
Compare
@tanujkhattar can you check this PR?, all checks pass now :) |
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.
Some initial comments.
…mlib#4512 created control_values.py which contains the ControlValues class. FreeVars and ConstrainedVars classes are provided for ease of use. while the basic idea of ControlValues integrating it inside the code base was challening the old way of using control_values assumed it's a tuple of tuples of ints and was used as thus (comparasion, hashing, slicing, fomatting, conditioning, and loops), the ControlValues class had to provide these functionalities the trickiest part to get right was the support for formatting!
fe8494b
to
320511c
Compare
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.
Some quick comments from my phone.
'Control values <{!r}> outside of range for control qubit ' | ||
'number <{!r}>.'.format(val, i) | ||
if not isinstance(control_values, cv.ControlValues): | ||
self.control_values = cv.ControlValues( |
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 needs to be marked as a breaking change since it changes the data type of a public field. @tanujkhattar
cirq-core/cirq/ops/control_values.py
Outdated
return tuple(tuple(product) for product in vals) | ||
|
||
|
||
class ControlValues: |
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.
All public functions need docstrings. Also this class needs to be available in the Cirq root namespace.
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.
what is the root namespace?
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.
The root __init__.py
file, so users can do cirq.ControlValues
. (Once you do this you'll find it triggers some additional tests that check for serializability etc. These tasks need to be completed too, or else the control won't be able to run on real machines).
Can you add "fixes #4512" to the description so they are linked? |
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.
One more thing
…mlib#4512 created control_values.py which contains the ControlValues class. FreeVars and ConstrainedVars classes are provided for ease of use. while the basic idea of ControlValues integrating it inside the code base was challening the old way of using control_values assumed it's a tuple of tuples of ints and was used as thus (comparasion, hashing, slicing, fomatting, conditioning, and loops), the ControlValues class had to provide these functionalities the trickiest part to get right was the support for formatting!
@daxfohl I addressed most of the comments, I have a couple of questions which I put in the comments. |
@NoureldinYosri consider using |
…plementations, SimpleControlValues which represents the old format of control_values, ConstrainedControlValues which can be used to represent more complex expressions (e.g. xor), this allows us to later add concrete implementations that support other formats (e.g. sympy expressions), I also added a builder class to facilitate the creation of ControlValues.
The changes resolve my concerns about mutability. Still need to add these classes to the root namespace in |
There are some concerns here re: qsim compatability @95-martin-orion , this has implications for TFQ as well and our goals for decompose always returning X,Y,Z,C*Z. Have you looked into this Orion ? |
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.
Quick review pass + follow up to Mike's question
tuple((val,) if isinstance(val, int) else tuple(sorted(val)) for val in control_values), | ||
) | ||
print('building from', control_values) | ||
self.control_values = cv.ControlValuesBuilder().append(control_values).build() |
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.
RE @MichaelBroughton's comment: my concern is how this completely replaces existing control behavior. qsim has optimizations for simple controls (|0> or |1>), but I'd need to check with Sergei on whether they're generalizable to arbitrary controls.
Outside of that concern, though, qsim is happy as long as the gate can be expressed as a unitary on at most 6 qubits total (the optimization allows 6 target qubits + any number of controls).
_combinations: Any | ||
_nxt: Optional['ControlValues'] = None |
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.
What is the motivation behind this linked-list structure, instead of e.g. a list-of-lists? It seems to me that something like this would be more manageable:
[[comb1, comb2, ...], [nxt_comb1, nxt_comb2, ...], ...]
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 assumes that lists of combinations are the only way that wil be used to define control_values, there was a previous comment on this PR about possibly supporting other ways like tensors or sympy expressions in the future.
the linked-list structure allows defining using different ways at the same time e.g. CV1(a table) -> CV2(a sympy expression) -> ... etc
each of those object will inheret from the ControlValues base class so that wil work perfectly.
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.
The comment referred to above is on the linked issue. #4512 (comment)
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.
Another thought, have you considered making a separate ControlValuesProduct
class that merges two (or maybe N) ControlValues objects into a product? That approach would allow for a smaller base class and adhere more to the single responsibility pattern.
for control_vals in self.control_values: | ||
control_vals = cv.ControlValues.flatten(control_vals) |
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.
At a glance, this doesn't appear to add functionality - for a control like |00><00| + |11><11|
I would expect some extra handling to be needed (sum over tensors?), and tests to be added to cover it.
Is this being added in a future PR?
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.
the |00><00| + |11><11|
combination can be added in the same way that the xor combination |01><01| + |10><10|
which was not supported in the old way but is supported now as can be seen in the tests
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.
Did we ever have a design review on this one? I provided the initial code reviews assuming a design was approved, but I still kind of stand by what I originally said on the linked issue, that this could be done more simply by just adding a bool flag to the ControlledGate constructor. That flag would control whether to interpret the 2D input array vertically as allowed states by qubit (which is how it currently works, but is limited), or horizontally as a full set of allowed states (which allows full control).
That this question came up during code review makes me think end users are going to have trouble understanding how to use these classes.
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 introduced the idea in #4512 (comment) and talked about it ina cirq meeting #4512 (comment), wrote a prototype in #4512 (comment) and got a review in #4512 (comment)
the boolean idea has some issues
- it doesn't address your previous comment Controlled gate construction cannot construct some cases #4512 (comment), it fact it will make it harder to add those new ways lalter.
- there is no way of getting around computing products, what a boolean will do is to force the users to compute them before passing them to the constructor, a work around would be passing an array of booleans but that has its own issues.
- any work around will make the constructors for controlledGate and controlledOperation execture complex logic which is not a good idea in general, introduce tech debt and make it harder change or support new types later,
lets discuss this in the next cirq meeting.
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.
Sounds good. I have limited availability for cynqs these days, but I think a reprise with the rest of the team on Wed would be great just to get the ball rolling on this PR, since it seems to keep going stagnant. I'm looking forward to the functionality.
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'll remove the debug line and add the classes to the root namespace ASAP.
the main point of the link-list structure is all for different ways of creating control_values (e.g. the old way, the new way, using tensors, using generators, using sympy expressions ..etc) to be used together.
Bump: the checks appear to have gotten stuck 😖 . @NoureldinYosri, you may need to push a change (maybe resolving the conflicts marked) to get it moving again. |
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.
Added a couple of thoughts that we could go over during cynq.
_combinations: Any | ||
_nxt: Optional['ControlValues'] = None |
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.
Another thought, have you considered making a separate ControlValuesProduct
class that merges two (or maybe N) ControlValues objects into a product? That approach would allow for a smaller base class and adhere more to the single responsibility pattern.
|
||
|
||
@ControlValues.register | ||
class SimpleControlValues(ControlValues): |
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.
Let's bikeshed this during synq. I don't think users will find the name intuitive. Perhaps PerQubitControlValues
? Also the docstring could use some examples to make it clear.
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.
One more question
return ( | ||
f'cirq.ControlledGate(sub_gate={self.sub_gate!r}, ' | ||
f'num_controls={self.num_controls()!r})' | ||
) | ||
return ( | ||
f'cirq.ControlledGate(sub_gate={self.sub_gate!r}, ' | ||
f'control_values={self.control_values!r},' | ||
f'control_values={self.control_values.arrangements()!r},' |
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.
Does this still round-trip? Given there's no type information about which subclass of ControlValues
it uses, I'm not seeing how it could.
@daxfohl @95-martin-orion I have a simple way of implementing this which I think addresses all the comments/concerns, I will present it during the cirq meeting for discussion. |
Great! One other thing to look out for is the circuit diagram. The current
logic labels each qubit with the control values. That probably won't work
for the new scenarios. Will need a different approach for them.
…On Mon, Mar 21, 2022, 6:46 AM Noureldin ***@***.***> wrote:
@daxfohl <https://github.com/daxfohl> @95-martin-orion
<https://github.com/95-martin-orion> I have a simple way of implementing
this which I think addresses all the comments/concerns, I will present it
during the cirq meeting.
—
Reply to this email directly, view it on GitHub
<#4714 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAG464PGUDURYJ2V5NQRFTLVBB4VDANCNFSM5JCVGBLA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Discussed on Cirq sync: @NoureldinYosri will write a document capturing the overarching design of this change, we'll discuss and reach a consensus there, then any changes can be reflected here. |
As discussed in the cirq-sync meeting I wrote a design descriping the current approach. |
This can now be closed after #5788 |