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 batch support to cirq Engine client #3114

Merged
merged 11 commits into from
Jul 8, 2020

Conversation

dstrain115
Copy link
Collaborator

  • Adds a run_batch() call to engine that can handle
    lists of circuits and lists of programs.
  • Creates BatchProgram and BatchRunContext objects.
  • Pipes the objects through engine program and engine job.

- Adds a run_batch() call to engine that can handle
lists of circuits and lists of programs.
- Creates BatchProgram and BatchRunContext objects.
- Pipes the objects through engine program and engine job.
@googlebot googlebot added the cla: yes Makes googlebot stop complaining. label Jun 29, 2020
code = qtypes.any_pb2.Any()
batch = v2.batch_pb2.BatchProgram()
for program in programs:
batch.programs.append(gate_set.serialize(program))
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing you can do here is to instead serialize directly into the existing message:

for program in programs:
    gate_set.serialize(program, msg=batch.programs.add())

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, I guess this would get rid of the need for a copy. Done.

@@ -285,6 +285,10 @@ def results(self) -> List[study.TrialResult]:
result_type == 'cirq.api.google.v2.Result'):
v2_parsed_result = v2.result_pb2.Result.FromString(result.value)
self._results = self._get_job_results_v2(v2_parsed_result)
elif result_type == 'cirq.google.api.v2.BatchResult':
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use the built-in Any mechanism for checking types here:

elif result.Is(v2.batch_pb2.BatchResult.DESCRIPTOR):
    ...

We could change other cases as well though we'd have to leave in code to handle old cirq.api.google types if we still need that.

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. I will leave the other cases how they are for now.

for sweep_result in sweep_results:
for trial_result in sweep_result:
trial_results.append(trial_result)
return trial_results
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use the existing _get_job_results_v2 function here:

@classmethod
def _get_batch_results_v2(cls, ...):
    trial_results = []
    for result in results.results:
        trial_results.extend(cls._get_job_results_v2(result))
    return trial_results

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.

elif result_type == 'cirq.google.api.v2.BatchResult':
v2_parsed_result = v2.batch_pb2.BatchResult.FromString(
result.value)
self._results = self._get_batch_results_v2(v2_parsed_result)
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems rather odd to flatten this down to a single list of results across all programs in the batch. I would think we might want a new API to access batch results in a more structured manner.

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 plan is to have something like that in a future PR that extends the interface. This will fit into the current implementation, and then we can have a new API to grab batch results in a follow-up.

Sound good?

Copy link
Collaborator

@dabacon dabacon left a comment

Choose a reason for hiding this comment

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

Drive by :)

Do we want to make EngineProgram and EngineJob be able to know if they were batch or not? Right now I don't think you won't be able to tell this easily?

job_id: Optional[str] = None,
params_list: List[study.Sweepable] = None,
repetitions: int = 1,
processor_ids: Sequence[str] = ('xmonsim',),
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggest not having a default and yelling if not supplied. this is different from other cases, but there is an issue out to not have this default (it is too dangerous, you could think you were doing an experiment, but actually be running on a simulator).

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.

to run the program. Only one of these will be scheduled for
execution.
gate_set: The gate set used to serialize the circuit. The gate set
must be supported by the selected processor.
Copy link
Collaborator

Choose a reason for hiding this comment

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

not a fan of the default being xmon here, but we should at least document (yes run doesn't document this too)

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 it to raise an error, like the other parameters.

@dstrain115
Copy link
Collaborator Author

Do we want to make EngineProgram and EngineJob be able to know if they were batch or not? Right now I don't think you won't be able to tell this easily?

Acknowledged but have not addressed yet. Will fix before submitting.

@dstrain115
Copy link
Collaborator Author

Acknowledged but have not addressed yet. Will fix before submitting.

I have added batch_mode to the classes. For EngineJob, it is purely informational.

@dstrain115 dstrain115 requested review from maffoo and dabacon July 1, 2020 21:33
@dstrain115
Copy link
Collaborator Author

FYI: I gave this a try over the weekend and it worked! Woo woo!

After this is submitted, I think we are good to try this out with a limited set of testers.

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.

Minor comments, then LGTM.

raise ValueError('No parameter list specified')

# Pack the run contexts into batches
batch_context = qtypes.any_pb2.Any()
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to import any_pb2 directly here, rather than rely on it being "reexported" by the qtypes module, but it looks like this is the pattern we're using throughout this module.

Also, consider creating this below, after populating the batch message, when it's ready to be packed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved batch below the for loop. Also changed this to import any_pb2 directly.

job_id: Optional[str] = None,
params_list: List[study.Sweepable] = None,
repetitions: int = 1,
processor_ids: Sequence[str] = ('xmonsim',),
Copy link
Contributor

Choose a reason for hiding this comment

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

Use empty tuple for the default value, as in engine.py above?

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.

params_list: List[study.Sweepable] = None,
repetitions: int = 1,
processor_ids: Sequence[str] = (),
gate_set: serializable_gate_set.SerializableGateSet = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be Optional[serializable_gate_set.SerializableGateSet] if None is allowed.

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. Fixed in a bunch of places in this file.

TrialResults, one for each parameter sweep.
"""
if not gate_set:
raise ValueError('Gate set must be specified.')
Copy link
Contributor

Choose a reason for hiding this comment

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

Move this check to create_batch_program instead (or add a similar check there so that create_batch_program works the same way).

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.

Returns:
A EngineProgram for the newly created program.
"""
gate_set = gate_set or gate_sets.XMON
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this raise an error if gate_set is not given, as does run_batch.

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.

if not program_id:
program_id = _make_random_id('prog-')

code = qtypes.any_pb2.Any()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: move this down below after batch is populated and ready to pack.

It might be nice to create a helper function that packs a proto message into an any and can be called in a single expression. Then we could do, for example:

self.context.client.create_program(..., code=pack_any(batch), ...)

We could also consider changing client.create_program to take google.protobuf.Message for code and have it do the packing internally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Implemented the first suggestion.

I hesitate to mess with the client too much, since it is partially generated by the gapic tools, so, if we regenerate the library, we don't want to mess with any customizations to the greatest extent possible.

Copy link
Collaborator

@dabacon dabacon left a comment

Choose a reason for hiding this comment

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

Yeah!

job_description: Optional[str] = None,
job_labels: Optional[Dict[str, str]] = None,
) -> engine_job.EngineJob:
"""Runs the supplied Circuit via Quantum Engine.Creates
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Circuit -> Circuits

@dstrain115 dstrain115 merged commit b21294b into quantumlib:master Jul 8, 2020
tonybruguier pushed a commit to tonybruguier/Cirq that referenced this pull request Aug 23, 2020
* Add batch support to cirq Engine client

- Adds a run_batch() call to engine that can handle
lists of circuits and lists of programs.
- Creates BatchProgram and BatchRunContext objects.
- Pipes the objects through engine program and engine job.
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants