def test_http_compilation_failure(compiler): device_name = "test_device" mock_url = "http://mock-qpu-compiler" config = PyquilConfig(TEST_CONFIG_PATHS) session = get_session(config=config) mock_adapter = requests_mock.Adapter() session.mount("http://", mock_adapter) headers = { # access token from ./data/user_auth_token_valid.json. "Authorization": "Bearer secret" } mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/get_version_info", status_code=200, json={}, headers=headers, ) mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/native_quilt_to_binary", status_code=500, json={"message": "test compilation failed"}, headers=headers, ) mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/get_quilt_calibrations", status_code=200, json=CALIBRATIONS_RESPONSE, headers=headers, ) device = Device(name="not_actually_device_name", raw={ "device_name": device_name, "isa": DUMMY_ISA_DICT }) compiler = QPUCompiler( quilc_endpoint=session.config.quilc_url, qpu_compiler_endpoint=mock_url, device=device, session=session, ) native_quil = compiler.quil_to_native_quil(simple_program()) try: compiler.native_quil_to_executable(native_quil) except UserMessageError as e: assert "test compilation failed" in str(e)
def test_compile_with_quilt_calibrations(compiler): device_name = "test_device" mock_url = "http://mock-qpu-compiler" config = PyquilConfig(TEST_CONFIG_PATHS) session = get_session(config=config) mock_adapter = requests_mock.Adapter() session.mount("http://", mock_adapter) headers = { # access token from ./data/user_auth_token_valid.json. "Authorization": "Bearer secret" } mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/get_version_info", status_code=200, json={}, headers=headers, ) mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/native_quilt_to_binary", status_code=200, json=SIMPLE_RESPONSE, headers=headers, ) device = Device(name="not_actually_device_name", raw={ "device_name": device_name, "isa": DUMMY_ISA_DICT }) compiler = QPUCompiler( quilc_endpoint=session.config.quilc_url, qpu_compiler_endpoint=mock_url, device=device, session=session, ) program = simple_program() q = FormalArgument("q") defn = DefCalibration( "H", [], [q], [RZ(math.pi / 2, q), RX(math.pi / 2, q), RZ(math.pi / 2, q)]) cals = [defn] program._calibrations = cals # this should more or less pass through compilation_result = compiler.quil_to_native_quil(program, protoquil=True) assert compilation_result.calibrations == cals assert program.calibrations == cals assert compilation_result == program
def test_http_compilation(compiler): device_name = "test_device" mock_url = "http://mock-qpu-compiler" config = PyquilConfig(TEST_CONFIG_PATHS) session = get_session(config=config) mock_adapter = requests_mock.Adapter() session.mount("http://", mock_adapter) headers = { # access token from ./data/user_auth_token_valid.json. "Authorization": "Bearer secret" } mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/get_version_info", status_code=200, json={}, headers=headers, ) mock_adapter.register_uri( "POST", f"{mock_url}/devices/{device_name}/native_quil_to_binary", status_code=200, json=SIMPLE_RESPONSE, headers=headers, ) device = Device( name="not_actually_device_name", raw={"device_name": device_name, "isa": DUMMY_ISA_DICT} ) compiler = QPUCompiler( quilc_endpoint=session.config.quilc_url, qpu_compiler_endpoint=mock_url, device=device, session=session, ) compilation_result = compiler.native_quil_to_executable( compiler.quil_to_native_quil(simple_program()) ) assert isinstance(compilation_result, BinaryExecutableResponse) assert compilation_result.program == SIMPLE_RESPONSE["program"]
def test_compile_with_quilt_calibrations(compiler: QPUCompiler): program = simple_program() q = FormalArgument("q") defn = DefCalibration("H", [], [q], [RZ(math.pi / 2, q), RX(math.pi / 2, q), RZ(math.pi / 2, q)]) cals = [defn] program._calibrations = cals # this should more or less pass through compilation_result = compiler.quil_to_native_quil(program, protoquil=True) assert compilation_result.calibrations == cals assert program.calibrations == cals assert compilation_result == program
def test_invalid_protocol(): device_name = "test_device" mock_url = "not-http-or-tcp://mock-qpu-compiler" config = PyquilConfig(TEST_CONFIG_PATHS) session = get_session(config=config) mock_adapter = requests_mock.Adapter() session.mount("", mock_adapter) device = Device( name="not_actually_device_name", raw={"device_name": device_name, "isa": DUMMY_ISA_DICT} ) with pytest.raises(UserMessageError): QPUCompiler( quilc_endpoint=session.config.quilc_url, qpu_compiler_endpoint=mock_url, device=device, session=session, )
def get_qc(name: str, *, as_qvm: bool = None, noisy: bool = None, connection: ForestConnection = None) -> QuantumComputer: """ Get a quantum computer. A quantum computer is an object of type :py:class:`QuantumComputer` and can be backed either by a QVM simulator ("Quantum/Quil Virtual Machine") or a physical Rigetti QPU ("Quantum Processing Unit") made of superconducting qubits. You can choose the quantum computer to target through a combination of its name and optional flags. There are multiple ways to get the same quantum computer. The following are equivalent:: >>> qc = get_qc("Aspen-0-12Q-A-noisy-qvm") >>> qc = get_qc("Aspen-0-12Q-A", as_qvm=True, noisy=True) and will construct a simulator of the 8q-agave chip with a noise model based on device characteristics. We also provide a means for constructing generic quantum simulators that are not related to a given piece of Rigetti hardware:: >>> qc = get_qc("9q-square-qvm") >>> qc = get_qc("9q-square", as_qvm=True) Finally, you can get request a QVM with "no" topology of a given number of qubits (technically, it's a fully connected graph among the given number of qubits) with:: >>> qc = get_qc("5q-qvm") # or "6q-qvm", or "34q-qvm", ... Redundant flags are acceptable, but conflicting flags will raise an exception:: >>> qc = get_qc("9q-square-qvm") # qc is fully specified by its name >>> qc = get_qc("9q-square-qvm", as_qvm=True) # redundant, but ok >>> qc = get_qc("9q-square-qvm", as_qvm=False) # Error! Use :py:func:`list_quantum_computers` to retrieve a list of known qc names. This method is provided as a convenience to quickly construct and use QVM's and QPU's. Power users may wish to have more control over the specification of a quantum computer (e.g. custom noise models, bespoke topologies, etc.). This is possible by constructing a :py:class:`QuantumComputer` object by hand. Please refer to the documentation on :py:class:`QuantumComputer` for more information. :param name: The name of the desired quantum computer. This should correspond to a name returned by :py:func:`list_quantum_computers`. Names ending in "-qvm" will return a QVM. Names ending in "-noisy-qvm" will return a QVM with a noise model. Otherwise, we will return a QPU with the given name. :param as_qvm: An optional flag to force construction of a QVM (instead of a QPU). If specified and set to ``True``, a QVM-backed quantum computer will be returned regardless of the name's suffix :param noisy: An optional flag to force inclusion of a noise model. If specified and set to ``True``, a quantum computer with a noise model will be returned regardless of the name's suffix. The noise model for QVM's based on a real QPU is an empirically parameterized model based on real device noise characteristics. The generic QVM noise model is simple T1 and T2 noise plus readout error. See :py:func:`decoherance_noise_with_asymmetric_ro`. :param connection: An optional :py:class:ForestConnection` object. If not specified, the default values for URL endpoints, ping time, and status time will be used. Your user id and API key will be read from ~/.pyquil_config. If you deign to change any of these parameters, pass your own :py:class:`ForestConnection` object. :return: """ if connection is None: connection = ForestConnection() full_name = name name, as_qvm, noisy = _parse_name(name, as_qvm, noisy) ma = re.fullmatch(r'(\d+)q', name) if ma is not None: n_qubits = int(ma.group(1)) if not as_qvm: raise ValueError("Please name a valid device or run as a QVM") return _get_unrestricted_qvm(connection=connection, noisy=noisy, n_qubits=n_qubits) if name == '9q-generic' or name == '9q-square': if name == '9q-generic': warnings.warn("Please prefer '9q-square' instead of '9q-generic'", DeprecationWarning) if not as_qvm: raise ValueError( "The device '9q-square' is only available as a QVM") return _get_9q_square_qvm(connection=connection, noisy=noisy) device = get_lattice(name) if not as_qvm: if noisy is not None and noisy: warnings.warn( "You have specified `noisy=True`, but you're getting a QPU. This flag " "is meant for controlling noise models on QVMs.") return QuantumComputer(name=full_name, qam=QPU(endpoint=pyquil_config.qpu_url, user=pyquil_config.user_id), device=device, compiler=QPUCompiler( endpoint=pyquil_config.compiler_url, device=device)) if noisy: noise_model = device.noise_model else: noise_model = None return QuantumComputer(name=full_name, qam=QVM(connection=connection, noise_model=noise_model), device=device, compiler=_get_qvm_compiler_based_on_endpoint( device=device, endpoint=connection.compiler_endpoint))
def get_qc(name: str, *, as_qvm: bool = None, noisy: bool = None, connection: ForestConnection = None) -> QuantumComputer: """ Get a quantum computer. A quantum computer is an object of type :py:class:`QuantumComputer` and can be backed either by a QVM simulator ("Quantum/Quil Virtual Machine") or a physical Rigetti QPU ("Quantum Processing Unit") made of superconducting qubits. You can choose the quantum computer to target through a combination of its name and optional flags. There are multiple ways to get the same quantum computer. The following are equivalent:: >>> qc = get_qc("Aspen-1-16Q-A-noisy-qvm") >>> qc = get_qc("Aspen-1-16Q-A", as_qvm=True, noisy=True) and will construct a simulator of an Aspen-1 lattice with a noise model based on device characteristics. We also provide a means for constructing generic quantum simulators that are not related to a given piece of Rigetti hardware:: >>> qc = get_qc("9q-square-qvm") >>> qc = get_qc("9q-square", as_qvm=True) Finally, you can get request a QVM with "no" topology of a given number of qubits (technically, it's a fully connected graph among the given number of qubits) with:: >>> qc = get_qc("5q-qvm") # or "6q-qvm", or "34q-qvm", ... These less-realistic, fully-connected QVMs will also be more lenient on what types of programs they will ``run``. Specifically, you do not need to do any compilation. For the other, realistic QVMs you must use :py:func:`qc.compile` or :py:func:`qc.compiler.native_quil_to_executable` prior to :py:func:`qc.run`. The Rigetti QVM must be downloaded from https://www.rigetti.com/forest and run as a server alongside your python program. To use pyQuil's built-in QVM, replace all ``"-qvm"`` suffixes with ``"-pyqvm"``:: >>> qc = get_qc("5q-pyqvm") Redundant flags are acceptable, but conflicting flags will raise an exception:: >>> qc = get_qc("9q-square-qvm") # qc is fully specified by its name >>> qc = get_qc("9q-square-qvm", as_qvm=True) # redundant, but ok >>> qc = get_qc("9q-square-qvm", as_qvm=False) # Error! Use :py:func:`list_quantum_computers` to retrieve a list of known qc names. This method is provided as a convenience to quickly construct and use QVM's and QPU's. Power users may wish to have more control over the specification of a quantum computer (e.g. custom noise models, bespoke topologies, etc.). This is possible by constructing a :py:class:`QuantumComputer` object by hand. Please refer to the documentation on :py:class:`QuantumComputer` for more information. :param name: The name of the desired quantum computer. This should correspond to a name returned by :py:func:`list_quantum_computers`. Names ending in "-qvm" will return a QVM. Names ending in "-pyqvm" will return a :py:class:`PyQVM`. Names ending in "-noisy-qvm" will return a QVM with a noise model. Otherwise, we will return a QPU with the given name. :param as_qvm: An optional flag to force construction of a QVM (instead of a QPU). If specified and set to ``True``, a QVM-backed quantum computer will be returned regardless of the name's suffix :param noisy: An optional flag to force inclusion of a noise model. If specified and set to ``True``, a quantum computer with a noise model will be returned regardless of the name's suffix. The noise model for QVMs based on a real QPU is an empirically parameterized model based on real device noise characteristics. The generic QVM noise model is simple T1 and T2 noise plus readout error. See :py:func:`~pyquil.noise.decoherence_noise_with_asymmetric_ro`. :param connection: An optional :py:class:`ForestConnection` object. If not specified, the default values for URL endpoints will be used. If you deign to change any of these parameters, pass your own :py:class:`ForestConnection` object. :return: A pre-configured QuantumComputer """ # 1. Parse name, check for redundant options, canonicalize names. prefix, qvm_type, noisy = _parse_name(name, as_qvm, noisy) del as_qvm # do not use after _parse_name name = _canonicalize_name(prefix, qvm_type, noisy) # 2. Check for unrestricted {n}q-qvm ma = re.fullmatch(r'(\d+)q', prefix) if ma is not None: n_qubits = int(ma.group(1)) if qvm_type is None: raise ValueError("Please name a valid device or run as a QVM") return _get_unrestricted_qvm(name=name, connection=connection, noisy=noisy, n_qubits=n_qubits, qvm_type=qvm_type) # 3. Check for "9q-square" qvm if prefix == '9q-generic' or prefix == '9q-square': if prefix == '9q-generic': warnings.warn("Please prefer '9q-square' instead of '9q-generic'", DeprecationWarning) if qvm_type is None: raise ValueError( "The device '9q-square' is only available as a QVM") return _get_9q_square_qvm(name=name, connection=connection, noisy=noisy, qvm_type=qvm_type) # 4. Not a special case, query the web for information about this device. device = get_lattice(prefix) if qvm_type is not None: # 4.1 QVM based on a real device. return _get_qvm_based_on_real_device(name=name, device=device, noisy=noisy, connection=connection, qvm_type=qvm_type) else: # 4.2 A real device if noisy is not None and noisy: warnings.warn( "You have specified `noisy=True`, but you're getting a QPU. This flag " "is meant for controlling noise models on QVMs.") return QuantumComputer( name=name, qam=QPU(endpoint=pyquil_config.qpu_url, user=pyquil_config.user_id), device=device, compiler=QPUCompiler( quilc_endpoint=pyquil_config.quilc_url, qpu_compiler_endpoint=pyquil_config.qpu_compiler_url, device=device, name=prefix))