def test_run_program__returns_results( client_configuration: QCSClientConfiguration): qvm_client = QVMClient(client_configuration=client_configuration) respx.post( url=client_configuration.profile.applications.pyquil.qvm_url, json={ "type": "multishot", "compiled-quil": "some-program", "addresses": { "ro": True }, "trials": 1, "measurement-noise": (3.14, 1.61, 6.28), "gate-noise": (1.0, 2.0, 3.0), "rng-seed": 314, }, ).respond(status_code=200, json={"ro": [[1, 0, 1]]}) request = RunProgramRequest( program="some-program", addresses={"ro": True}, trials=1, measurement_noise=(3.14, 1.61, 6.28), gate_noise=(1.0, 2.0, 3.0), seed=314, ) assert qvm_client.run_program(request) == RunProgramResponse( results={"ro": [[1, 0, 1]]})
class QVM(QAM[QVMExecuteResponse]): def __init__( self, noise_model: Optional[NoiseModel] = None, gate_noise: Optional[Tuple[float, float, float]] = None, measurement_noise: Optional[Tuple[float, float, float]] = None, random_seed: Optional[int] = None, timeout: float = 10.0, client_configuration: Optional[QCSClientConfiguration] = None, ) -> None: """ A virtual machine that classically emulates the execution of Quil programs. :param noise_model: A noise model that describes noise to apply when emulating a program's execution. :param gate_noise: A tuple of three numbers [Px, Py, Pz] indicating the probability of an X, Y, or Z gate getting applied to each qubit after a gate application or reset. The default value of None indicates no noise. :param measurement_noise: A tuple of three numbers [Px, Py, Pz] indicating the probability of an X, Y, or Z gate getting applied before a measurement. The default value of None indicates no noise. :param random_seed: A seed for the QVM's random number generators. Either None (for an automatically generated seed) or a non-negative integer. :param timeout: Time limit for requests, in seconds. :param client_configuration: Optional client configuration. If none is provided, a default one will be loaded. """ super().__init__() if (noise_model is not None) and (gate_noise is not None or measurement_noise is not None): raise ValueError(""" You have attempted to supply the QVM with both a Kraus noise model (by supplying a `noise_model` argument), as well as either `gate_noise` or `measurement_noise`. At this time, only one may be supplied. To read more about supplying noise to the QVM, see http://pyquil.readthedocs.io/en/latest/noise_models.html#support-for-noisy-gates-on-the-rigetti-qvm. """) self.noise_model = noise_model validate_noise_probabilities(gate_noise) validate_noise_probabilities(measurement_noise) self.gate_noise = gate_noise self.measurement_noise = measurement_noise if random_seed is None: self.random_seed = None elif isinstance(random_seed, int) and random_seed >= 0: self.random_seed = random_seed else: raise TypeError("random_seed should be None or a non-negative int") client_configuration = client_configuration or QCSClientConfiguration.load( ) self._qvm_client = QVMClient(client_configuration=client_configuration, request_timeout=timeout) self.connect() def connect(self) -> None: try: version = self.get_version_info() check_qvm_version(version) except ConnectionError: raise QVMNotRunning( f"No QVM server running at {self._qvm_client.base_url}") def execute(self, executable: QuantumExecutable) -> QVMExecuteResponse: """ Synchronously execute the input program to completion. """ executable = executable.copy() if not isinstance(executable, Program): raise TypeError( f"`QVM#executable` argument must be a `Program`; got {type(executable)}" ) result_memory = {} for region in executable.declarations.keys(): result_memory[region] = np.ndarray((executable.num_shots, 0), dtype=np.int64) trials = executable.num_shots classical_addresses = get_classical_addresses_from_program(executable) if self.noise_model is not None: executable = apply_noise_model(executable, self.noise_model) executable._set_parameter_values_at_runtime() request = qvm_run_request( executable, classical_addresses, trials, self.measurement_noise, self.gate_noise, self.random_seed, ) response = self._qvm_client.run_program(request) ram = {key: np.array(val) for key, val in response.results.items()} result_memory.update(ram) return QVMExecuteResponse(executable=executable, memory=result_memory) def get_result(self, execute_response: QVMExecuteResponse) -> QAMExecutionResult: """ Return the results of execution on the QVM. Because QVM execution is synchronous, this is a no-op which returns its input. """ return QAMExecutionResult(executable=execute_response.executable, readout_data=execute_response.memory) def get_version_info(self) -> str: """ Return version information for the QVM. :return: String with version information """ return self._qvm_client.get_version()