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

Convert cirq.Result to ABC and add ResultDict implementation #4806

Merged
merged 7 commits into from
Jan 26, 2022

Conversation

maffoo
Copy link
Contributor

@maffoo maffoo commented Jan 6, 2022

This converts cirq.Result into an abstract base class which defines the interface provided by measurement result objects. We introduce a new class cirq.ResultImpl which is the default implementation of this result type, based on a dict mapping string keys to array data. This will allow us to provide alternate implementations as needed, for example one could have a result that is based on a DataFrame and only converts to measurement dict on demand (cf #4774), or a result type that is based on experimental data (such as raw IQ data) and allows accessing this raw data while also converting to the standard measurements dict on demand (see #3868 for other possible Result implementations). Making this separation will also simplify the implementation of repeated measurement keys (#4274), since it will allow us to define the new interface for accessing data from repeated keys separately from the implementation of how this data is stored.

@CirqBot CirqBot added the size: L 250< lines changed <1000 label Jan 6, 2022
@maffoo maffoo requested a review from 95-martin-orion January 6, 2022 19:46
@maffoo
Copy link
Contributor Author

maffoo commented Jan 6, 2022

@daxfohl, FYI

@daxfohl
Copy link
Collaborator

daxfohl commented Jan 6, 2022

I think before doing this, let's make sure it's necessary. It may be possible that there's a unique internal representation that allows us to do everything we need, with high performance in all cases. Like perhaps if we model more closely what happens in hardware, that would be sufficient and performant for all the simulation logic as well. If that's the case then that one implementation would be sufficient and the abstraction would be unnecessary. (Not saying that there definitely is such a representation, just suggesting we should explore that approach first.)

@maffoo
Copy link
Contributor Author

maffoo commented Jan 6, 2022

I think it's unlikely we'll have a single representation. At the very least, for data coming from hardware we sometimes have each measurement represented as a pair of I,Q values (if doing discrimination in software) and sometimes as a single bit (if doing discrimination onboard the control electronics). Most simulators would likely produce bits for measurements, so would never want to use the IQ representation.

@maffoo maffoo force-pushed the u/maffoo/result-impl branch from d91186c to e4799e0 Compare January 7, 2022 00:12
@95-martin-orion
Copy link
Collaborator

I think it's unlikely we'll have a single representation {...}

I'll second this comment, with the added note that IQ points are specific to a particular subset of QC hardware designs. In order for Cirq to support IQ points and remain applicable to other hardware, multiple representations are all but required.

@95-martin-orion
Copy link
Collaborator

That said, as implemented this is a breaking change. To avoid this we could instead name the abstract class AbstractResult and use Result for the implementation - or if Result is not the desired implementation name, then rename it and make Result a deprecated, empty subclass of the new class:

class NewResult(AbstractResult):
    # ...contents of old Result class

@deprecated
class Result(NewResult):
    pass  # acts identically to NewResult, for backwards-compatibility

@95-martin-orion 95-martin-orion added the BREAKING CHANGE For pull requests that are important to mention in release notes. label Jan 11, 2022
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.

Couple of smaller comments in addition to the breaking-change concern.

@@ -80,52 +82,35 @@ def _key_to_str(key: TMeasurementKey) -> str:
return ','.join(str(q) for q in key)


class Result:
class Result(abc.ABC):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Update class docstring to reflect new behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

return ResultImpl(params=self.params, measurements=all_measurements)


class ResultImpl(Result):
Copy link
Collaborator

Choose a reason for hiding this comment

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

With an eye towards #3868, it may be preferable to name this implementation something that clearly indicates that its "default" storage type is keyed measurements (as opposed to raw values, pandas DataFrame, or something more exotic like IQ points).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm certainly open to renaming this, but not sure what a good name would be. Any specific suggestions?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The simplest would be "KeyedResultImpl" or "MeasurementKeyResultImpl" - the others could then be e.g. "RawResultImpl" and "DataFrameResultImpl".

@maffoo
Copy link
Contributor Author

maffoo commented Jan 14, 2022

Re: backwards compatibility, I'm personally inclined to keep the simple name Result as the name of the interface, and take the hit of minor breakage (I wish we had taken this road for Circuit vs AbstractCircuit, but I guess that ship has sailed). I think the breakage should be pretty small since most code that uses results does not construct them directly (other than simulator implementations and hardware interfaces) and so most users should not be affected by this change. The advantage we get is that all functions currently annotated as returning cirq.Result will immediately work with any result type, without having to change their annotations, including important interfaces like cirq.Sampler. (In the case of Circuit we probably still have a long tail of functions that should be changed to take AbstractCircuit, and it'd be nice to avoid that.)

In addition, to ease the transition I've added a Result.__new__ method that will construct a ResultImpl so calls to the Result constructor can go through a standard deprecation cycle.

"""
"""The results of multiple executions of a circuit with fixed parameters."""

def __new__(cls, *args, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Huh, this is a really neat workaround.

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 I'm sold on the ResultImpl vs AbstractResult discussion, especially with the fix to avoid breaking Result(...) calls. Left a comment on names for ResultImpl, but other than that this is ready.

@maffoo maffoo changed the title Convert cirq.Result to ABC and add ResultImpl implementation Convert cirq.Result to ABC and add ResultDict implementation Jan 25, 2022
@maffoo
Copy link
Contributor Author

maffoo commented Jan 25, 2022

I've renamed the concrete implementation class ResultDict. I prefer Result*** over ***Result because it keeps things alphabetically close, but I don't have a strong preference here. Opening this up for brief bike-shedding from maintainers if anyone feels strongly, but I'd like to merge today if possible.

@maffoo maffoo merged commit b0519b3 into master Jan 26, 2022
@maffoo maffoo deleted the u/maffoo/result-impl branch January 26, 2022 20:13
Strilanc pushed a commit to quantumlib/Stim that referenced this pull request Feb 4, 2022
The `cirq.Result` constructor is deprecated as of quantumlib/Cirq#4806. Instead, we should use a concrete type like `cirq.ResultDict`. This changes `StimSampler` to use `cirq.ResultDict` when available, to avoid deprecation warnings.

I'm also curious to know what is the support policy for stimcirq supporting different pyle versions. This workaround can be removed whenever support for cirq < 0.14 is dropped.
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
…lib#4806)

This converts cirq.Result into an abstract base class which defines the interface provided by measurement result objects. We introduce a new class cirq.ResultDict which is the default implementation of this result type, based on a dict mapping string keys to array data. This will allow us to provide alternate implementations as needed, for example a result type that is based on a DataFrame and only converts to measurement dict on demand (cf quantumlib#4774), or a result type that is based on experimental data (such as raw IQ data) and allows accessing this raw data while also converting to the standard measurements dict on demand (see quantumlib#3868 for other possible Result implementations). Making this separation will also simplify the implementation of repeated measurement keys (quantumlib#4274), since it will allow us to define the new interface for accessing data from repeated keys separately from the implementation of how this data is stored.

Review: @95-martin-orion
harry-phasecraft pushed a commit to PhaseCraft/Cirq that referenced this pull request Oct 31, 2024
…lib#4806)

This converts cirq.Result into an abstract base class which defines the interface provided by measurement result objects. We introduce a new class cirq.ResultDict which is the default implementation of this result type, based on a dict mapping string keys to array data. This will allow us to provide alternate implementations as needed, for example a result type that is based on a DataFrame and only converts to measurement dict on demand (cf quantumlib#4774), or a result type that is based on experimental data (such as raw IQ data) and allows accessing this raw data while also converting to the standard measurements dict on demand (see quantumlib#3868 for other possible Result implementations). Making this separation will also simplify the implementation of repeated measurement keys (quantumlib#4274), since it will allow us to define the new interface for accessing data from repeated keys separately from the implementation of how this data is stored.

Review: @95-martin-orion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BREAKING CHANGE For pull requests that are important to mention in release notes. size: L 250< lines changed <1000
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants