def _get_qvm_qc(name: str, qvm_type: str, device: AbstractDevice, noise_model: NoiseModel = None, requires_executable: bool = False, connection: ForestConnection = None) -> QuantumComputer: """Construct a QuantumComputer backed by a QVM. This is a minimal wrapper over the QuantumComputer, QVM, and QVMCompiler constructors. :param name: A string identifying this particular quantum computer. :param qvm_type: The type of QVM. Either qvm or pyqvm. :param device: A device following the AbstractDevice interface. :param noise_model: An optional noise model :param requires_executable: Whether this QVM will refuse to run a :py:class:`Program` and only accept the result of :py:func:`compiler.native_quil_to_executable`. Setting this to True better emulates the behavior of a QPU. :param connection: An optional :py:class:`ForestConnection` object. If not specified, the default values for URL endpoints will be used. :return: A QuantumComputer backed by a QVM with the above options. """ if connection is None: connection = ForestConnection() return QuantumComputer( name=name, qam=_get_qvm_or_pyqvm(qvm_type=qvm_type, connection=connection, noise_model=noise_model, device=device, requires_executable=requires_executable), device=device, compiler=QVMCompiler(device=device, endpoint=connection.compiler_endpoint))
def _get_qvm_compiler_based_on_endpoint(endpoint=None, device=None): if endpoint.startswith("http"): return LocalQVMCompiler(endpoint=endpoint, device=device) elif endpoint.startswith("tcp"): return QVMCompiler(endpoint=endpoint, device=device) else: raise ValueError( "Protocol for QVM compiler endpoints must be HTTP or TCP.")
def _get_qvm_qc( *, client_configuration: QCSClientConfiguration, name: str, qvm_type: str, quantum_processor: AbstractQuantumProcessor, compiler_timeout: float, execution_timeout: float, noise_model: Optional[NoiseModel], ) -> QuantumComputer: """Construct a QuantumComputer backed by a QVM. This is a minimal wrapper over the QuantumComputer, QVM, and QVMCompiler constructors. :param client_configuration: Client configuration. :param name: A string identifying this particular quantum computer. :param qvm_type: The type of QVM. Either qvm or pyqvm. :param quantum_processor: A quantum_processor following the AbstractQuantumProcessor interface. :param noise_model: An optional noise model :param compiler_timeout: Time limit for compilation requests, in seconds. :param execution_timeout: Time limit for execution requests, in seconds. :return: A QuantumComputer backed by a QVM with the above options. """ return QuantumComputer( name=name, qam=_get_qvm_or_pyqvm( client_configuration=client_configuration, qvm_type=qvm_type, noise_model=noise_model, quantum_processor=quantum_processor, execution_timeout=execution_timeout, ), compiler=QVMCompiler( quantum_processor=quantum_processor, timeout=compiler_timeout, client_configuration=client_configuration, ), )
def __init__(self, clauses, m=None, steps=1, grid_size=None, tol=1e-5, gate_noise=None, verbose=False, visualize=False): self.clauses = clauses self.m = m self.verbose = verbose self.visualize = visualize self.step_by_step_results = None self.optimization_history = None self.gate_noise = gate_noise if grid_size is None: self.grid_size = len(clauses) + len(qubits) else: self.grid_size = grid_size cost_operators, mapping = self.create_operators_from_clauses() self.mapping = mapping driver_operators = self.create_driver_operators() # minimizer_kwargs = {'method': 'BFGS', # 'options': {'gtol': tol, 'disp': False}} # bounds = [(0, np.pi)]*steps + [(0, 2*np.pi)]*steps # minimizer_kwargs = {'method': 'L-BFGS-B', # 'options': {'gtol': tol, 'disp': False}, # 'bounds': bounds} minimizer_kwargs = { 'method': 'Nelder-Mead', 'options': { 'ftol': tol, 'tol': tol, 'disp': False } } if self.verbose: print_fun = print else: print_fun = pass_fun qubits = list(range(len(mapping))) if gate_noise: self.samples = int(1e3) pauli_channel = [gate_noise] * 3 else: self.samples = None pauli_channel = None connection = ForestConnection() qvm = QVM(connection=connection, gate_noise=pauli_channel) topology = nx.complete_graph(len(qubits)) device = NxDevice(topology=topology) qc = QuantumComputer(name="my_qvm", qam=qvm, device=device, compiler=QVMCompiler( device=device, endpoint=connection.compiler_endpoint)) vqe_option = { 'disp': print_fun, 'return_all': True, 'samples': self.samples } self.qaoa_inst = QAOA(qc, qubits, steps=steps, init_betas=None, init_gammas=None, cost_ham=cost_operators, ref_ham=driver_operators, minimizer=scipy.optimize.minimize, minimizer_kwargs=minimizer_kwargs, rand_seed=None, vqe_options=vqe_option, store_basis=True) self.ax = None
def __init__(self, device=None, endpoint=None, gate_noise=None, measurement_noise=None, random_seed=None, compiler_endpoint=None): """ Constructor for QVMConnection. Sets up any necessary security, and establishes the noise model to use. :param Device device: The optional device, from which noise will be added by default to all programs run on this instance. :param endpoint: The endpoint of the server for running small jobs :param gate_noise: A list 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. (default None) :param measurement_noise: A list of three numbers [Px, Py, Pz] indicating the probability of an X, Y, or Z gate getting applied before a a measurement. (default None) :param random_seed: A seed for the QVM's random number generators. Either None (for an automatically generated seed) or a non-negative integer. """ if endpoint is None: pyquil_config = PyquilConfig() endpoint = pyquil_config.qvm_url if compiler_endpoint is None: pyquil_config = PyquilConfig() compiler_endpoint = pyquil_config.quilc_url if (device is not None and device.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 device noise model (by having supplied a device 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. """) if device is not None and device.noise_model is None: warnings.warn(""" You have supplied the QVM with a device that does not have a noise model. No noise will be added to programs run on this QVM. """) self.noise_model = device.noise_model if device else None self.compiler = QVMCompiler(endpoint=compiler_endpoint, device=device) if device \ else None self.sync_endpoint = endpoint 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") self._connection = ForestConnection(sync_endpoint=endpoint) self.session = self._connection.session # backwards compatibility self.connect()
class QVMConnection(object): """ Represents a connection to the QVM. """ @_record_call def __init__(self, device=None, endpoint=None, gate_noise=None, measurement_noise=None, random_seed=None, compiler_endpoint=None): """ Constructor for QVMConnection. Sets up any necessary security, and establishes the noise model to use. :param Device device: The optional device, from which noise will be added by default to all programs run on this instance. :param endpoint: The endpoint of the server for running small jobs :param gate_noise: A list 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. (default None) :param measurement_noise: A list of three numbers [Px, Py, Pz] indicating the probability of an X, Y, or Z gate getting applied before a a measurement. (default None) :param random_seed: A seed for the QVM's random number generators. Either None (for an automatically generated seed) or a non-negative integer. """ if endpoint is None: pyquil_config = PyquilConfig() endpoint = pyquil_config.qvm_url if compiler_endpoint is None: pyquil_config = PyquilConfig() compiler_endpoint = pyquil_config.quilc_url if (device is not None and device.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 device noise model (by having supplied a device 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. """) if device is not None and device.noise_model is None: warnings.warn(""" You have supplied the QVM with a device that does not have a noise model. No noise will be added to programs run on this QVM. """) self.noise_model = device.noise_model if device else None self.compiler = QVMCompiler(endpoint=compiler_endpoint, device=device) if device \ else None self.sync_endpoint = endpoint 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") self._connection = ForestConnection(sync_endpoint=endpoint) self.session = self._connection.session # backwards compatibility self.connect() def connect(self): try: version_dict = self.get_version_info() check_qvm_version(version_dict) except ConnectionError: raise QVMNotRunning( f'No QVM server running at {self._connection.sync_endpoint}') @_record_call def get_version_info(self): """ Return version information for the QVM. :return: Dictionary with version information """ return self._connection._qvm_get_version_info() @_record_call def run(self, quil_program, classical_addresses: List[int] = None, trials=1): """ Run a Quil program multiple times, accumulating the values deposited in a list of classical addresses. :param Program quil_program: A Quil program. :param classical_addresses: The classical memory to retrieve. Specified as a list of integers that index into a readout register named ``ro``. This function--and particularly this argument--are included for backwards compatibility and will be removed in the future. :param int trials: Number of shots to collect. :return: A list of dictionaries of bits. Each dictionary corresponds to the values in `classical_addresses`. :rtype: list """ if classical_addresses is None: caddresses = get_classical_addresses_from_program(quil_program) else: caddresses = {'ro': classical_addresses} buffers = self._connection._qvm_run(quil_program, caddresses, trials, self.measurement_noise, self.gate_noise, self.random_seed) if len(buffers) == 0: return [] if 'ro' in buffers: return buffers['ro'].tolist() raise ValueError( "You are using QVMConnection.run with multiple readout registers not " "named `ro`. Please use the new `QuantumComputer` abstraction.") @_record_call def run_and_measure(self, quil_program, qubits, trials=1): """ Run a Quil program once to determine the final wavefunction, and measure multiple times. :note: If the execution of ``quil_program`` is **non-deterministic**, i.e., if it includes measurements and/or noisy quantum gates, then the final wavefunction from which the returned bitstrings are sampled itself only represents a stochastically generated sample and the outcomes sampled from *different* ``run_and_measure`` calls *generally sample different bitstring distributions*. :param Program quil_program: A Quil program. :param list|range qubits: A list of qubits. :param int trials: Number of shots to collect. :return: A list of a list of bits. :rtype: list """ # Developer note: This code is for backwards compatibility. It can't be replaced with # ForestConnection._run_and_measure because we've turned off the ability to set # `needs_compilation` (that usually indicates the user is doing something iffy like # using a noise model with this function) payload = self._run_and_measure_payload(quil_program, qubits, trials) response = post_json(self.session, self.sync_endpoint + "/qvm", payload) return response.json() @_record_call def _run_and_measure_payload(self, quil_program, qubits, trials): if not quil_program: raise ValueError( "You have attempted to run an empty program." " Please provide gates or measure instructions to your program." ) if not isinstance(quil_program, Program): raise TypeError("quil_program must be a Quil program object") qubits = validate_qubit_list(qubits) if not isinstance(trials, int): raise TypeError("trials must be an integer") if self.noise_model is not None: compiled_program = self.compiler.quil_to_native_quil(quil_program) quil_program = apply_noise_model(compiled_program, self.noise_model) payload = { "type": TYPE_MULTISHOT_MEASURE, "qubits": list(qubits), "trials": trials, "compiled-quil": quil_program.out() } self._maybe_add_noise_to_payload(payload) self._add_rng_seed_to_payload(payload) return payload @_record_call def wavefunction(self, quil_program): """ Simulate a Quil program and get the wavefunction back. :note: If the execution of ``quil_program`` is **non-deterministic**, i.e., if it includes measurements and/or noisy quantum gates, then the final wavefunction from which the returned bitstrings are sampled itself only represents a stochastically generated sample and the wavefunctions returned by *different* ``wavefunction`` calls *will generally be different*. :param Program quil_program: A Quil program. :return: A Wavefunction object representing the state of the QVM. :rtype: Wavefunction """ # Developer note: This code is for backwards compatibility. It can't be replaced with # ForestConnection._wavefunction because we've turned off the ability to set # `needs_compilation` (that usually indicates the user is doing something iffy like # using a noise model with this function) payload = self._wavefunction_payload(quil_program) response = post_json(self.session, self.sync_endpoint + "/qvm", payload) return Wavefunction.from_bit_packed_string(response.content) @_record_call def _wavefunction_payload(self, quil_program): # Developer note: This code is for backwards compatibility. It can't be replaced with # _base_connection._wavefunction_payload because we've turned off the ability to set # `needs_compilation` (that usually indicates the user is doing something iffy like # using a noise model with this function) if not isinstance(quil_program, Program): raise TypeError("quil_program must be a Quil program object") payload = { 'type': TYPE_WAVEFUNCTION, 'compiled-quil': quil_program.out() } self._maybe_add_noise_to_payload(payload) self._add_rng_seed_to_payload(payload) return payload @_record_call def expectation(self, prep_prog, operator_programs=None): """ Calculate the expectation value of operators given a state prepared by prep_program. :note: If the execution of ``quil_program`` is **non-deterministic**, i.e., if it includes measurements and/or noisy quantum gates, then the final wavefunction from which the expectation values are computed itself only represents a stochastically generated sample. The expectations returned from *different* ``expectation`` calls *will then generally be different*. To measure the expectation of a PauliSum, you probably want to do something like this:: progs, coefs = hamiltonian.get_programs() expect_coeffs = np.array(cxn.expectation(prep_program, operator_programs=progs)) return np.real_if_close(np.dot(coefs, expect_coeffs)) :param Program prep_prog: Quil program for state preparation. :param list operator_programs: A list of Programs, each specifying an operator whose expectation to compute. Default is a list containing only the empty Program. :return: Expectation values of the operators. :rtype: List[float] """ # Developer note: This code is for backwards compatibility. It can't be replaced with # ForestConnection._expectation because we've turned off the ability to set # `needs_compilation` (that usually indicates the user is doing something iffy like # using a noise model with this function) if isinstance(operator_programs, Program): warnings.warn( "You have provided a Program rather than a list of Programs. The results from expectation " "will be line-wise expectation values of the operator_programs.", SyntaxWarning) payload = self._expectation_payload(prep_prog, operator_programs) response = post_json(self.session, self.sync_endpoint + "/qvm", payload) return response.json() @_record_call def pauli_expectation(self, prep_prog, pauli_terms): """ Calculate the expectation value of Pauli operators given a state prepared by prep_program. If ``pauli_terms`` is a ``PauliSum`` then the returned value is a single ``float``, otherwise the returned value is a list of ``float``s, one for each ``PauliTerm`` in the list. :note: If the execution of ``quil_program`` is **non-deterministic**, i.e., if it includes measurements and/or noisy quantum gates, then the final wavefunction from which the expectation values are computed itself only represents a stochastically generated sample. The expectations returned from *different* ``expectation`` calls *will then generally be different*. :param Program prep_prog: Quil program for state preparation. :param Sequence[PauliTerm]|PauliSum pauli_terms: A list of PauliTerms or a PauliSum. :return: If ``pauli_terms`` is a PauliSum return its expectation value. Otherwise return a list of expectation values. :rtype: float|List[float] """ is_pauli_sum = False if isinstance(pauli_terms, PauliSum): progs, coeffs = pauli_terms.get_programs() is_pauli_sum = True else: coeffs = [pt.coefficient for pt in pauli_terms] progs = [pt.program for pt in pauli_terms] bare_results = self.expectation(prep_prog, progs) results = [c * r for c, r in zip(coeffs, bare_results)] if is_pauli_sum: return sum(results) return results def _expectation_payload(self, prep_prog, operator_programs): if operator_programs is None: operator_programs = [Program()] if not isinstance(prep_prog, Program): raise TypeError("prep_prog variable must be a Quil program object") payload = { 'type': TYPE_EXPECTATION, 'state-preparation': prep_prog.out(), 'operators': [x.out() for x in operator_programs] } self._add_rng_seed_to_payload(payload) return payload def _maybe_add_noise_to_payload(self, payload): """ Set the gate noise and measurement noise of a payload. """ if self.measurement_noise is not None: payload["measurement-noise"] = self.measurement_noise if self.gate_noise is not None: payload["gate-noise"] = self.gate_noise def _add_rng_seed_to_payload(self, payload): """ Add a random seed to the payload. """ if self.random_seed is not None: payload['rng-seed'] = self.random_seed