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 cg.ProcessorSampler #5361

Merged
merged 9 commits into from
May 16, 2022
Merged

Conversation

mpharrigan
Copy link
Collaborator

@mpharrigan mpharrigan commented May 13, 2022

We aim to have interfaces and classical/simulated implementations of all of the cg.engine. classes.

Wheras QuantumEngineSampler turns a cg.Engine into a Sampler, ProcessorSampler turns a AbstractProcessor into a Sampler. Unfortunately, QuantumEngineSampler does not work with an AbstractEngine; hence the new class.

Engine, Engine.get_sampler(), and QuantumEngineSampler can accept a list of processor_ids, so they are preserved for backwards compatibility. QuantumEngineSampler and this behavior should be considered for deprecation.

This PR unifies the implementations of AbstractProcessor.get_sampler() to always just return ProcessorSampler(processor=self).

fixes #5360

Backwards incompatible changes:

  • the return type of AbstractProcessor.get_sampler() has changed. The behavior of the resulting object has not changed for EngineProcessors (unless you're doing something bad, see below), but has changed for SimulatedLocalProcessors. This fixes the issues like SimulatedLocalProcessor .run() vs .get_sampler().run() #5360
  • ProcessorSampler doesn't handle program argument types of EngineProgram. We'd need to teach AbstractProgram some run methods. cc QuantumEngineSampler doesn't allow any control over program id or job id.  #1958 for context
  • Previously, you could have done something weird where you use the sampler from EngineProcessor.get_sampler() to run on processors other than the one pointed to by EngineProcessor. This is no longer possible.

Backwards compatible parts:

  • QuantumEngineSampler still exists and is returned by Engine.get_sampler or cg.get_engine_sampler()

@dstrain115 @verult

@mpharrigan mpharrigan requested review from wcourtney, a team, vtomole, cduck and verult as code owners May 13, 2022 01:24
@mpharrigan mpharrigan requested a review from viathor May 13, 2022 01:24
@CirqBot CirqBot added the size: M 50< lines changed <250 label May 13, 2022
Copy link
Collaborator

@dstrain115 dstrain115 left a comment

Choose a reason for hiding this comment

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

I am good with these changes modulo some double-checking and fixing tests. Thanks for cleaning this interface up!

@@ -105,7 +106,7 @@ def engine(self) -> 'engine_base.Engine':
@util.deprecated_gate_set_parameter
def get_sampler(
self, gate_set: Optional[serializer.Serializer] = None
) -> engine_sampler.QuantumEngineSampler:
) -> 'cg.engine.QuantumProcessorSampler':
Copy link
Collaborator

Choose a reason for hiding this comment

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

@wcourtney Will changing this cause any backwards incompatibility problems? This affects the QCS engine in addition to the simulated version.

I think it should be fine, but want to double-check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a backwards incompatible change for anyone using processor.get_sampler() and trying to use multiple processor_ids. However, this workflow doesn't make any sense and I don't think many people are using processor.get_sampler().

Most people using cg.get_engine().get_sampler() or cg.get_engine_sampler() will be unaffected

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 updated the PR description

# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Optional: should EngineSampler use this class underneath?

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 don't think it (easily) can.

Engine.run_sweep and Processor.run_sweep have meaningfully different semantics: The first will send out a job to one of potentially many processors. This logic happens server-side. While we could theoretically replicate it using a collection of Processors / ProcessorSamplers underneath EngineSampler, it wouldn't be a small change.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am starting to agree with maffoo. It seems like ProcessorSampler is basically the same as a QuantumEngineSampler with a processor set already. Seems like these two can/should be combined.

Copy link
Collaborator

Choose a reason for hiding this comment

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

After off-line conversations, I am fully on-board with this change.

I think we should deprecate QuantumEngineSampler and switch engine.get_sampler(processor_id) to use engine.get_processor(processor_id).get_sampler() (or, alternatively, make QuantumEngineSampler a thin wrapper around this ProcessorSampler functionality). Since this functionality currently supports multiple processor ids and sending an EngineProgram, it probably needs more thought and should be done in a follow-up PR.

Short story, I approve this PR as is.

@mpharrigan
Copy link
Collaborator Author

Thanks for the preliminary check. I'll flesh this out

@mpharrigan
Copy link
Collaborator Author

@dstrain115 @wcourtney PTAL now that this is complete

Copy link
Contributor

@maffoo maffoo left a comment

Choose a reason for hiding this comment

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

The relationship between QuantumEngineSampler and QuantumProcessorSampler is confusing to me. I'd suggest that we only have one of these and that any custom code that currently lives in QuantumEngineSampler methods be moved into EngineProcessor methods that the sampler calls. As for names, I think call this something like ProcessorSampler would probably be best, since it implements the Sampler interface given an AbstractProcessor. We could use that name and deprecate the existing QuantumEngineSampler name.

)

@property
def processor(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: add return type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

@mpharrigan
Copy link
Collaborator Author

any custom code that currently lives in QuantumEngineSampler methods be moved into EngineProcessor methods that the sampler calls.

We can't do this because QuantumEngineSampler can dispatch to multiple processors.

@mpharrigan
Copy link
Collaborator Author

The other glue code relates to repetitions: cirq.Sampler accepts a list of values but AbstractProcessor accepts only one. Lots of discussion about this here: #3265

@mpharrigan mpharrigan changed the title QuantumProcessorSampler ProcessorSampler May 16, 2022
@mpharrigan mpharrigan changed the title ProcessorSampler Add cg.ProcessorSampler May 16, 2022
@maffoo
Copy link
Contributor

maffoo commented May 16, 2022

We can't do this because QuantumEngineSampler can dispatch to multiple processors.

Do we ever use this? To me it makes more sense to just create separate sampler instances for each processor you want to talk to (just like you'd create separate AbstractProcessor instances for each of them). I know we thought early-on that it might make sense to allow sending a program and letting the service decide which of a set of processors to dispatch to, but given NISQ realities and how much device-specific calibration is needed to make this run well this seems unlikely to be useful.

The other glue code relates to repetitions: cirq.Sampler accepts a list of values but AbstractProcessor accepts only one.

True, but the code to handle this is almost identical in both QuantumEngineSampler and QuantumProcessorSampler, so this doesn't seem like a good argument for having separate classes.

@mpharrigan
Copy link
Collaborator Author

I agree that QuantumEngineSampler has annoying semantics and should be deprecated. This would have to be done with some care.

Is your only remaining concern the duplicate code that exists in QuantumEngineSampler?

One cannot refactor QuantumEngineSampler to use ProcessorSampler because of the multi-processor_id and engineprogram support.

Copy link
Contributor

@maffoo maffoo left a comment

Choose a reason for hiding this comment

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

Consider renaming ProcessorSampler to EngineSampler (it is a cirq.Sampler that returns EngineResult). As discussed, we plan to deprecate and remove QuantumEngineSampler, but that can be a separate PR.

@mpharrigan
Copy link
Collaborator Author

I think I'm going to keep it as ProcessorSampler to highlight the link to AbstractProcessor (which happens to return EngineResults). I suspect most folks will not notice or care that it returns EngineResults, which are a subclass of cirq.Result and the cirq.Sampler interface only guarantees the methods and properties on cirq.Result.

@mpharrigan
Copy link
Collaborator Author

opened #5371 for follow-on work

# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Copy link
Collaborator

Choose a reason for hiding this comment

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

After off-line conversations, I am fully on-board with this change.

I think we should deprecate QuantumEngineSampler and switch engine.get_sampler(processor_id) to use engine.get_processor(processor_id).get_sampler() (or, alternatively, make QuantumEngineSampler a thin wrapper around this ProcessorSampler functionality). Since this functionality currently supports multiple processor ids and sending an EngineProgram, it probably needs more thought and should be done in a follow-up PR.

Short story, I approve this PR as is.

@mpharrigan mpharrigan added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label May 16, 2022
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label May 16, 2022
@CirqBot CirqBot merged commit 0a16517 into quantumlib:master May 16, 2022
@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 May 16, 2022
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
We aim to have interfaces and classical/simulated implementations of all of the `cg.engine.` classes.

Wheras `QuantumEngineSampler` turns a `cg.Engine` into a `Sampler`, `ProcessorSampler` turns a `AbstractProcessor` into a `Sampler`. Unfortunately, `QuantumEngineSampler` does **not** work with an `AbstractEngine`; hence the new class.

`Engine`, `Engine.get_sampler()`,  and `QuantumEngineSampler` can accept a _list_ of processor_ids, so they are preserved for backwards compatibility. `QuantumEngineSampler` and this behavior should be considered for deprecation.

This PR unifies the implementations of `AbstractProcessor.get_sampler()` to always just return `ProcessorSampler(processor=self)`. 

fixes quantumlib#5360


Backwards incompatible changes:

 - the return type of `AbstractProcessor.get_sampler()` has changed. The behavior of the resulting object has not changed for EngineProcessors (unless you're doing something bad, see below), but has changed for SimulatedLocalProcessors. This fixes the issues like quantumlib#5360
 - ProcessorSampler doesn't handle `program` argument types of `EngineProgram`. We'd need to teach `AbstractProgram` some run methods. cc quantumlib#1958 for context
 - Previously, you could have done something weird where you use the sampler from EngineProcessor.get_sampler() to run on processors other than the one pointed to by EngineProcessor. This is no longer possible.

Backwards compatible parts:
 - QuantumEngineSampler still exists and is returned by Engine.get_sampler or cg.get_engine_sampler()

@dstrain115 @verult
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: M 50< lines changed <250
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SimulatedLocalProcessor .run() vs .get_sampler().run()
4 participants