-
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
[Obs] 4.5 - High-level API #4392
Conversation
I'm still adding some tests, annotations, and going over the docstrings but feel free to take a look in the mean time |
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.
Nothing major for now, just a couple of cleanup suggestions.
Thanks for the initial look @95-martin-orion . Since you wrote #3910, I'd like to make this PR get us basically all the way there. I'll re-work, but let me know if you have any ideas or want to set up a video call to sync |
f92fc65 implements the API from #3910 It includes the tests verbatim. One's failing because I think I'm doing the sweep unrolling wrong. I haven't gone through the docstring/input validation again. Those are copied from the original PR by @95-martin-orion Current thinking on the APIs:
I'm not super happy with the "medium" version's return type. Maybe it can be a flat list of |
I think this is a reasonable philosophy for the API - especially now that the Sampler code is (mostly) relying on the public interface of Observable Measurements. Probably can update the docstring there to point specifically at |
qubits = sorted({q for ms in grouped_settings.keys() for q in ms.init_state.qubits}) | ||
qubit_to_index = {q: i for i, q in enumerate(qubits)} | ||
|
||
needs_init_layer = _needs_init_layer(grouped_settings) | ||
measurement_param_circuit = _with_parameterized_layers(circuit, qubits, needs_init_layer) | ||
grouped_settings = { | ||
_pad_setting(max_setting, qubits): settings |
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 call to _pad_setting
has been moved to right near where it's actually needed in _get_params_for_setting
. This was causing a subtle bug with the new test I added that uses a particularly bad grouper: it puts each observable in its own group. However, if you pad the keys in this bad grouping you start to get collisions. We'll keep the user's input max_setting
s whenever we're using it as a key anywhere and just "pad" when we need the actual circuit parameters.
Lint check is for documenting exceptions. Some offline discussion has built some consensus that we should follow the style guide, especially:
and not document "input validation" checks in the docstring. |
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 this pass captures all of my remaining concerns. Feel free to punt to another reviewer for final approval if I'm unavailable when these are resolved.
from dataclasses import dataclass as json_serializable_dataclass | ||
else: | ||
from cirq.protocols import json_serializable_dataclass |
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 the Cirq json_serializable_dataclass
break type checking? Using two different classes under the same name feels really unstable to me.
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 breaks type checking. I introduced #4391 to fix this, but it isn't merged yet.
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.
Got it. I stand by my comment, though - this should wait until #4391 is merged and use the fix it provides.
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.
Ok. We definitely have this pattern in Cirq already as the linked pr demonstrates. It's really a drawback of mypy more than anything else; the decorator works fine
from dataclasses import dataclass as json_serializable_dataclass | ||
else: | ||
from cirq.protocols import json_serializable_dataclass |
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.
Ditto here for using different types during typechecking.
""" | ||
record = dataclasses.asdict(self) | ||
del record['circuit_params'] | ||
record.update(**self.circuit_params) |
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 you think we should check for collisions between record
and self.circuit_params
here? It seems unlikely that someone would use overlapping parameter names, but if they did it would be really tricky to spot what went wrong.
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.
You're right. What should the behavior be in this case? an error? that could be pretty annoying especially if it's after a long data collection run. Prefix the new key names with "circuit_params."
? Probably. Should we only do the prefixing if a collision would have happened, or always?
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.
prefixed names with "circuit_params." if and only if any of the circuit params would collide
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.
Agreed that we shouldn't return an error, though personally I'm in favor of consistency (i.e. always prefix).
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.
changed to always prefixing
@95-martin-orion PTAL if you're still around :) |
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.
Approval conditioned on the changes discussed. Thanks!
Automerge cancelled: A required status check is not present. Missing statuses: ['Build docs', 'Build protos', 'Coverage check', 'Doc test', 'Format check', 'Lint check', 'Misc check', 'Notebook formatting', 'Pytest MacOS (3.7)', 'Pytest MacOS (3.8)', 'Pytest MacOS (3.9)', 'Pytest Ubuntu (3.7)', 'Pytest Ubuntu (3.8)', 'Pytest Ubuntu (3.9)', 'Pytest Windows (3.7)', 'Pytest Windows (3.8)', 'Pytest Windows (3.9)', 'Type check', 'Typescript lint check', 'Typescript tests', 'Typescript tests coverage'] |
Provide two very similar high-level entries into the observable measurement framework. This provides more sensible defaults for some of the execution details and can return nicer values ```python df = measure_observables_df( circuit, [observable], cirq.Simulator(), stopping_criteria='variance', stopping_criteria_val=1e-3 ** 2, ) ``` ------------------ `measure_grouped_settings` lets you control how settings are grouped and returns "raw" data in one container (`BitstringAccumulator`) per group. This level of control is probably desirable for people seriously executing and expensive experiment on the device. The high-level API makes some simplifying assumptions - The qubits are the union of circuit and observable qubits - We will group the settings using a `GROUPER_T` function, by default "greedy" - We'll always initialize settings into the `|00..00>` state, so you can just provide `PauliString`s - You'll probably be using one of the two built-in stopping criteria and can just provide a name and a value instead of grokking these objects. - (for the second entrypoint) you really don't care about how things were grouped and executed, you'd just like the observable values in a dataframe table
Provide two very similar high-level entries into the observable measurement framework. This provides more sensible defaults for some of the execution details and can return nicer values ```python df = measure_observables_df( circuit, [observable], cirq.Simulator(), stopping_criteria='variance', stopping_criteria_val=1e-3 ** 2, ) ``` ------------------ `measure_grouped_settings` lets you control how settings are grouped and returns "raw" data in one container (`BitstringAccumulator`) per group. This level of control is probably desirable for people seriously executing and expensive experiment on the device. The high-level API makes some simplifying assumptions - The qubits are the union of circuit and observable qubits - We will group the settings using a `GROUPER_T` function, by default "greedy" - We'll always initialize settings into the `|00..00>` state, so you can just provide `PauliString`s - You'll probably be using one of the two built-in stopping criteria and can just provide a name and a value instead of grokking these objects. - (for the second entrypoint) you really don't care about how things were grouped and executed, you'd just like the observable values in a dataframe table
Provide two very similar high-level entries into the observable measurement framework. This provides more sensible defaults for some of the execution details and can return nicer values
measure_grouped_settings
lets you control how settings are grouped and returns "raw" data in one container (BitstringAccumulator
) per group. This level of control is probably desirable for people seriously executing and expensive experiment on the device. The high-level API makes some simplifying assumptionsGROUPER_T
function, by default "greedy"|00..00>
state, so you can just providePauliString
s