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

[Obs] 4.5 - High-level API #4392

Merged
merged 18 commits into from
Aug 26, 2021
Merged

[Obs] 4.5 - High-level API #4392

merged 18 commits into from
Aug 26, 2021

Conversation

mpharrigan
Copy link
Collaborator

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

    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 PauliStrings
  • 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

@mpharrigan mpharrigan requested a review from cduck as a code owner August 6, 2021 19:01
@mpharrigan mpharrigan requested review from vtomole and a team as code owners August 6, 2021 19:01
@mpharrigan mpharrigan requested a review from maffoo August 6, 2021 19:01
@google-cla google-cla bot added the cla: yes Makes googlebot stop complaining. label Aug 6, 2021
@mpharrigan
Copy link
Collaborator Author

I'm still adding some tests, annotations, and going over the docstrings but feel free to take a look in the mean time

@mpharrigan mpharrigan requested review from 95-martin-orion and removed request for maffoo August 6, 2021 19:05
Copy link
Collaborator

@95-martin-orion 95-martin-orion left a 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.

cirq-core/cirq/work/observable_measurement.py Outdated Show resolved Hide resolved
cirq-core/cirq/work/observable_measurement.py Outdated Show resolved Hide resolved
cirq-core/cirq/work/observable_measurement.py Show resolved Hide resolved
@CirqBot CirqBot added size: XL lines changed >1000 size: M 50< lines changed <250 and removed size: XL lines changed >1000 labels Aug 12, 2021
@mpharrigan
Copy link
Collaborator Author

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

@mpharrigan
Copy link
Collaborator Author

mpharrigan commented Aug 17, 2021

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:

  • lowest: measure_grouped_observables. The user has to group things and deal with InitObsSetting instead of PauliString and gets back raw data (List[BistringAccumulators]) that are grouped like the grouping
  • questionable medium: measure_observables will group for you and take PauliString. It still returns the raw data (List[BitstringAccumulator]) that are grouped like the grouping
  • high, data-science edition: measure_observables_df: get a dataframe with all the results
  • high, Sampler API edition: Sampler.sample_expectation_values takes PauliSums (as well as PauliStrings) and will do the summation for you. It only lets you use RepetitionsStoppingCriteria. It returns values nested by input sweep index, input pauli sum index.

I'm not super happy with the "medium" version's return type. Maybe it can be a flat list of ObservableMeasuredResult

@95-martin-orion
Copy link
Collaborator

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 measure_observables_df as the data science alternative.

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
Copy link
Collaborator Author

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_settings whenever we're using it as a key anywhere and just "pad" when we need the actual circuit parameters.

@mpharrigan
Copy link
Collaborator Author

Lint check is for documenting exceptions. Some offline discussion has built some consensus that we should follow the style guide, especially:

You should not document exceptions that get raised if the API specified in the docstring is violated (because this would paradoxically make behavior under violation of the API part of the API)

and not document "input validation" checks in the docstring.

Copy link
Collaborator

@95-martin-orion 95-martin-orion left a 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.

Comment on lines 54 to 56
from dataclasses import dataclass as json_serializable_dataclass
else:
from cirq.protocols import json_serializable_dataclass
Copy link
Collaborator

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.

Copy link
Collaborator Author

@mpharrigan mpharrigan Aug 23, 2021

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.

Copy link
Collaborator

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.

Copy link
Collaborator Author

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

Comment on lines 32 to 34
from dataclasses import dataclass as json_serializable_dataclass
else:
from cirq.protocols import json_serializable_dataclass
Copy link
Collaborator

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.

cirq-core/cirq/work/observable_measurement_test.py Outdated Show resolved Hide resolved
cirq-core/cirq/work/sampler.py Show resolved Hide resolved
"""
record = dataclasses.asdict(self)
del record['circuit_params']
record.update(**self.circuit_params)
Copy link
Collaborator

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.

Copy link
Collaborator Author

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?

Copy link
Collaborator Author

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

Copy link
Collaborator

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).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed to always prefixing

@mpharrigan
Copy link
Collaborator Author

@95-martin-orion PTAL if you're still around :)

Copy link
Collaborator

@95-martin-orion 95-martin-orion left a 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!

@mpharrigan mpharrigan added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Aug 26, 2021
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Aug 26, 2021
@CirqBot
Copy link
Collaborator

CirqBot commented Aug 26, 2021

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']

@CirqBot CirqBot removed automerge Tells CirqBot to sync and merge this PR. (If it's running.) front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. labels Aug 26, 2021
@mpharrigan mpharrigan added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Aug 26, 2021
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Aug 26, 2021
@CirqBot CirqBot merged commit 0dce2c1 into quantumlib:master Aug 26, 2021
@CirqBot CirqBot removed automerge Tells CirqBot to sync and merge this PR. (If it's running.) front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. labels Aug 26, 2021
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
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
harry-phasecraft pushed a commit to PhaseCraft/Cirq that referenced this pull request Oct 31, 2024
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/expectation-value 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.

3 participants