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