Skip to content
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

Add control_keys protocol #4490

Closed
wants to merge 19 commits into from
Closed

Conversation

daxfohl
Copy link
Collaborator

@daxfohl daxfohl commented Sep 8, 2021

Use control keys in circuit construction to block controlled gates "earliest" append method from falling back to before the measurement.

Parts 5&7 of https://tinyurl.com/cirq-feedforward. (I grouped them together in a single PR so that circuit.append could be a POC that control_keys protocol design works as intended).

Replaces #4466 since underlying measurement_keys has changed.

@CirqBot CirqBot added the size: M 50< lines changed <250 label Sep 8, 2021
@google-cla google-cla bot added the cla: yes Makes googlebot stop complaining. label Sep 8, 2021
@daxfohl daxfohl marked this pull request as ready for review September 8, 2021 20:05
@daxfohl daxfohl requested review from cduck, vtomole and a team as code owners September 8, 2021 20:05
@daxfohl daxfohl requested a review from mpharrigan September 8, 2021 20:05
@daxfohl
Copy link
Collaborator Author

daxfohl commented Sep 8, 2021

@95-martin-orion This is ready for review

Comment on lines +917 to +918
def _control_key_names_(self):
return {key for op in self.all_operations() for key in protocols.control_key_names(op)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[No change needed] We'll need to be careful about this - the circuit itself isn't controlled, just the gates inside it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, my initial thought was to have this return only external keys like CircuitOperations. But for diagramming we'll need a list of all the control keys so we can draw the creg lines.

That said, I agree it's odd that the Circuit would have different list than the CircuitOperation. Maybe it's best to have them both hide internal keys, and the diagramming tool can just iterate the circuit's ops and aggregate the control key names manually.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note to self: I think it's better to have the two be consistent, or people will get confused. Probably best to provide an explicit include_internal flag in control_key_names()).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be exposing internal cbits as control_keys. This would really make the semantics confusing as you noted. control_keys for any entity (Operation, CircuitOperation or Circuit) should mean "The classical bits that need to be True for this entity to run". So internal keys are not really part of this. We can have a separate protocol to get the internal keys but I don't see what purpose it can serve that cannot already be served by measurement_keys protocol.

The diagram can iterate through internal operations to figure out which of the measurement keys we need to draw a creg for.

from cirq._doc import doc_private


class SupportsControlKey(Protocol):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean up docstrings in this file - it looks like there was a copy & replace from measurement keys, but the meaning doesn't line up.

k = value.MeasurementKey.parse_serialized(k).name
k_new = self.measurement_key_map.get(k, k)
k_new = key_map.get(k_new, k_new)
if k_new != k:
new_map[k] = k_new
new_op = self.replace(measurement_key_map=new_map)
if len(new_op._measurement_key_names_()) != len(self._measurement_key_names_()):
if len(new_op._measurement_key_names_()) != len(self._measurement_key_names_()) or len(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need a check that this doesn't cause any new conflicts between control keys and measurement keys. Maybe check len(measurement_keys & control_keys) is unchanged. I'm pretty sure we need all three checks. Write some unit tests that cover each case.

@smitsanghavi
Copy link
Collaborator

smitsanghavi commented Sep 13, 2021

I think there is a conflation between some slightly different sets of classical data here that's causing confusion.

  1. Measurement keys that control the operation (or Circuit or CircuitOperation) . This is the set of classical bits that all have to be True for the operation to run, else it doesn't run at all. This is analogous to the controls in ControlledOperation. This is case where the name control_keys fits best. In the current implementation, most higher-level entities (anything except regular Operations) do not return this.
  2. Measurement keys that this operation (or Circuit or CircuitOperation) creates/produces. measurement_keys remains the accurate method to capture these.
  3. Measurement keys that this operation (or Circuit or CircuitOperation) uses (but does not produce). These are the "external control keys" in the discussions above. This is what CircuitOperation.control_key_names currently returns.
  4. A union of 2 and 3. Any measurement keys that this entity produces or uses. This is analogous to a classical version of qubits protocol. Currently this is what control_keys tries to return for Circuits. I think this should be a separate cbits protocol.

If we need to compute "external control keys" (type 3 above) anywhere, we can just resort to set_difference(cbits, measurement_keys).

Comment on lines +917 to +918
def _control_key_names_(self):
return {key for op in self.all_operations() for key in protocols.control_key_names(op)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be exposing internal cbits as control_keys. This would really make the semantics confusing as you noted. control_keys for any entity (Operation, CircuitOperation or Circuit) should mean "The classical bits that need to be True for this entity to run". So internal keys are not really part of this. We can have a separate protocol to get the internal keys but I don't see what purpose it can serve that cannot already be served by measurement_keys protocol.

The diagram can iterate through internal operations to figure out which of the measurement keys we need to draw a creg for.

@daxfohl
Copy link
Collaborator Author

daxfohl commented Sep 13, 2021

@smitsanghavi This is a good breakdown. Actually what I am shooting for in the control_keys protocol is a union of types 1 and 3, to be interpreted as "the control keys that need to be defined prior to running this operation". Or in other words, the classical inputs to this function/operation/subcircuit. This corresponds to measurement_keys protocol as being the classical outputs of the same.

I'm interested to understand more about the CBits augmentation. Are you proposing a new class to represent them or just the protocol? One big difference I see between qubits and cbits is that cbits can be dynamically allocated. They aren't yet, but as we start introducing flow control features like repeat-until, there may be no way to a-priori determine a specific number of cbits in a circuit.

So I was hoping to keep feedforward on the simple side; just base it on measurement keys until some of the other issues like flow control, IQ points, non-unique measurements were better fleshed out. Ultimately I think we'll need something like CValue that can be a range of different data types or lists, and have measurement operations like "xor", "and", "or", "sum", "concat", etc (#4274 (comment)) that operate on different subtypes of CValue. But that's all obviously very nebulous and hand wavy at this point.

@95-martin-orion
Copy link
Collaborator

Discussed in the classical control sync: control_keys should continue returning types 1 and 3, as a parallel to measurement_keys which has a similar behavior. A separate controlled_by method which returns only type 1 will be introduced as part of the ClassicallyControlledOperation class in a subsequent operation.

while k > 0:
k -= 1
if not self._can_commute_past(k, op):
moment = self._moments[k]
if moment.operates_on(op_qubits) or any(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any is not the correct thing here. Use len(...)==0, or bool(...), or just nothing.

@daxfohl
Copy link
Collaborator Author

daxfohl commented Nov 1, 2021

Closing for now in favor of #4610. Will revisit extern control keys later.

@daxfohl daxfohl closed this Nov 1, 2021
CirqBot pushed a commit that referenced this pull request Nov 4, 2021
Use control keys in circuit construction to block controlled gates "earliest" append method from falling back to before the measurement.

Parts 5&7 of https://tinyurl.com/cirq-feedforward. (I grouped them together in a single PR so that circuit.append could be a POC that control_keys protocol design works as intended).

Replaces/simplifies #4490 since we will handle "extern" control keys in subcircuits as a separate PR.
@daxfohl daxfohl deleted the control_keys3 branch November 4, 2021 17:38
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
Use control keys in circuit construction to block controlled gates "earliest" append method from falling back to before the measurement.

Parts 5&7 of https://tinyurl.com/cirq-feedforward. (I grouped them together in a single PR so that circuit.append could be a POC that control_keys protocol design works as intended).

Replaces/simplifies quantumlib#4490 since we will handle "extern" control keys in subcircuits as a separate PR.
harry-phasecraft pushed a commit to PhaseCraft/Cirq that referenced this pull request Oct 31, 2024
Use control keys in circuit construction to block controlled gates "earliest" append method from falling back to before the measurement.

Parts 5&7 of https://tinyurl.com/cirq-feedforward. (I grouped them together in a single PR so that circuit.append could be a POC that control_keys protocol design works as intended).

Replaces/simplifies quantumlib#4490 since we will handle "extern" control keys in subcircuits as a separate PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Makes googlebot stop complaining. size: M 50< lines changed <250
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants