Skip to content

Commit

Permalink
Add Abstract Engine Interface (#4644)
Browse files Browse the repository at this point in the history
The first four classes define abstract base classes (ABC)
for the four main types of the Engine:

AbstractJob, AbstractProgram, AbstractProcessor, AbstractEngine

These classes closely model the currect EngineJob, EngineProgram,
EngineProcessor, and Engine classes with the methods turned into
abstract models.

This PR is part 1 of a 3 three part series.
  • Loading branch information
dstrain115 authored Nov 18, 2021
1 parent 3af7e89 commit 1d7436f
Show file tree
Hide file tree
Showing 5 changed files with 1,064 additions and 0 deletions.
139 changes: 139 additions & 0 deletions cirq-google/cirq_google/engine/abstract_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Copyright 2021 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# 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.
# coverage: ignore
"""Interface for Engine objects.
This class is an abstract class which all Engine implementations
(production API or locally simulated) should follow.
"""

import abc
import datetime
from typing import Dict, List, Optional, Sequence, Set, Union

import cirq
from cirq_google.engine import abstract_job, abstract_program, abstract_processor
from cirq_google.engine.client import quantum
from cirq_google.serialization import Serializer

VALID_DATE_TYPE = Union[datetime.datetime, datetime.date]


class AbstractEngine(abc.ABC):
"""An abstract object representing a collection of quantum processors.
Each processor within the AbstractEngine can be referenced by a string
identifier through the get_processor interface.
The Engine interface also includes convenience methods to access
programs, jobs, and sampler.
This is an abstract interface and inheritors must implement the abstract methods.
"""

@abc.abstractmethod
def get_program(self, program_id: str) -> abstract_program.AbstractProgram:
"""Returns an existing AbstractProgram given an identifier.
Args:
program_id: Unique ID of the program.
Returns:
An AbstractProgram object for the program.
"""

@abc.abstractmethod
def list_programs(
self,
created_before: Optional[VALID_DATE_TYPE] = None,
created_after: Optional[VALID_DATE_TYPE] = None,
has_labels: Optional[Dict[str, str]] = None,
) -> List[abstract_program.AbstractProgram]:
"""Returns a list of previously executed quantum programs.
Args:
created_after: retrieve programs that were created after this date
or time.
created_before: retrieve programs that were created before this date
or time.
has_labels: retrieve programs that have labels on them specified by
this dict. If the value is set to `*`, programs having the label
regardless of the label value will be returned. For example, to
query programs that have the shape label and have the color
label with value red can be queried using
`{'color': 'red', 'shape': '*'}`
"""

@abc.abstractmethod
def list_jobs(
self,
created_before: Optional[VALID_DATE_TYPE] = None,
created_after: Optional[VALID_DATE_TYPE] = None,
has_labels: Optional[Dict[str, str]] = None,
execution_states: Optional[Set[quantum.enums.ExecutionStatus.State]] = None,
) -> List[abstract_job.AbstractJob]:
"""Returns the list of jobs that match the specified criteria.
All historical jobs can be retrieved using this method and filtering
options are available too, to narrow down the search based on:
* creation time
* job labels
* execution states
Args:
created_after: retrieve jobs that were created after this date
or time.
created_before: retrieve jobs that were created before this date
or time.
has_labels: retrieve jobs that have labels on them specified by
this dict. If the value is set to `*`, jobs having the label
regardless of the label value will be returned. For example, to
query programs that have the shape label and have the color
label with value red can be queried using
{'color': 'red', 'shape':'*'}
execution_states: retrieve jobs that have an execution state that
is contained in `execution_states`. See
`quantum.enums.ExecutionStatus.State` enum for accepted values.
"""

@abc.abstractmethod
def list_processors(self) -> Sequence[abstract_processor.AbstractProcessor]:
"""Returns all processors in this engine visible to the user."""

@abc.abstractmethod
def get_processor(self, processor_id: str) -> abstract_processor.AbstractProcessor:
"""Returns an EngineProcessor for a Quantum Engine processor.
Args:
processor_id: The processor unique identifier.
Returns:
A EngineProcessor for the processor.
"""

@abc.abstractmethod
def get_sampler(
self, processor_id: Union[str, List[str]], gate_set: Serializer
) -> cirq.Sampler:
"""Returns a sampler backed by the engine.
Args:
processor_id: String identifier, or list of string identifiers,
determining which processors may be used when sampling.
gate_set: Determines how to serialize circuits when requesting
samples.
"""
202 changes: 202 additions & 0 deletions cirq-google/cirq_google/engine/abstract_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Copyright 2021 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# 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.
"""A helper for jobs that have been created on the Quantum Engine."""

import abc
from typing import Dict, Iterator, List, Optional, overload, Tuple, TYPE_CHECKING

import cirq
import cirq_google.engine.client.quantum as quantum


if TYPE_CHECKING:
import datetime
import cirq_google.engine.calibration as calibration
import cirq_google.engine.calibration_result as calibration_result
import cirq_google.engine.abstract_engine as abstract_engine
import cirq_google.engine.abstract_processor as abstract_processor
import cirq_google.engine.abstract_program as abstract_program


class AbstractJob(abc.ABC):
"""An abstract object representing a quantum job execution.
This represents the state of a possibly asynchronous Job being
executed by a simulator, the cloud Engine service, or other means.
This is an abstract interface that implementers of services or mocks
should implement. It generally represents the execution of a circuit
using a set of parameters called a sweep. It can also represent the
execution of a batch job (a list of circuit/sweep pairs) or the
execution of a calibration request.
This job may be in a variety of states. It may be scheduling, it may be
executing on a machine, or it may have entered a terminal state
(either succeeding or failing).
`AbstractJob`s can be iterated over, returning `Result`s. These
`Result`s can also be accessed by index. Note that this will block
until the results are returned.
"""

@abc.abstractmethod
def engine(self) -> 'abstract_engine.AbstractEngine':
"""Returns the parent `AbstractEngine` object."""

@abc.abstractmethod
def id(self) -> str:
"""Returns the id of this job."""

@abc.abstractmethod
def program(self) -> 'abstract_program.AbstractProgram':
"""Returns the parent `AbstractProgram`object."""

@abc.abstractmethod
def create_time(self) -> 'datetime.datetime':
"""Returns when the job was created."""

@abc.abstractmethod
def update_time(self) -> 'datetime.datetime':
"""Returns when the job was last updated."""

@abc.abstractmethod
def description(self) -> str:
"""Returns the description of the job."""

@abc.abstractmethod
def set_description(self, description: str) -> 'AbstractJob':
"""Sets the description of the job.
Params:
description: The new description for the job.
Returns:
This `AbstractJob`.
"""

@abc.abstractmethod
def labels(self) -> Dict[str, str]:
"""Returns the labels of the job."""

@abc.abstractmethod
def set_labels(self, labels: Dict[str, str]) -> 'AbstractJob':
"""Sets (overwriting) the labels for a previously created quantum job.
Params:
labels: The entire set of new job labels.
Returns:
This `AbstractJob`.
"""

@abc.abstractmethod
def add_labels(self, labels: Dict[str, str]) -> 'AbstractJob':
"""Adds new labels to a previously created quantum job.
Params:
labels: New labels to add to the existing job labels.
Returns:
This `AbstractJob`.
"""

@abc.abstractmethod
def remove_labels(self, keys: List[str]) -> 'AbstractJob':
"""Removes labels with given keys.
Params:
label_keys: Label keys to remove from the existing job labels.
Returns:
This `AbstractJob`.
"""

@abc.abstractmethod
def processor_ids(self) -> List[str]:
"""Returns the processor ids provided when the job was created."""

@abc.abstractmethod
def execution_status(self) -> quantum.enums.ExecutionStatus.State:
"""Return the execution status of the job."""

@abc.abstractmethod
def failure(self) -> Optional[Tuple[str, str]]:
"""Return failure code and message of the job if present."""

@abc.abstractmethod
def get_repetitions_and_sweeps(self) -> Tuple[int, List[cirq.Sweep]]:
"""Returns the repetitions and sweeps for the job.
Returns:
A tuple of the repetition count and list of sweeps.
"""

@abc.abstractmethod
def get_processor(self) -> Optional['abstract_processor.AbstractProcessor']:
"""Returns the AbstractProcessor for the processor the job is/was run on,
if available, else None."""

@abc.abstractmethod
def get_calibration(self) -> Optional['calibration.Calibration']:
"""Returns the recorded calibration at the time when the job was run, if
one was captured, else None."""

@abc.abstractmethod
def cancel(self) -> Optional[bool]:
"""Cancel the job."""

@abc.abstractmethod
def delete(self) -> Optional[bool]:
"""Deletes the job and result, if any."""

@abc.abstractmethod
def batched_results(self) -> List[List[cirq.Result]]:
"""Returns the job results, blocking until the job is complete.
This method is intended for batched jobs. Instead of flattening
results into a single list, this will return a List[Result]
for each circuit in the batch.
"""

@abc.abstractmethod
def results(self) -> List[cirq.Result]:
"""Returns the job results, blocking until the job is complete."""

@abc.abstractmethod
def calibration_results(self) -> List['calibration_result.CalibrationResult']:
"""Returns the results of a run_calibration() call.
This function will fail if any other type of results were returned.
"""

def __iter__(self) -> Iterator[cirq.Result]:
return iter(self.results())

# pylint: disable=function-redefined
@overload
def __getitem__(self, item: int) -> cirq.Result:
pass

@overload
def __getitem__(self, item: slice) -> List[cirq.Result]:
pass

def __getitem__(self, item):
return self.results()[item]

# pylint: enable=function-redefined

def __len__(self) -> int:
return len(self.results())
Loading

0 comments on commit 1d7436f

Please sign in to comment.