Skip to content

Commit

Permalink
Deprecate QuantumEngineSampler (#5432)
Browse files Browse the repository at this point in the history
* Deprecate QuantumEngineSampler

- Deprecates this class in favor of ProcessorSampler
- Moves get_engine_sampler into engine with the other utility functions
- Change engine.get_sampler to only support a single processor and have
  it point to the processor's sampler.
- Fix calibration workflow to use ProcessorSampler

Fixes: #5371
  • Loading branch information
dstrain115 authored Jun 3, 2022
1 parent 74e6c7a commit fdb1527
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 53 deletions.
40 changes: 19 additions & 21 deletions cirq-google/cirq_google/calibration/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
LocalXEBPhasedFSimCalibrationRequest,
)
from cirq_google.calibration.xeb_wrapper import run_local_xeb_calibration
from cirq_google.engine import Engine, QuantumEngineSampler, util
from cirq_google.engine import AbstractProcessor, AbstractEngine, ProcessorSampler, util
from cirq_google.serialization.serializer import Serializer

_CALIBRATION_IRRELEVANT_GATES = cirq.MeasurementGate, cirq.WaitGate
Expand Down Expand Up @@ -730,8 +730,7 @@ def _merge_into_calibrations(

def _run_calibrations_via_engine(
calibration_requests: Sequence[PhasedFSimCalibrationRequest],
engine: Engine,
processor_id: str,
processor: AbstractProcessor,
max_layers_per_request: int = 1,
progress_func: Optional[Callable[[int, int], None]] = None,
):
Expand All @@ -750,7 +749,7 @@ def _run_calibrations_via_engine(
]

for cal_layers in nested_calibration_layers:
job = engine.run_calibration(cal_layers, processor_id=processor_id)
job = processor.run_calibration(cal_layers)
request_results = job.calibration_results()
results += [
calibration.parse_result(result, job)
Expand All @@ -776,7 +775,7 @@ def _run_local_calibrations_via_sampler(
@util.deprecated_gate_set_parameter
def run_calibrations(
calibrations: Sequence[PhasedFSimCalibrationRequest],
sampler: Union[Engine, cirq.Sampler],
sampler: Union[AbstractEngine, cirq.Sampler],
processor_id: Optional[str] = None,
gate_set: Optional[Serializer] = None,
max_layers_per_request: int = 1,
Expand All @@ -787,7 +786,7 @@ def run_calibrations(
Args:
calibrations: List of calibrations to perform described in a request object.
sampler: cirq_google.Engine or cirq.Sampler object used for running the calibrations. When
sampler is cirq_google.Engine or cirq_google.QuantumEngineSampler object then the
sampler is cirq_google.Engine or cirq_google.ProcessorSampler object then the
calibrations are issued against a Google's quantum device. The only other sampler
supported for simulation purposes is cirq_google.PhasedFSimEngineSimulator.
processor_id: Used when sampler is cirq_google.Engine object and passed to
Expand Down Expand Up @@ -824,24 +823,23 @@ def run_calibrations(
)
(calibration_request_type,) = calibration_request_types

if isinstance(sampler, Engine):
engine: Optional[Engine] = sampler
elif isinstance(sampler, QuantumEngineSampler):
engine = sampler.engine
(processor_id,) = sampler._processor_ids
else:
engine = None

if engine is not None:
if isinstance(sampler, AbstractEngine):
if processor_id is None:
raise ValueError('processor_id must be provided.') # coverage: ignore
processor: Optional[AbstractProcessor] = sampler.get_processor(processor_id=processor_id)
elif isinstance(sampler, ProcessorSampler):
processor = sampler.processor
else:
processor = None

if processor is not None:

if calibration_request_type == LocalXEBPhasedFSimCalibrationRequest:
engine_sampler = engine.get_sampler(processor_id=processor_id)
engine_sampler = processor.get_sampler()
return _run_local_calibrations_via_sampler(calibrations, engine_sampler)

return _run_calibrations_via_engine(
calibrations, engine, processor_id, max_layers_per_request, progress_func
calibrations, processor, max_layers_per_request, progress_func
)

if calibration_request_type == LocalXEBPhasedFSimCalibrationRequest:
Expand Down Expand Up @@ -1165,7 +1163,7 @@ def as_circuit(self) -> cirq.Circuit:
@util.deprecated_gate_set_parameter
def run_floquet_characterization_for_moments(
circuit: cirq.Circuit,
sampler: Union[Engine, cirq.Sampler],
sampler: Union[AbstractEngine, cirq.Sampler],
processor_id: Optional[str] = None,
gate_set: Optional[Serializer] = None,
options: FloquetPhasedFSimCalibrationOptions = WITHOUT_CHI_FLOQUET_PHASED_FSIM_CHARACTERIZATION,
Expand All @@ -1185,7 +1183,7 @@ def run_floquet_characterization_for_moments(
Args:
circuit: Circuit to characterize.
sampler: cirq_google.Engine or cirq.Sampler object used for running the calibrations. When
sampler is cirq_google.Engine or cirq_google.QuantumEngineSampler object then the
sampler is cirq_google.Engine or cirq_google.ProcessorSampler object then the
calibrations are issued against a Google's quantum device. The only other sampler
supported for simulation purposes is cirq_google.PhasedFSimEngineSimulator.
processor_id: Used when sampler is cirq_google.Engine object and passed to
Expand Down Expand Up @@ -1236,7 +1234,7 @@ def run_floquet_characterization_for_moments(
@util.deprecated_gate_set_parameter
def run_zeta_chi_gamma_compensation_for_moments(
circuit: cirq.Circuit,
sampler: Union[Engine, cirq.Sampler],
sampler: Union[AbstractEngine, cirq.Sampler],
processor_id: Optional[str] = None,
gate_set: Optional[Serializer] = None,
options: FloquetPhasedFSimCalibrationOptions = (
Expand All @@ -1261,7 +1259,7 @@ def run_zeta_chi_gamma_compensation_for_moments(
Args:
circuit: Circuit to characterize and calibrate.
sampler: cirq_google.Engine or cirq.Sampler object used for running the calibrations. When
sampler is cirq_google.Engine or cirq_google.QuantumEngineSampler object then the
sampler is cirq_google.Engine or cirq_google.ProcessorSampler object then the
calibrations are issued against a Google's quantum device. The only other sampler
supported for simulation purposes is cirq_google.PhasedFSimEngineSimulator.
processor_id: Used when sampler is cirq_google.Engine object and passed to
Expand Down
29 changes: 18 additions & 11 deletions cirq-google/cirq_google/calibration/workflow_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1006,10 +1006,14 @@ def test_run_calibrations():
job = cirq_google.engine.EngineJob('project_id', 'program_id', 'job_id', None)
job._calibration_results = [result]

engine = mock.MagicMock(spec=cirq_google.Engine)
engine.run_calibration.return_value = job
processor = mock.MagicMock(spec=cirq_google.engine.SimulatedLocalProcessor)
processor.processor_id = 'qproc'
engine = cirq_google.engine.SimulatedLocalEngine([processor])
processor.engine.return_value = engine
processor.run_calibration.return_value = job
progress_calls = []

sampler = cirq_google.QuantumEngineSampler(engine=engine, processor_id='qproc')
sampler = cirq_google.ProcessorSampler(processor=processor)

progress_calls = []

Expand Down Expand Up @@ -1105,9 +1109,11 @@ def test_run_characterization_with_engine():
job = cirq_google.engine.EngineJob('project_id', 'program_id', 'job_id', None)
job._calibration_results = [result]

engine = mock.MagicMock(spec=cirq_google.Engine)
engine.run_calibration.return_value = job

processor = mock.MagicMock(spec=cirq_google.engine.SimulatedLocalProcessor)
processor.processor_id = 'qproc'
engine = cirq_google.engine.SimulatedLocalEngine([processor])
processor.engine.return_value = engine
processor.run_calibration.return_value = job
progress_calls = []

def progress(step: int, steps: int) -> None:
Expand Down Expand Up @@ -1267,8 +1273,11 @@ def test_run_floquet_characterization_for_moments():
)
]

engine = mock.MagicMock(spec=cirq_google.Engine)
engine.run_calibration.return_value = job
processor = mock.MagicMock(spec=cirq_google.engine.SimulatedLocalProcessor)
processor.processor_id = 'qproc'
engine = cirq_google.engine.SimulatedLocalEngine([processor])
processor.engine.return_value = engine
processor.run_calibration.return_value = job

circuit_with_calibration, requests = workflow.run_floquet_characterization_for_moments(
circuit, engine, 'qproc', options=options
Expand Down Expand Up @@ -1657,9 +1666,7 @@ def test_run_zeta_chi_gamma_calibration_for_moments_no_chi() -> None:
)


_MOCK_ENGINE_SAMPLER = mock.MagicMock(
spec=cirq_google.QuantumEngineSampler, _processor_ids=['my_fancy_processor'], _gate_set='test'
)
_MOCK_ENGINE_SAMPLER = mock.MagicMock(spec=cirq_google.ProcessorSampler)


@pytest.mark.parametrize('sampler_engine', [cirq.Simulator, _MOCK_ENGINE_SAMPLER])
Expand Down
3 changes: 2 additions & 1 deletion cirq-google/cirq_google/engine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
get_engine,
get_engine_calibration,
get_engine_device,
get_engine_sampler,
ProtoVersion,
)

Expand All @@ -66,7 +67,7 @@
estimate_run_sweep_time,
)

from cirq_google.engine.engine_sampler import get_engine_sampler, QuantumEngineSampler
from cirq_google.engine.engine_sampler import QuantumEngineSampler

from cirq_google.engine.validating_sampler import ValidatingSampler

Expand Down
42 changes: 36 additions & 6 deletions cirq-google/cirq_google/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
engine_job,
engine_processor,
engine_program,
engine_sampler,
util,
)
from cirq_google.cloud import quantum
Expand Down Expand Up @@ -782,7 +781,7 @@ def get_processor(self, processor_id: str) -> engine_processor.EngineProcessor:
@util.deprecated_gate_set_parameter
def sampler(
self, processor_id: Union[str, List[str]], gate_set: Optional[Serializer] = None
) -> engine_sampler.QuantumEngineSampler:
) -> 'cirq_google.ProcessorSampler':
"""Returns a sampler backed by the engine.
Args:
Expand All @@ -801,21 +800,29 @@ def sampler(
@util.deprecated_gate_set_parameter
def get_sampler(
self, processor_id: Union[str, List[str]], gate_set: Optional[Serializer] = None
) -> engine_sampler.QuantumEngineSampler:
) -> 'cirq_google.ProcessorSampler':
"""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.
processor_id: String identifier of which processor should be used to sample.
gate_set: A `Serializer` that determines how to serialize
circuits when requesting samples.
Returns:
A `cirq.Sampler` instance (specifically a `engine_sampler.QuantumEngineSampler`
that will send circuits to the Quantum Computing Service
when sampled.
Raises:
ValueError: if a list of processors is provided. This is no longer supported.
"""
return engine_sampler.QuantumEngineSampler(engine=self, processor_id=processor_id)
if not isinstance(processor_id, str):
raise ValueError(
f'Passing a list of processors ({processor_id}) '
'to get_sampler() no longer supported. Use Engine.run() instead if '
'you need to specify a list.'
)
return self.get_processor(processor_id).get_sampler()


def get_engine(project_id: Optional[str] = None) -> Engine:
Expand Down Expand Up @@ -877,3 +884,26 @@ def get_engine_calibration(
May return None if no calibration metrics exist for the device.
"""
return get_engine(project_id).get_processor(processor_id).get_current_calibration()


def get_engine_sampler(
processor_id: str, project_id: Optional[str] = None
) -> 'cirq_google.ProcessorSampler':
"""Get an EngineSampler assuming some sensible defaults.
This uses the environment variable GOOGLE_CLOUD_PROJECT for the Engine
project_id, unless set explicitly.
Args:
processor_id: Engine processor ID (from Cloud console or
``Engine.list_processors``).
project_id: Optional explicit Google Cloud project id. Otherwise,
this defaults to the environment variable GOOGLE_CLOUD_PROJECT.
By using an environment variable, you can avoid hard-coding
personal project IDs in shared code.
Raises:
EnvironmentError: If no project_id is specified and the environment
variable GOOGLE_CLOUD_PROJECT is not set.
"""
return get_engine(project_id).get_processor(processor_id).get_sampler()
10 changes: 3 additions & 7 deletions cirq-google/cirq_google/engine/engine_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import cirq_google


@cirq._compat.deprecated_class(deadline='v0.16', fix='Use cirq_google.ProcessorSampler instead.')
class QuantumEngineSampler(cirq.Sampler):
"""A sampler that samples from processors managed by the Quantum Engine.
Expand Down Expand Up @@ -106,15 +107,10 @@ def engine(self) -> 'cirq_google.Engine':
return self._engine


@cirq._compat.deprecated_parameter(
deadline='v0.15',
fix='Remove the "gate_set_name" parameter.',
parameter_desc='gate_set_name',
match=lambda args, kwargs: 'gate_set_name' in kwargs or len(args) > 1,
)
@cirq._compat.deprecated(deadline='v0.16', fix='Use cirq_google.get_engine_sampler()')
def get_engine_sampler(
processor_id: str, gate_set_name: str = '', project_id: Optional[str] = None
) -> 'cirq_google.QuantumEngineSampler':
) -> cirq.Sampler:
"""Get an EngineSampler assuming some sensible defaults.
This uses the environment variable GOOGLE_CLOUD_PROJECT for the Engine
Expand Down
35 changes: 28 additions & 7 deletions cirq-google/cirq_google/engine/engine_sampler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
@pytest.mark.parametrize('circuit', [cirq.Circuit(), cirq.FrozenCircuit()])
def test_run_circuit(circuit):
engine = mock.Mock()
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
params = [cirq.ParamResolver({'a': 1})]
sampler.run_sweep(circuit, params, 5)
engine.run_sweep.assert_called_with(
Expand All @@ -34,7 +37,10 @@ def test_run_circuit(circuit):

def test_run_engine_program():
engine = mock.Mock()
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
program = mock.Mock(spec=cg.EngineProgram)
params = [cirq.ParamResolver({'a': 1})]
sampler.run_sweep(program, params, 5)
Expand All @@ -44,7 +50,10 @@ def test_run_engine_program():

def test_run_batch():
engine = mock.Mock()
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
a = cirq.LineQubit(0)
circuit1 = cirq.Circuit(cirq.X(a))
circuit2 = cirq.Circuit(cirq.Y(a))
Expand All @@ -60,7 +69,10 @@ def test_run_batch():

def test_run_batch_identical_repetitions():
engine = mock.Mock()
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
a = cirq.LineQubit(0)
circuit1 = cirq.Circuit(cirq.X(a))
circuit2 = cirq.Circuit(cirq.Y(a))
Expand All @@ -76,7 +88,10 @@ def test_run_batch_identical_repetitions():

def test_run_batch_bad_number_of_repetitions():
engine = mock.Mock()
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
a = cirq.LineQubit(0)
circuit1 = cirq.Circuit(cirq.X(a))
circuit2 = cirq.Circuit(cirq.Y(a))
Expand All @@ -93,7 +108,10 @@ def test_run_batch_differing_repetitions():
job = mock.Mock()
job.results.return_value = []
engine.run_sweep.return_value = job
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
a = cirq.LineQubit(0)
circuit1 = cirq.Circuit(cirq.X(a))
circuit2 = cirq.Circuit(cirq.Y(a))
Expand All @@ -111,7 +129,10 @@ def test_run_batch_differing_repetitions():

def test_engine_sampler_engine_property():
engine = mock.Mock()
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
with cirq.testing.assert_deprecated(
'Use cirq_google.ProcessorSampler instead.', deadline='v0.16'
):
sampler = cg.QuantumEngineSampler(engine=engine, processor_id='tmp')
assert sampler.engine is engine


Expand Down
3 changes: 3 additions & 0 deletions cirq-google/cirq_google/engine/engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,9 @@ def test_sampler(client):
with cirq.testing.assert_deprecated('sampler', deadline='1.0'):
_ = engine.sampler(processor_id='tmp')

with pytest.raises(ValueError, match='list of processors'):
_ = engine.get_sampler(['test1', 'test2'])


@mock.patch('cirq_google.cloud.quantum.QuantumEngineServiceClient')
def test_get_engine(build):
Expand Down

0 comments on commit fdb1527

Please sign in to comment.