1# Copyright 2018 The Cirq Developers 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Classes for running against Google's Quantum Cloud Service. 15 16As an example, to run a circuit against the xmon simulator on the cloud, 17 engine = cirq_google.Engine(project_id='my-project-id') 18 program = engine.create_program(circuit) 19 result0 = program.run(params=params0, repetitions=10) 20 result1 = program.run(params=params1, repetitions=10) 21 22In order to run on must have access to the Quantum Engine API. Access to this 23API is (as of June 22, 2018) restricted to invitation only. 24""" 25 26import datetime 27import enum 28import os 29import random 30import string 31from typing import Dict, Iterable, List, Optional, Sequence, Set, TypeVar, Union, TYPE_CHECKING 32 33from google.protobuf import any_pb2 34 35import cirq 36from cirq_google.api import v2 37from cirq_google.engine import engine_client 38from cirq_google.engine.client import quantum 39from cirq_google.engine.result_type import ResultType 40from cirq_google.serialization import SerializableGateSet, Serializer 41from cirq_google.serialization.arg_func_langs import arg_to_proto 42from cirq_google.engine import ( 43 engine_client, 44 engine_program, 45 engine_job, 46 engine_processor, 47 engine_sampler, 48) 49 50if TYPE_CHECKING: 51 import cirq_google 52 import google.protobuf 53 54TYPE_PREFIX = 'type.googleapis.com/' 55 56_R = TypeVar('_R') 57 58 59class ProtoVersion(enum.Enum): 60 """Protocol buffer version to use for requests to the quantum engine.""" 61 62 UNDEFINED = 0 63 V1 = 1 64 V2 = 2 65 66 67def _make_random_id(prefix: str, length: int = 16): 68 random_digits = [random.choice(string.ascii_uppercase + string.digits) for _ in range(length)] 69 suffix = ''.join(random_digits) 70 suffix += datetime.datetime.now().strftime('%y%m%d-%H%M%S') 71 return f'{prefix}{suffix}' 72 73 74@cirq.value_equality 75class EngineContext: 76 """Context for running against the Quantum Engine API. Most users should 77 simply create an Engine object instead of working with one of these 78 directly.""" 79 80 # TODO(#3388) Add documentation for Args. 81 # TODO(#3388) Add documentation for Raises. 82 # pylint: disable=missing-param-doc,missing-raises-doc 83 def __init__( 84 self, 85 proto_version: Optional[ProtoVersion] = None, 86 service_args: Optional[Dict] = None, 87 verbose: Optional[bool] = None, 88 client: 'Optional[engine_client.EngineClient]' = None, 89 timeout: Optional[int] = None, 90 ) -> None: 91 """Context and client for using Quantum Engine. 92 93 Args: 94 proto_version: The version of cirq protos to use. If None, then 95 ProtoVersion.V2 will be used. 96 service_args: A dictionary of arguments that can be used to 97 configure options on the underlying client. 98 verbose: Suppresses stderr messages when set to False. Default is 99 true. 100 timeout: Timeout for polling for results, in seconds. Default is 101 to never timeout. 102 """ 103 if (service_args or verbose) and client: 104 raise ValueError('either specify service_args and verbose or client') 105 106 self.proto_version = proto_version or ProtoVersion.V2 107 if self.proto_version == ProtoVersion.V1: 108 raise ValueError('ProtoVersion V1 no longer supported') 109 110 if not client: 111 client = engine_client.EngineClient(service_args=service_args, verbose=verbose) 112 self.client = client 113 self.timeout = timeout 114 115 # pylint: enable=missing-param-doc,missing-raises-doc 116 def copy(self) -> 'EngineContext': 117 return EngineContext(proto_version=self.proto_version, client=self.client) 118 119 def _value_equality_values_(self): 120 return self.proto_version, self.client 121 122 123class Engine: 124 """Runs programs via the Quantum Engine API. 125 126 This class has methods for creating programs and jobs that execute on 127 Quantum Engine: 128 create_program 129 run 130 run_sweep 131 run_batch 132 133 Another set of methods return information about programs and jobs that 134 have been previously created on the Quantum Engine, as well as metadata 135 about available processors: 136 get_program 137 list_processors 138 get_processor 139 """ 140 141 # TODO(#3388) Add documentation for Raises. 142 # pylint: disable=missing-raises-doc 143 def __init__( 144 self, 145 project_id: str, 146 proto_version: Optional[ProtoVersion] = None, 147 service_args: Optional[Dict] = None, 148 verbose: Optional[bool] = None, 149 timeout: Optional[int] = None, 150 context: Optional[EngineContext] = None, 151 ) -> None: 152 """Supports creating and running programs against the Quantum Engine. 153 154 Args: 155 project_id: A project_id string of the Google Cloud Project to use. 156 API interactions will be attributed to this project and any 157 resources created will be owned by the project. See 158 https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects 159 proto_version: The version of cirq protos to use. If None, then 160 ProtoVersion.V2 will be used. 161 service_args: A dictionary of arguments that can be used to 162 configure options on the underlying client. 163 verbose: Suppresses stderr messages when set to False. Default is 164 true. 165 timeout: Timeout for polling for results, in seconds. Default is 166 to never timeout. 167 context: Engine configuration and context to use. For most users 168 this should never be specified. 169 """ 170 if context and (proto_version or service_args or verbose): 171 raise ValueError('Either provide context or proto_version, service_args and verbose.') 172 173 self.project_id = project_id 174 if not context: 175 context = EngineContext( 176 proto_version=proto_version, 177 service_args=service_args, 178 verbose=verbose, 179 timeout=timeout, 180 ) 181 self.context = context 182 183 # pylint: enable=missing-raises-doc 184 def __str__(self) -> str: 185 return f'Engine(project_id={self.project_id!r})' 186 187 # TODO(#3388) Add documentation for Raises. 188 # pylint: disable=missing-raises-doc 189 def run( 190 self, 191 program: cirq.Circuit, 192 program_id: Optional[str] = None, 193 job_id: Optional[str] = None, 194 param_resolver: cirq.ParamResolver = cirq.ParamResolver({}), 195 repetitions: int = 1, 196 processor_ids: Sequence[str] = ('xmonsim',), 197 gate_set: Optional[Serializer] = None, 198 program_description: Optional[str] = None, 199 program_labels: Optional[Dict[str, str]] = None, 200 job_description: Optional[str] = None, 201 job_labels: Optional[Dict[str, str]] = None, 202 ) -> cirq.Result: 203 """Runs the supplied Circuit via Quantum Engine. 204 205 Args: 206 program: The Circuit to execute. If a circuit is 207 provided, a moment by moment schedule will be used. 208 program_id: A user-provided identifier for the program. This must 209 be unique within the Google Cloud project being used. If this 210 parameter is not provided, a random id of the format 211 'prog-################YYMMDD' will be generated, where # is 212 alphanumeric and YYMMDD is the current year, month, and day. 213 job_id: Job identifier to use. If this is not provided, a random id 214 of the format 'job-################YYMMDD' will be generated, 215 where # is alphanumeric and YYMMDD is the current year, month, 216 and day. 217 param_resolver: Parameters to run with the program. 218 repetitions: The number of repetitions to simulate. 219 processor_ids: The engine processors that should be candidates 220 to run the program. Only one of these will be scheduled for 221 execution. 222 gate_set: The gate set used to serialize the circuit. The gate set 223 must be supported by the selected processor. 224 program_description: An optional description to set on the program. 225 program_labels: Optional set of labels to set on the program. 226 job_description: An optional description to set on the job. 227 job_labels: Optional set of labels to set on the job. 228 229 Returns: 230 A single Result for this run. 231 """ 232 if not gate_set: 233 raise ValueError('No gate set provided') 234 return list( 235 self.run_sweep( 236 program=program, 237 program_id=program_id, 238 job_id=job_id, 239 params=[param_resolver], 240 repetitions=repetitions, 241 processor_ids=processor_ids, 242 gate_set=gate_set, 243 program_description=program_description, 244 program_labels=program_labels, 245 job_description=job_description, 246 job_labels=job_labels, 247 ) 248 )[0] 249 250 # TODO(#3388) Add documentation for Raises. 251 def run_sweep( 252 self, 253 program: cirq.Circuit, 254 program_id: Optional[str] = None, 255 job_id: Optional[str] = None, 256 params: cirq.Sweepable = None, 257 repetitions: int = 1, 258 processor_ids: Sequence[str] = ('xmonsim',), 259 gate_set: Optional[Serializer] = None, 260 program_description: Optional[str] = None, 261 program_labels: Optional[Dict[str, str]] = None, 262 job_description: Optional[str] = None, 263 job_labels: Optional[Dict[str, str]] = None, 264 ) -> engine_job.EngineJob: 265 """Runs the supplied Circuit via Quantum Engine.Creates 266 267 In contrast to run, this runs across multiple parameter sweeps, and 268 does not block until a result is returned. 269 270 Args: 271 program: The Circuit to execute. If a circuit is 272 provided, a moment by moment schedule will be used. 273 program_id: A user-provided identifier for the program. This must 274 be unique within the Google Cloud project being used. If this 275 parameter is not provided, a random id of the format 276 'prog-################YYMMDD' will be generated, where # is 277 alphanumeric and YYMMDD is the current year, month, and day. 278 job_id: Job identifier to use. If this is not provided, a random id 279 of the format 'job-################YYMMDD' will be generated, 280 where # is alphanumeric and YYMMDD is the current year, month, 281 and day. 282 params: Parameters to run with the program. 283 repetitions: The number of circuit repetitions to run. 284 processor_ids: The engine processors that should be candidates 285 to run the program. Only one of these will be scheduled for 286 execution. 287 gate_set: The gate set used to serialize the circuit. The gate set 288 must be supported by the selected processor. 289 program_description: An optional description to set on the program. 290 program_labels: Optional set of labels to set on the program. 291 job_description: An optional description to set on the job. 292 job_labels: Optional set of labels to set on the job. 293 294 Returns: 295 An EngineJob. If this is iterated over it returns a list of 296 TrialResults, one for each parameter sweep. 297 """ 298 if not gate_set: 299 raise ValueError('No gate set provided') 300 engine_program = self.create_program( 301 program, program_id, gate_set, program_description, program_labels 302 ) 303 return engine_program.run_sweep( 304 job_id=job_id, 305 params=params, 306 repetitions=repetitions, 307 processor_ids=processor_ids, 308 description=job_description, 309 labels=job_labels, 310 ) 311 312 # TODO(#3388) Add documentation for Raises. 313 def run_batch( 314 self, 315 programs: Sequence[cirq.AbstractCircuit], 316 program_id: Optional[str] = None, 317 job_id: Optional[str] = None, 318 params_list: List[cirq.Sweepable] = None, 319 repetitions: int = 1, 320 processor_ids: Sequence[str] = (), 321 gate_set: Optional[Serializer] = None, 322 program_description: Optional[str] = None, 323 program_labels: Optional[Dict[str, str]] = None, 324 job_description: Optional[str] = None, 325 job_labels: Optional[Dict[str, str]] = None, 326 ) -> engine_job.EngineJob: 327 """Runs the supplied Circuits via Quantum Engine.Creates 328 329 This will combine each Circuit provided in `programs` into 330 a BatchProgram. Each circuit will pair with the associated 331 parameter sweep provided in the `params_list`. The number of 332 programs is required to match the number of sweeps. 333 334 This method does not block until a result is returned. However, 335 no results will be available until the entire batch is complete. 336 337 Args: 338 programs: The Circuits to execute as a batch. 339 program_id: A user-provided identifier for the program. This must 340 be unique within the Google Cloud project being used. If this 341 parameter is not provided, a random id of the format 342 'prog-################YYMMDD' will be generated, where # is 343 alphanumeric and YYMMDD is the current year, month, and day. 344 job_id: Job identifier to use. If this is not provided, a random id 345 of the format 'job-################YYMMDD' will be generated, 346 where # is alphanumeric and YYMMDD is the current year, month, 347 and day. 348 params_list: Parameter sweeps to use with the circuits. The number 349 of sweeps should match the number of circuits and will be 350 paired in order with the circuits. If this is None, it is 351 assumed that the circuits are not parameterized and do not 352 require sweeps. 353 repetitions: Number of circuit repetitions to run. Each sweep value 354 of each circuit in the batch will run with the same repetitions. 355 processor_ids: The engine processors that should be candidates 356 to run the program. Only one of these will be scheduled for 357 execution. 358 gate_set: The gate set used to serialize the circuit. The gate set 359 must be supported by the selected processor. 360 program_description: An optional description to set on the program. 361 program_labels: Optional set of labels to set on the program. 362 job_description: An optional description to set on the job. 363 job_labels: Optional set of labels to set on the job. 364 365 Returns: 366 An EngineJob. If this is iterated over it returns a list of 367 TrialResults. All TrialResults for the first circuit are listed 368 first, then the TrialResults for the second, etc. The TrialResults 369 for a circuit are listed in the order imposed by the associated 370 parameter sweep. 371 """ 372 if params_list is None: 373 params_list = [None] * len(programs) 374 elif len(programs) != len(params_list): 375 raise ValueError('Number of circuits and sweeps must match') 376 if not processor_ids: 377 raise ValueError('Processor id must be specified.') 378 engine_program = self.create_batch_program( 379 programs, program_id, gate_set, program_description, program_labels 380 ) 381 return engine_program.run_batch( 382 job_id=job_id, 383 params_list=params_list, 384 repetitions=repetitions, 385 processor_ids=processor_ids, 386 description=job_description, 387 labels=job_labels, 388 ) 389 390 # TODO(#3388) Add documentation for Raises. 391 def run_calibration( 392 self, 393 layers: List['cirq_google.CalibrationLayer'], 394 program_id: Optional[str] = None, 395 job_id: Optional[str] = None, 396 processor_id: str = None, 397 processor_ids: Sequence[str] = (), 398 gate_set: Optional[Serializer] = None, 399 program_description: Optional[str] = None, 400 program_labels: Optional[Dict[str, str]] = None, 401 job_description: Optional[str] = None, 402 job_labels: Optional[Dict[str, str]] = None, 403 ) -> engine_job.EngineJob: 404 """Runs the specified calibrations via the Calibration API. 405 406 Each calibration will be specified by a `CalibrationLayer` 407 that contains the type of the calibrations to run, a `Circuit` 408 to optimize, and any arguments needed by the calibration routine. 409 410 Arguments and circuits needed for each layer will vary based on the 411 calibration type. However, the typical calibration routine may 412 require a single moment defining the gates to optimize, for example. 413 414 Note: this is an experimental API and is not yet fully supported 415 for all users. 416 417 Args: 418 layers: The layers of calibration to execute as a batch. 419 program_id: A user-provided identifier for the program. This must 420 be unique within the Google Cloud project being used. If this 421 parameter is not provided, a random id of the format 422 'calibration-################YYMMDD' will be generated, 423 where # is alphanumeric and YYMMDD is the current year, month, 424 and day. 425 job_id: Job identifier to use. If this is not provided, a random id 426 of the format 'calibration-################YYMMDD' will be 427 generated, where # is alphanumeric and YYMMDD is the current 428 year, month, and day. 429 processor_id: The engine processor that should run the calibration. 430 If this is specified, processor_ids should not be specified. 431 processor_ids: The engine processors that should be candidates 432 to run the program. Only one of these will be scheduled for 433 execution. 434 gate_set: The gate set used to serialize the circuit. The gate set 435 must be supported by the selected processor. 436 program_description: An optional description to set on the program. 437 program_labels: Optional set of labels to set on the program. 438 job_description: An optional description to set on the job. 439 job_labels: Optional set of labels to set on the job. By defauly, 440 this will add a 'calibration' label to the job. 441 442 Returns: 443 An EngineJob whose results can be retrieved by calling 444 calibration_results(). 445 """ 446 if processor_id and processor_ids: 447 raise ValueError('Only one of processor_id and processor_ids can be specified.') 448 if not processor_ids and not processor_id: 449 raise ValueError('Processor id must be specified.') 450 if processor_id: 451 processor_ids = [processor_id] 452 if job_labels is None: 453 job_labels = {'calibration': ''} 454 engine_program = self.create_calibration_program( 455 layers, program_id, gate_set, program_description, program_labels 456 ) 457 return engine_program.run_calibration( 458 job_id=job_id, 459 processor_ids=processor_ids, 460 description=job_description, 461 labels=job_labels, 462 ) 463 464 # TODO(#3388) Add documentation for Raises. 465 def create_program( 466 self, 467 program: cirq.Circuit, 468 program_id: Optional[str] = None, 469 gate_set: Optional[Serializer] = None, 470 description: Optional[str] = None, 471 labels: Optional[Dict[str, str]] = None, 472 ) -> engine_program.EngineProgram: 473 """Wraps a Circuit for use with the Quantum Engine. 474 475 Args: 476 program: The Circuit to execute. 477 program_id: A user-provided identifier for the program. This must be 478 unique within the Google Cloud project being used. If this 479 parameter is not provided, a random id of the format 480 'prog-################YYMMDD' will be generated, where # is 481 alphanumeric and YYMMDD is the current year, month, and day. 482 gate_set: The gate set used to serialize the circuit. The gate set 483 must be supported by the selected processor 484 description: An optional description to set on the program. 485 labels: Optional set of labels to set on the program. 486 487 Returns: 488 A EngineProgram for the newly created program. 489 """ 490 if not gate_set: 491 raise ValueError('No gate set provided') 492 493 if not program_id: 494 program_id = _make_random_id('prog-') 495 496 new_program_id, new_program = self.context.client.create_program( 497 self.project_id, 498 program_id, 499 code=self._serialize_program(program, gate_set), 500 description=description, 501 labels=labels, 502 ) 503 504 return engine_program.EngineProgram( 505 self.project_id, new_program_id, self.context, new_program 506 ) 507 508 # TODO(#3388) Add documentation for Raises. 509 def create_batch_program( 510 self, 511 programs: Sequence[cirq.AbstractCircuit], 512 program_id: Optional[str] = None, 513 gate_set: Optional[Serializer] = None, 514 description: Optional[str] = None, 515 labels: Optional[Dict[str, str]] = None, 516 ) -> engine_program.EngineProgram: 517 """Wraps a list of Circuits into a BatchProgram for the Quantum Engine. 518 519 Args: 520 programs: The Circuits to execute within a batch. 521 program_id: A user-provided identifier for the program. This must be 522 unique within the Google Cloud project being used. If this 523 parameter is not provided, a random id of the format 524 'prog-################YYMMDD' will be generated, where # is 525 alphanumeric and YYMMDD is the current year, month, and day. 526 gate_set: The gate set used to serialize the circuit. The gate set 527 must be supported by the selected processor 528 description: An optional description to set on the program. 529 labels: Optional set of labels to set on the program. 530 531 Returns: 532 A EngineProgram for the newly created program. 533 """ 534 if not gate_set: 535 raise ValueError('Gate set must be specified.') 536 if not program_id: 537 program_id = _make_random_id('prog-') 538 539 batch = v2.batch_pb2.BatchProgram() 540 for program in programs: 541 gate_set.serialize(program, msg=batch.programs.add()) 542 543 new_program_id, new_program = self.context.client.create_program( 544 self.project_id, 545 program_id, 546 code=self._pack_any(batch), 547 description=description, 548 labels=labels, 549 ) 550 551 return engine_program.EngineProgram( 552 self.project_id, new_program_id, self.context, new_program, result_type=ResultType.Batch 553 ) 554 555 # TODO(#3388) Add documentation for Raises. 556 def create_calibration_program( 557 self, 558 layers: List['cirq_google.CalibrationLayer'], 559 program_id: Optional[str] = None, 560 gate_set: Optional[Serializer] = None, 561 description: Optional[str] = None, 562 labels: Optional[Dict[str, str]] = None, 563 ) -> engine_program.EngineProgram: 564 """Wraps a list of calibration layers into an Any for Quantum Engine. 565 566 Args: 567 layers: The calibration routines to execute. All layers will be 568 executed within the same API call in the order specified, 569 though some layers may be interleaved together using 570 hardware-specific batching. 571 program_id: A user-provided identifier for the program. This must be 572 unique within the Google Cloud project being used. If this 573 parameter is not provided, a random id of the format 574 'calibration-################YYMMDD' will be generated, 575 where # is alphanumeric and YYMMDD is the current year, month, 576 and day. 577 gate_set: The gate set used to serialize the circuits in each 578 layer. The gate set must be supported by the processor. 579 description: An optional description to set on the program. 580 labels: Optional set of labels to set on the program. 581 582 Returns: 583 A EngineProgram for the newly created program. 584 """ 585 if not gate_set: 586 raise ValueError('Gate set must be specified.') 587 if not program_id: 588 program_id = _make_random_id('calibration-') 589 590 calibration = v2.calibration_pb2.FocusedCalibration() 591 for layer in layers: 592 new_layer = calibration.layers.add() 593 new_layer.calibration_type = layer.calibration_type 594 for arg in layer.args: 595 arg_to_proto(layer.args[arg], out=new_layer.args[arg]) 596 gate_set.serialize(layer.program, msg=new_layer.layer) 597 598 new_program_id, new_program = self.context.client.create_program( 599 self.project_id, 600 program_id, 601 code=self._pack_any(calibration), 602 description=description, 603 labels=labels, 604 ) 605 606 return engine_program.EngineProgram( 607 self.project_id, 608 new_program_id, 609 self.context, 610 new_program, 611 result_type=ResultType.Calibration, 612 ) 613 614 # pylint: enable=missing-raises-doc 615 def _serialize_program(self, program: cirq.Circuit, gate_set: Serializer) -> any_pb2.Any: 616 if not isinstance(program, cirq.Circuit): 617 raise TypeError(f'Unrecognized program type: {type(program)}') 618 program.device.validate_circuit(program) 619 620 if self.context.proto_version == ProtoVersion.V2: 621 program = gate_set.serialize(program) 622 return self._pack_any(program) 623 else: 624 raise ValueError(f'invalid program proto version: {self.context.proto_version}') 625 626 def _pack_any(self, message: 'google.protobuf.Message') -> any_pb2.Any: 627 """Packs a message into an Any proto. 628 629 Returns the packed Any proto. 630 """ 631 packed = any_pb2.Any() 632 packed.Pack(message) 633 return packed 634 635 def get_program(self, program_id: str) -> engine_program.EngineProgram: 636 """Returns an EngineProgram for an existing Quantum Engine program. 637 638 Args: 639 program_id: Unique ID of the program within the parent project. 640 641 Returns: 642 A EngineProgram for the program. 643 """ 644 return engine_program.EngineProgram(self.project_id, program_id, self.context) 645 646 def list_programs( 647 self, 648 created_before: Optional[Union[datetime.datetime, datetime.date]] = None, 649 created_after: Optional[Union[datetime.datetime, datetime.date]] = None, 650 has_labels: Optional[Dict[str, str]] = None, 651 ) -> List[engine_program.EngineProgram]: 652 """Returns a list of previously executed quantum programs. 653 654 Args: 655 created_after: retrieve programs that were created after this date 656 or time. 657 created_before: retrieve programs that were created after this date 658 or time. 659 has_labels: retrieve programs that have labels on them specified by 660 this dict. If the value is set to `*`, filters having the label 661 regardless of the label value will be filtered. For example, to 662 query programs that have the shape label and have the color 663 label with value red can be queried using 664 `{'color: red', 'shape:*'}` 665 """ 666 667 client = self.context.client 668 response = client.list_programs( 669 self.project_id, 670 created_before=created_before, 671 created_after=created_after, 672 has_labels=has_labels, 673 ) 674 return [ 675 engine_program.EngineProgram( 676 project_id=engine_client._ids_from_program_name(p.name)[0], 677 program_id=engine_client._ids_from_program_name(p.name)[1], 678 _program=p, 679 context=self.context, 680 ) 681 for p in response 682 ] 683 684 def list_jobs( 685 self, 686 created_before: Optional[Union[datetime.datetime, datetime.date]] = None, 687 created_after: Optional[Union[datetime.datetime, datetime.date]] = None, 688 has_labels: Optional[Dict[str, str]] = None, 689 execution_states: Optional[Set[quantum.enums.ExecutionStatus.State]] = None, 690 ): 691 """Returns the list of jobs in the project. 692 693 All historical jobs can be retrieved using this method and filtering 694 options are available too, to narrow down the search baesd on: 695 * creation time 696 * job labels 697 * execution states 698 699 Args: 700 created_after: retrieve jobs that were created after this date 701 or time. 702 created_before: retrieve jobs that were created after this date 703 or time. 704 has_labels: retrieve jobs that have labels on them specified by 705 this dict. If the value is set to `*`, filters having the label 706 regardless of the label value will be filtered. For example, to 707 query programs that have the shape label and have the color 708 label with value red can be queried using 709 710 {'color': 'red', 'shape':'*'} 711 712 execution_states: retrieve jobs that have an execution state that 713 is contained in `execution_states`. See 714 `quantum.enums.ExecutionStatus.State` enum for accepted values. 715 """ 716 client = self.context.client 717 response = client.list_jobs( 718 self.project_id, 719 None, 720 created_before=created_before, 721 created_after=created_after, 722 has_labels=has_labels, 723 execution_states=execution_states, 724 ) 725 return [ 726 engine_job.EngineJob( 727 project_id=engine_client._ids_from_job_name(j.name)[0], 728 program_id=engine_client._ids_from_job_name(j.name)[1], 729 job_id=engine_client._ids_from_job_name(j.name)[2], 730 context=self.context, 731 _job=j, 732 ) 733 for j in response 734 ] 735 736 def list_processors(self) -> List[engine_processor.EngineProcessor]: 737 """Returns a list of Processors that the user has visibility to in the 738 current Engine project. The names of these processors are used to 739 identify devices when scheduling jobs and gathering calibration metrics. 740 741 Returns: 742 A list of EngineProcessors to access status, device and calibration 743 information. 744 """ 745 response = self.context.client.list_processors(self.project_id) 746 return [ 747 engine_processor.EngineProcessor( 748 self.project_id, 749 engine_client._ids_from_processor_name(p.name)[1], 750 self.context, 751 p, 752 ) 753 for p in response 754 ] 755 756 def get_processor(self, processor_id: str) -> engine_processor.EngineProcessor: 757 """Returns an EngineProcessor for a Quantum Engine processor. 758 759 Args: 760 processor_id: The processor unique identifier. 761 762 Returns: 763 A EngineProcessor for the processor. 764 """ 765 return engine_processor.EngineProcessor(self.project_id, processor_id, self.context) 766 767 def sampler( 768 self, processor_id: Union[str, List[str]], gate_set: Serializer 769 ) -> engine_sampler.QuantumEngineSampler: 770 """Returns a sampler backed by the engine. 771 772 Args: 773 processor_id: String identifier, or list of string identifiers, 774 determining which processors may be used when sampling. 775 gate_set: Determines how to serialize circuits when requesting 776 samples. 777 """ 778 return engine_sampler.QuantumEngineSampler( 779 engine=self, processor_id=processor_id, gate_set=gate_set 780 ) 781 782 783# TODO(#3388) Add documentation for Raises. 784# pylint: disable=missing-raises-doc 785def get_engine(project_id: Optional[str] = None) -> Engine: 786 """Get an Engine instance assuming some sensible defaults. 787 788 This uses the environment variable GOOGLE_CLOUD_PROJECT for the Engine 789 project_id, unless set explicitly. By using an environment variable, 790 you can avoid hard-coding the project_id in shared code. 791 792 If the environment variables are set, but incorrect, an authentication 793 failure will occur when attempting to run jobs on the engine. 794 795 Args: 796 project_id: If set overrides the project id obtained from the 797 environment variable `GOOGLE_CLOUD_PROJECT`. 798 799 Returns: 800 The Engine instance. 801 802 Raises: 803 EnvironmentError: If the environment variable GOOGLE_CLOUD_PROJECT is 804 not set. 805 """ 806 env_project_id = 'GOOGLE_CLOUD_PROJECT' 807 if not project_id: 808 project_id = os.environ.get(env_project_id) 809 if not project_id: 810 raise EnvironmentError(f'Environment variable {env_project_id} is not set.') 811 812 return Engine(project_id=project_id) 813 814 815# pylint: enable=missing-raises-doc 816def get_engine_device( 817 processor_id: str, 818 project_id: Optional[str] = None, 819 gatesets: Iterable[SerializableGateSet] = (), 820) -> cirq.Device: 821 """Returns a `Device` object for a given processor. 822 823 This is a short-cut for creating an engine object, getting the 824 processor object, and retrieving the device. Note that the 825 gateset is required in order to match the serialized specification 826 back into cirq objects. 827 """ 828 return get_engine(project_id).get_processor(processor_id).get_device(gatesets) 829 830 831def get_engine_calibration( 832 processor_id: str, 833 project_id: Optional[str] = None, 834) -> Optional['cirq_google.Calibration']: 835 """Returns calibration metrics for a given processor. 836 837 This is a short-cut for creating an engine object, getting the 838 processor object, and retrieving the current calibration. 839 May return None if no calibration metrics exist for the device. 840 """ 841 return get_engine(project_id).get_processor(processor_id).get_current_calibration() 842