Skip to content

Enforces specifying run_name and device_config_name on Engine interfaces #6649

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

Merged
merged 7 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions cirq-google/cirq_google/engine/abstract_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class AbstractProcessor(abc.ABC):
async def run_async(
self,
program: cirq.Circuit,
*,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: try to remove the * and see what breaks. if a few things break then we can fix them

Copy link
Collaborator

@pavoljuhas pavoljuhas Jun 25, 2024

Choose a reason for hiding this comment

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

nit: try to remove the * and see what breaks. if a few things break then we can fix them

This may be dangerous, because the main-branch version had 2 optional string arguments at those positions.
Requiring everything to be a kwarg will make sure that string arguments are not mixed up.

run_name: str,
device_config_name: str,
program_id: Optional[str] = None,
job_id: Optional[str] = None,
param_resolver: Optional[cirq.ParamResolver] = None,
Expand All @@ -64,14 +67,18 @@ async def run_async(
program_labels: Optional[Dict[str, str]] = None,
job_description: Optional[str] = None,
job_labels: Optional[Dict[str, str]] = None,
run_name: str = "",
device_config_name: str = "",
) -> cirq.Result:
"""Runs the supplied Circuit on this processor.

Args:
program: The Circuit to execute. If a circuit is
provided, a moment by moment schedule will be used.
run_name: A unique identifier representing an automation run for the
processor. An Automation Run contains a collection of device
configurations for the processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
program_id: A user-provided identifier for the program. This must
be unique within the Google Cloud project being used. If this
parameter is not provided, a random id of the format
Expand All @@ -87,12 +94,7 @@ async def run_async(
program_labels: Optional set of labels to set on the program.
job_description: An optional description to set on the job.
job_labels: Optional set of labels to set on the job.
run_name: A unique identifier representing an automation run for the
processor. An Automation Run contains a collection of device
configurations for the processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.

Returns:
A single Result for this run.
"""
Expand All @@ -117,6 +119,9 @@ async def run_async(
async def run_sweep_async(
self,
program: cirq.AbstractCircuit,
*,
run_name: str,
device_config_name: str,
program_id: Optional[str] = None,
job_id: Optional[str] = None,
params: cirq.Sweepable = None,
Expand All @@ -125,8 +130,6 @@ async def run_sweep_async(
program_labels: Optional[Dict[str, str]] = None,
job_description: Optional[str] = None,
job_labels: Optional[Dict[str, str]] = None,
run_name: str = "",
device_config_name: str = "",
) -> 'abstract_job.AbstractJob':
"""Runs the supplied Circuit on this processor.

Expand All @@ -135,6 +138,12 @@ async def run_sweep_async(
Args:
program: The Circuit to execute. If a circuit is
provided, a moment by moment schedule will be used.
run_name: A unique identifier representing an automation run for the
processor. An Automation Run contains a collection of device
configurations for the processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
program_id: A user-provided identifier for the program. This must
be unique within the Google Cloud project being used. If this
parameter is not provided, a random id of the format
Expand All @@ -150,12 +159,7 @@ async def run_sweep_async(
program_labels: Optional set of labels to set on the program.
job_description: An optional description to set on the job.
job_labels: Optional set of labels to set on the job.
run_name: A unique identifier representing an automation run for the
processor. An Automation Run contains a collection of device
configurations for the processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.

Returns:
An AbstractJob. If this is iterated over it returns a list of
`cirq.Result`, one for each parameter sweep.
Expand Down
17 changes: 9 additions & 8 deletions cirq-google/cirq_google/engine/engine_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ def get_sampler(
async def run_sweep_async(
self,
program: cirq.AbstractCircuit,
*,
run_name: str,
device_config_name: str,
program_id: Optional[str] = None,
job_id: Optional[str] = None,
params: cirq.Sweepable = None,
Expand All @@ -123,8 +126,6 @@ async def run_sweep_async(
program_labels: Optional[Dict[str, str]] = None,
job_description: Optional[str] = None,
job_labels: Optional[Dict[str, str]] = None,
run_name: str = "",
device_config_name: str = "",
) -> 'abstract_job.AbstractJob':
"""Runs the supplied Circuit on this processor.

Expand All @@ -134,6 +135,12 @@ async def run_sweep_async(
Args:
program: The Circuit to execute. If a circuit is
provided, a moment by moment schedule will be used.
run_name: A unique identifier representing an automation run for the
processor. An Automation Run contains a collection of device
configurations for the processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
program_id: A user-provided identifier for the program. This must
be unique within the Google Cloud project being used. If this
parameter is not provided, a random id of the format
Expand All @@ -149,12 +156,6 @@ async def run_sweep_async(
program_labels: Optional set of labels to set on the program.
job_description: An optional description to set on the job.
job_labels: Optional set of labels to set on the job.
run_name: A unique identifier representing an automation run for the
processor. An Automation Run contains a collection of device
configurations for the processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
Returns:
An AbstractJob. If this is iterated over it returns a list of
`cirq.Result`, one for each parameter sweep.
Expand Down
10 changes: 8 additions & 2 deletions cirq-google/cirq_google/engine/engine_processor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,10 @@ def test_run_sweep_params_with_unary_rpcs(client):

processor = cg.EngineProcessor('a', 'p', EngineContext(enable_streaming=False))
job = processor.run_sweep(
program=_CIRCUIT, params=[cirq.ParamResolver({'a': 1}), cirq.ParamResolver({'a': 2})]
program=_CIRCUIT,
params=[cirq.ParamResolver({'a': 1}), cirq.ParamResolver({'a': 2})],
run_name="run",
device_config_name="config_alias",
)
results = job.results()
assert len(results) == 2
Expand Down Expand Up @@ -887,7 +890,10 @@ def test_run_sweep_params_with_stream_rpcs(client):

processor = cg.EngineProcessor('a', 'p', EngineContext(enable_streaming=True))
job = processor.run_sweep(
program=_CIRCUIT, params=[cirq.ParamResolver({'a': 1}), cirq.ParamResolver({'a': 2})]
program=_CIRCUIT,
params=[cirq.ParamResolver({'a': 1}), cirq.ParamResolver({'a': 2})],
run_name="run",
device_config_name="config_alias",
)
results = job.results()
assert len(results) == 2
Expand Down
44 changes: 20 additions & 24 deletions cirq-google/cirq_google/engine/engine_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,28 @@ def __init__(
async def run_sweep_async(
self,
processor_id: str,
*,
run_name: str,
device_config_name: str,
job_id: Optional[str] = None,
params: cirq.Sweepable = None,
repetitions: int = 1,
description: Optional[str] = None,
labels: Optional[Dict[str, str]] = None,
*,
run_name: str = "",
device_config_name: str = "",
) -> engine_job.EngineJob:
"""Runs the program on the QuantumEngine.

In contrast to run, this runs across multiple parameter sweeps, and
does not block until a result is returned.

Args:
processor_id: Processor id for running the program.
run_name: A unique identifier representing an automation run for the
specified processor. An Automation Run contains a collection of
device configurations for a processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
job_id: Optional job id to use. If this is not provided, a random id
of the format 'job-################YYMMDD' will be generated,
where # is alphanumeric and YYMMDD is the current year, month,
Expand All @@ -86,15 +93,6 @@ async def run_sweep_async(
repetitions: The number of circuit repetitions to run.
description: An optional description to set on the job.
labels: Optional set of labels to set on the job.
processor_id: Processor id for running the program.
run_name: A unique identifier representing an automation run for the
specified processor. An Automation Run contains a collection of
device configurations for a processor. If specified, `processor_id`
is required to be set.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
If specified, `processor_id` is required to be set.

Returns:
An EngineJob. If this is iterated over it returns a list of
Expand Down Expand Up @@ -131,19 +129,26 @@ async def run_sweep_async(

async def run_async(
self,
*,
processor_id: str,
run_name: str,
device_config_name: str,
job_id: Optional[str] = None,
param_resolver: cirq.ParamResolver = cirq.ParamResolver({}),
repetitions: int = 1,
description: Optional[str] = None,
labels: Optional[Dict[str, str]] = None,
*,
run_name: str = "",
device_config_name: str = "",
) -> cirq.Result:
"""Runs the supplied Circuit via Quantum Engine.

Args:
processor_id: Processor id for running the program.
run_name: A unique identifier representing an automation run for the
specified processor. An Automation Run contains a collection of
device configurations for a processor.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
job_id: Optional job id to use. If this is not provided, a random id
of the format 'job-################YYMMDD' will be generated,
where # is alphanumeric and YYMMDD is the current year, month,
Expand All @@ -152,15 +157,6 @@ async def run_async(
repetitions: The number of repetitions to simulate.
description: An optional description to set on the job.
labels: Optional set of labels to set on the job.
processor_id: Processor id for running the program.
run_name: A unique identifier representing an automation run for the
specified processor. An Automation Run contains a collection of
device configurations for a processor. If specified, `processor_id`
is required to be set.
device_config_name: An identifier used to select the processor configuration
utilized to run the job. A configuration identifies the set of
available qubits, couplers, and supported gates in the processor.
If specified, `processor_id` is required to be set.

Returns:
A single Result for this run.
Expand Down
14 changes: 12 additions & 2 deletions cirq-google/cirq_google/engine/engine_program_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ def test_run_sweeps_delegation(create_job_async):
program = cg.EngineProgram('my-proj', 'my-prog', EngineContext())
param_resolver = cirq.ParamResolver({})
job = program.run_sweep(
job_id='steve', repetitions=10, params=param_resolver, processor_id='mine'
job_id='steve',
repetitions=10,
params=param_resolver,
processor_id='mine',
run_name="run_name",
device_config_name="config",
)
assert job._job == quantum.QuantumJob()

Expand Down Expand Up @@ -134,7 +139,12 @@ def test_run_delegation(create_job_async, get_results_async):
program = cg.EngineProgram('a', 'b', EngineContext())
param_resolver = cirq.ParamResolver({})
results = program.run(
job_id='steve', repetitions=10, param_resolver=param_resolver, processor_id='mine'
job_id='steve',
repetitions=10,
param_resolver=param_resolver,
processor_id='mine',
run_name="run_name",
device_config_name="config",
)

assert results == cg.EngineResult(
Expand Down
17 changes: 14 additions & 3 deletions cirq-google/cirq_google/engine/engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,12 +533,21 @@ def test_run_multiple_times(client):

engine = cg.Engine(project_id='proj', proto_version=cg.engine.engine.ProtoVersion.V2)
program = engine.create_program(program=_CIRCUIT)
program.run(processor_id='processor0', param_resolver=cirq.ParamResolver({'a': 1}))
program.run(
processor_id='processor0',
param_resolver=cirq.ParamResolver({'a': 1}),
run_name="run",
device_config_name="config_alias",
)
run_context = v2.run_context_pb2.RunContext()
client().create_job_async.call_args[1]['run_context'].Unpack(run_context)
sweeps1 = run_context.parameter_sweeps
job2 = program.run_sweep(
processor_id='processor0', repetitions=2, params=cirq.Points('a', [3, 4])
processor_id='processor0',
repetitions=2,
params=cirq.Points('a', [3, 4]),
run_name="run",
device_config_name="config_alias",
)
client().create_job_async.call_args[1]['run_context'].Unpack(run_context)
sweeps2 = run_context.parameter_sweeps
Expand Down Expand Up @@ -629,7 +638,9 @@ def test_bad_sweep_proto():
engine = cg.Engine(project_id='project-id', proto_version=cg.ProtoVersion.UNDEFINED)
program = cg.EngineProgram('proj', 'prog', engine.context)
with pytest.raises(ValueError, match='invalid run context proto version'):
program.run_sweep(processor_id='processor0')
program.run_sweep(
processor_id='processor0', run_name="run", device_config_name="config_alias"
)


@mock.patch('cirq_google.engine.engine_client.EngineClient', autospec=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def test_run():
proc = SimulatedLocalProcessor(processor_id='test_proc')
q = cirq.GridQubit(5, 4)
circuit = cirq.Circuit(cirq.X(q), cirq.measure(q, key='m'))
result = proc.run(circuit, repetitions=100)
result = proc.run(circuit, repetitions=100, run_name="run", device_config_name="config_alias")
assert np.all(result.measurements['m'] == 1)


Expand Down
14 changes: 8 additions & 6 deletions cirq-google/cirq_google/engine/virtual_engine_factory_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,22 @@ def _test_processor(processor: cg.engine.abstract_processor.AbstractProcessor):
Also tests the non-Sycamore qubits and gates fail."""
good_qubit = cirq.GridQubit(5, 4)
circuit = cirq.Circuit(cirq.X(good_qubit), cirq.measure(good_qubit))
results = processor.run(circuit, repetitions=100)
results = processor.run(circuit, repetitions=100, run_name="run", device_config_name="config")
assert np.all(results.measurements[str(good_qubit)] == 1)
with pytest.raises(RuntimeError, match='requested total repetitions'):
_ = processor.run(circuit, repetitions=100_000_000)
_ = processor.run(
circuit, repetitions=100_000_000, run_name="run", device_config_name="config"
)

bad_qubit = cirq.GridQubit(10, 10)
circuit = cirq.Circuit(cirq.X(bad_qubit), cirq.measure(bad_qubit))
with pytest.raises(ValueError, match='Qubit not on device'):
_ = processor.run(circuit, repetitions=100)
_ = processor.run(circuit, repetitions=100, run_name="run", device_config_name="config")
circuit = cirq.Circuit(
cirq.testing.DoesNotSupportSerializationGate()(good_qubit), cirq.measure(good_qubit)
)
with pytest.raises(ValueError, match='Cannot serialize op'):
_ = processor.run(circuit, repetitions=100)
_ = processor.run(circuit, repetitions=100, run_name="run", device_config_name="config")


def test_create_device_from_processor_id():
Expand Down Expand Up @@ -195,13 +197,13 @@ def test_create_default_noisy_quantum_virtual_machine():
bad_qubit = cirq.GridQubit(10, 10)
circuit = cirq.Circuit(cirq.X(bad_qubit), cirq.measure(bad_qubit))
with pytest.raises(ValueError, match='Qubit not on device'):
_ = processor.run(circuit, repetitions=100)
_ = processor.run(circuit, repetitions=100, run_name="run", device_config_name="config")
good_qubit = cirq.GridQubit(5, 4)
circuit = cirq.Circuit(
cirq.testing.DoesNotSupportSerializationGate()(good_qubit), cirq.measure(good_qubit)
)
with pytest.raises(ValueError, match='.* contains a gate which is not supported.'):
_ = processor.run(circuit, repetitions=100)
_ = processor.run(circuit, repetitions=100, run_name="run", device_config_name="config")
device_specification = processor.get_device_specification()
expected = factory.create_device_spec_from_processor_id(processor_id)
assert device_specification is not None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def test_engine_result():

circ = cirq.Circuit(cirq.measure(cirq.GridQubit(5, 4)))

res1 = proc.run(circ)
res1 = proc.run(circ, run_name="run", device_config_name="config_alias")
assert isinstance(res1, cg.EngineResult)
res2 = samp.run(circ)
assert isinstance(res2, cg.EngineResult)
Loading