def test_process_characterisation_complete_noise_model() -> None: my_noise_model = NoiseModel() readout_error_0 = 0.2 readout_error_1 = 0.3 my_noise_model.add_readout_error( [ [1 - readout_error_0, readout_error_0], [readout_error_0, 1 - readout_error_0], ], [0], ) my_noise_model.add_readout_error( [ [1 - readout_error_1, readout_error_1], [readout_error_1, 1 - readout_error_1], ], [1], ) my_noise_model.add_quantum_error(depolarizing_error(0.6, 2), ["cx"], [0, 1]) my_noise_model.add_quantum_error(depolarizing_error(0.5, 1), ["u3"], [0]) my_noise_model.add_quantum_error(pauli_error([("X", 0.35), ("Z", 0.65)]), ["u2"], [0]) my_noise_model.add_quantum_error(pauli_error([("X", 0.35), ("Y", 0.65)]), ["u1"], [0]) back = AerBackend(my_noise_model) char = cast(Dict[str, Any], back.characterisation) dev = Device( char.get("NodeErrors", {}), char.get("EdgeErrors", {}), char.get("Architecture", Architecture([])), ) assert char["GenericTwoQubitQErrors"][(0, 1)][0][1][0] == 0.0375 assert char["GenericTwoQubitQErrors"][(0, 1)][0][1][15] == 0.4375 assert char["GenericOneQubitQErrors"][0][0][1][0] == 0.125 assert char["GenericOneQubitQErrors"][0][0][1][3] == 0.625 assert char["GenericOneQubitQErrors"][0][1][1][0] == 0.35 assert char["GenericOneQubitQErrors"][0][1][1][1] == 0.65 assert char["GenericOneQubitQErrors"][0][2][1][0] == 0.35 assert char["GenericOneQubitQErrors"][0][2][1][1] == 0.65 assert dev.get_error(OpType.U3, dev.nodes[0]) == 0.375 assert dev.get_error(OpType.CX, (dev.nodes[0], dev.nodes[1])) == 0.5625 assert dev.get_error(OpType.CX, (dev.nodes[1], dev.nodes[0])) == 0.80859375 assert char["ReadoutErrors"][0] == [[0.8, 0.2], [0.2, 0.8]] assert char["ReadoutErrors"][1] == [[0.7, 0.3], [0.3, 0.7]]
def __init__( self, noise_model: Optional[NoiseModel] = None, simulation_method: str = "automatic", ): """Backend for running simulations on the Qiskit Aer QASM simulator. :param noise_model: Noise model to apply during simulation. Defaults to None. :type noise_model: Optional[NoiseModel], optional :param simulation_method: Simulation method, see https://qiskit.org/documentation/stubs/qiskit.providers.aer.QasmSimulator.html for available values. Defaults to "automatic". :type simulation_method: str """ super().__init__("qasm_simulator") if not noise_model or all(value == [] for value in noise_model.to_dict().values()): self._noise_model = None else: self._noise_model = noise_model self._characterisation = _process_model(noise_model, self._gate_set) self._device = Device( self._characterisation.get("NodeErrors", {}), self._characterisation.get("EdgeErrors", {}), self._characterisation.get("Architecture", Architecture([])), ) self._memory = True self._backend.set_options(method=simulation_method)
def __init__( self, api_key: str, device_name: Optional[str] = "qpu", label: Optional[str] = "job", ): """ Construct a new IonQ backend. :param api_key: IonQ API key :type api_key: string :param device_name: device name, either "qpu" or "simulator". Default is "qpu". :type device_name: Optional[string] :param label: label to apply to submitted jobs. Default is "job". :type label: Optional[string] """ super().__init__() self._url = IONQ_JOBS_URL self._device_name = device_name self._label = label self._header = {"Authorization": f"apiKey {api_key}"} self._max_n_qubits = IONQ_N_QUBITS self._device = Device(FullyConnected(self._max_n_qubits)) self._qm = {Qubit(i): node for i, node in enumerate(self._device.nodes)} self._MACHINE_DEBUG = False
def _retrieve_device(self, machine: str) -> Device: jr = self.available_devices(self._api_handler) try: self._machine_info = next(entry for entry in jr if entry["name"] == machine) except StopIteration: raise RuntimeError(f"Device {machine} is not available.") return Device(FullyConnected(self._machine_info["n_qubits"]))
def test_device() -> None: fox = Foxtail char = process_characterisation(fox) dev = Device( char.get("NodeErrors", {}), char.get("EdgeErrors", {}), char.get("Architecture", Architecture([])), ) arc = dev.architecture assert str(arc) == "<tket::Architecture, nodes=22>"
def test_characterisation(qvm: None, quilc: None) -> None: b = ForestBackend("9q-square") char = b.characterisation assert char dev = Device( char.get("NodeErrors", {}), char.get("EdgeErrors", {}), char.get("Architecture", Architecture([])), ) assert dev
def tket_run(file_name, device_name): connection_list = qcdevice(device_name).connection_list circ = circuit_from_qasm(file_name) circ.measure_all() arc = Architecture(connection_list) dev = Device(arc) routed_circ = route(circ, arc) # cu = CompilationUnit(routed_circ) Transform.DecomposeBRIDGE().apply(routed_circ) # pass1 = DecomposeSwapsToCXs(dev) # pass1.apply(cu) # circ2 = cu.circuit return routed_circ
def test_process_characterisation() -> None: if not IBMQ.active_account(): IBMQ.load_account() provider = IBMQ.providers(hub="ibm-q", group="open")[0] back = provider.get_backend("ibmq_santiago") char = process_characterisation(back) dev = Device( char.get("NodeErrors", {}), char.get("EdgeErrors", {}), char.get("Architecture", Architecture([])), ) assert len(dev.nodes) == 5 assert len(dev.errors_by_node) == 5 assert len(dev.coupling) == 8
def __init__(self, qc_name: str, simulator: bool = True): """Backend for running circuits on a Rigetti QCS device or simulating with the QVM. :param qc_name: The name of the particular QuantumComputer to use. See the pyQuil docs for more details. :type qc_name: str :param simulator: Simulate the device with the QVM (True), or run on the QCS (False). Defaults to True. :type simulator: bool, optional """ super().__init__() self._qc: QuantumComputer = get_qc(qc_name, as_qvm=simulator) self._characterisation: dict = process_characterisation(self._qc) self._device: Device = Device( self._characterisation.get("NodeErrors", {}), self._characterisation.get("EdgeErrors", {}), self._characterisation.get("Architecture", Architecture([])), )
def test_process_characterisation_incomplete_noise_model() -> None: my_noise_model = NoiseModel() my_noise_model.add_quantum_error(depolarizing_error(0.6, 2), ["cx"], [0, 1]) my_noise_model.add_quantum_error(depolarizing_error(0.5, 1), ["u3"], [1]) my_noise_model.add_quantum_error(depolarizing_error(0.1, 1), ["u3"], [3]) my_noise_model.add_quantum_error(pauli_error([("X", 0.35), ("Z", 0.65)]), ["u2"], [0]) my_noise_model.add_quantum_error(pauli_error([("X", 0.35), ("Y", 0.65)]), ["u1"], [2]) back = AerBackend(my_noise_model) char = cast(Dict[str, Any], back.characterisation) c = Circuit(4).CX(0, 1).H(2).CX(2, 1).H(3).CX(0, 3).H(1).X(0).measure_all() back.compile_circuit(c) assert back.valid_circuit(c) dev = Device( char.get("NodeErrors", {}), char.get("EdgeErrors", {}), char.get("Architecture", Architecture([])), ) nodes = dev.nodes assert set(dev.architecture.coupling) == set([ (nodes[0], nodes[1]), (nodes[0], nodes[2]), (nodes[0], nodes[3]), (nodes[1], nodes[2]), (nodes[1], nodes[3]), (nodes[2], nodes[0]), (nodes[2], nodes[1]), (nodes[2], nodes[3]), (nodes[3], nodes[0]), (nodes[3], nodes[1]), (nodes[3], nodes[2]), ])
def __init__( self, device_name: Optional[str] = "qpu", api_key: Optional[str] = None, label: Optional[str] = "job", ): """ Construct a new IonQ backend. :param device_name: device name, either "qpu" or "simulator". Default is "qpu". :type device_name: Optional[string] :param api_key: IonQ API key. Default is None (read from config). :type api_key: Optional[string] :param label: label to apply to submitted jobs. Default is "job". :type label: Optional[string] """ super().__init__() self._url = IONQ_JOBS_URL self._device_name = device_name self._label = label config = IonQConfig.from_default_config_file() if api_key is None: api_key = config.api_key if api_key is None: raise IonQAuthenticationError() self._header = {"Authorization": f"apiKey {api_key}"} self._max_n_qubits = IONQ_N_QUBITS self._device = Device(FullyConnected(self._max_n_qubits)) self._qm = { Qubit(i): node for i, node in enumerate(self._device.nodes) } self._MACHINE_DEBUG = False
def __init__( self, backend_name: str, hub: Optional[str] = None, group: Optional[str] = None, project: Optional[str] = None, monitor: bool = True, ): """A backend for running circuits on remote IBMQ devices. :param backend_name: Name of the IBMQ device, e.g. `ibmqx4`, `ibmq_16_melbourne`. :type backend_name: str :param hub: Name of the IBMQ hub to use for the provider. If None, just uses the first hub found. Defaults to None. :type hub: Optional[str], optional :param group: Name of the IBMQ group to use for the provider. Defaults to None. :type group: Optional[str], optional :param project: Name of the IBMQ project to use for the provider. Defaults to None. :type project: Optional[str], optional :param monitor: Use the IBM job monitor. Defaults to True. :type monitor: bool, optional :raises ValueError: If no IBMQ account is loaded and none exists on the disk. """ super().__init__() if not IBMQ.active_account(): if IBMQ.stored_account(): IBMQ.load_account() else: raise NoIBMQAccountError() provider_kwargs = {} if hub: provider_kwargs["hub"] = hub if group: provider_kwargs["group"] = group if project: provider_kwargs["project"] = project try: if provider_kwargs: provider = IBMQ.get_provider(**provider_kwargs) else: provider = IBMQ.providers()[0] except qiskit.providers.ibmq.exceptions.IBMQProviderError as err: logging.warn( ("Provider was not specified enough, specify hub," "group and project correctly (check your IBMQ account).")) raise err self._backend: "_QiskIBMQBackend" = provider.get_backend(backend_name) self._config: "QasmBackendConfiguration" = self._backend.configuration( ) self._gate_set: Set[OpType] # simulator i.e. "ibmq_qasm_simulator" does not have `supported_instructions` # attribute self._gate_set = _tk_gate_set(self._backend) self._mid_measure = self._config.simulator or self._config.multi_meas_enabled self._legacy_gateset = OpType.V not in self._gate_set if self._legacy_gateset: if not self._gate_set >= { OpType.U1, OpType.U2, OpType.U3, OpType.CX }: raise NotImplementedError( f"Gate set {self._gate_set} unsupported") self._rebase_pass = RebaseIBM() else: if not self._gate_set >= { OpType.X, OpType.V, OpType.Rz, OpType.CX }: raise NotImplementedError( f"Gate set {self._gate_set} unsupported") self._rebase_pass = RebaseCustom( {OpType.CX}, Circuit(2).CX(0, 1), {OpType.X, OpType.V, OpType.Rz}, _tk1_to_x_v_rz, ) if hasattr(self._config, "max_experiments"): self._max_per_job = self._config.max_experiments else: self._max_per_job = 1 self._characterisation: Dict[str, Any] = process_characterisation( self._backend) self._device = Device( self._characterisation.get("NodeErrors", {}), self._characterisation.get("EdgeErrors", {}), self._characterisation.get("Architecture", Architecture([])), ) self._monitor = monitor self._MACHINE_DEBUG = False
# # Let's first mitigate error from a noisy simulation, using a noise model straight from the 5-qubit IBMQ Santiago device. This will require a preloaded IBMQ account. from qiskit import IBMQ IBMQ.load_account() from pytket.extensions.qiskit import process_characterisation from pytket.device import Device ibmq_santiago_backend = IBMQ.providers()[0].get_backend("ibmq_santiago") pytket_santiago_characterisation = process_characterisation( ibmq_santiago_backend) pytket_santiago_device = Device( pytket_santiago_characterisation["NodeErrors"], pytket_santiago_characterisation["EdgeErrors"], pytket_santiago_characterisation["Architecture"], ) import networkx as nx import matplotlib.pyplot as plt santiago_graph = nx.Graph(pytket_santiago_device.coupling) nx.draw(santiago_graph, labels={node: node for node in santiago_graph.nodes()}) # SPAM correction requires subsets of qubits which are assumed to only have SPAM errors correlated with each other, and no other qubits. # # Correlated errors are usually dependent on the connectivity layout of devices, as shown above. # # As Santiago is a small 5-qubit device with few connections, let's assume that all qubits have correlated SPAM errors. The number of calibration circuits produced is exponential in the maximum number of correlated circuits, so finding good subsets of correlated qubits is important for characterising larger devices with smaller experimental overhead. #
directory = 'benchmarks_qasm/' qasm_file = 'path to your qasm file' q_circ = QuantumCircuit.from_qasm_file(qasm_file) tk_circ = qiskit_to_tk(q_circ) backend = FakeMelbourne() coupling_list = backend.configuration().coupling_map coupling_map = CouplingMap(coupling_list) characterisation = process_characterisation(backend) directed_arc = Device( characterisation.get("NodeErrors", {}), characterisation.get("EdgeErrors", {}), characterisation.get("Architecture", Architecture([])), ) comp_tk = tk_circ.copy() DecomposeBoxes().apply(comp_tk) FullPeepholeOptimise().apply(comp_tk) CXMappingPass(directed_arc, NoiseAwarePlacement(directed_arc), directed_cx=True, delay_measures=True).apply(comp_tk) DecomposeSwapsToCXs(directed_arc).apply(comp_tk) cost = lambda c: c.n_gates_of_type(OpType.CX) comp = RepeatWithMetricPass( SequencePass(
# If we are given a target architecture, we can generate passes tailored to it. # # In `pytket` an architecture is defined by a connectivity graph, i.e. a list of pairs of qubits capable of executing two-qubit operations. For example, we can represent a 5-qubit linear architecture, with qubits labelled `n[i]`, as follows: from pytket.routing import Architecture from pytket.circuit import Node n = [Node("n", i) for i in range(5)] arc = Architecture([[n[0], n[1]], [n[1], n[2]], [n[2], n[3]], [n[3], n[4]]]) # A `Device` is a model of a physical device, which encapsulates both its `Architecture` and its gate noise characteristics. Ignoring the latter, we can construct a 'noise-free' `Device` directly from an `Architecture`: from pytket.device import Device dev = Device(arc) # Suppose we have a circuit that we wish to run on this device: circ = Circuit(5) circ.CX(0, 1) circ.H(0) circ.Z(1) circ.CX(0, 3) circ.Rx(1.5, 3) circ.CX(2, 4) circ.X(2) circ.CX(1, 4) circ.CX(0, 4) print(tk_to_qiskit(circ))
def __init__( self, local: bool = False, s3_bucket: str = "", s3_folder: str = "", device_type: str = "quantum-simulator", provider: str = "amazon", device: str = "sv1", ): """ Construct a new braket backend. If `local=True`, other parameters are ignored. :param local: use simulator running on local machine :param s3_bucket: name of S3 bucket to store results :param s3_folder: name of folder ("key") in S3 bucket to store results in :param device_type: device type from device ARN (e.g. "qpu") :param provider: provider name from device ARN (e.g. "ionq", "rigetti", ...) :paran device: device name from device ARN (e.g. "ionQdevice", "Aspen-8", ...) """ super().__init__() if local: self._device = LocalSimulator() self._device_type = _DeviceType.LOCAL else: self._device = AwsDevice( "arn:aws:braket:::" + "/".join(["device", device_type, provider, device])) self._s3_dest = (s3_bucket, s3_folder) aws_device_type = self._device.type if aws_device_type == AwsDeviceType.SIMULATOR: self._device_type = _DeviceType.SIMULATOR elif aws_device_type == AwsDeviceType.QPU: self._device_type = _DeviceType.QPU else: raise ValueError(f"Unsupported device type {aws_device_type}") props = self._device.properties.dict() paradigm = props["paradigm"] n_qubits = paradigm["qubitCount"] connectivity_graph = None # None means "fully connected" if self._device_type == _DeviceType.QPU: connectivity = paradigm["connectivity"] if connectivity["fullyConnected"]: self._all_qubits: List = list(range(n_qubits)) else: connectivity_graph = connectivity["connectivityGraph"] # Convert strings to ints connectivity_graph = dict( (int(k), [int(v) for v in l]) for k, l in connectivity_graph.items()) self._all_qubits = sorted(connectivity_graph.keys()) if n_qubits < len(self._all_qubits): # This can happen, at least on rigetti devices, and causes errors. # As a kludgy workaround, remove some qubits from the architecture. self._all_qubits = self._all_qubits[:( n_qubits - len(self._all_qubits))] connectivity_graph = dict( (k, [v for v in l if v in self._all_qubits]) for k, l in connectivity_graph.items() if k in self._all_qubits) self._characteristics: Optional[Dict] = props["provider"] else: self._all_qubits = list(range(n_qubits)) self._characteristics = None device_info = props["action"][DeviceActionType.JAQCD] supported_ops = set(op.lower() for op in device_info["supportedOperations"]) supported_result_types = device_info["supportedResultTypes"] self._result_types = set() for rt in supported_result_types: rtname = rt["name"] rtminshots = rt["minShots"] rtmaxshots = rt["maxShots"] self._result_types.add(rtname) if rtname == "StateVector": self._supports_state = True # Always use n_shots = 0 for StateVector elif rtname == "Amplitude": pass # Always use n_shots = 0 for Amplitude elif rtname == "Probability": self._probability_min_shots = rtminshots self._probability_max_shots = rtmaxshots elif rtname == "Expectation": self._supports_expectation = True self._expectation_allows_nonhermitian = False self._expectation_min_shots = rtminshots self._expectation_max_shots = rtmaxshots elif rtname == "Sample": self._supports_shots = True self._supports_counts = True self._sample_min_shots = rtminshots self._sample_max_shots = rtmaxshots elif rtname == "Variance": self._variance_min_shots = rtminshots self._variance_max_shots = rtmaxshots self._multiqs = set() self._singleqs = set() if not {"cnot", "rx", "rz"} <= supported_ops: # This is so that we can define RebaseCustom without prior knowledge of the # gate set. We could do better than this, by having a few different options # for the CX- and tk1-replacement circuits. But it seems all existing # backends support these gates. raise NotImplementedError( "Device must support cnot, rx and rz gates.") for t in supported_ops: tkt = _gate_types[t] if tkt is not None: if t in _multiq_gate_types: if self._device_type == _DeviceType.QPU and t in [ "ccnot", "cswap" ]: # FullMappingPass can't handle 3-qubit gates, so ignore them. continue self._multiqs.add(tkt) else: self._singleqs.add(tkt) self._req_preds = [ NoClassicalControlPredicate(), NoFastFeedforwardPredicate(), NoMidMeasurePredicate(), NoSymbolsPredicate(), GateSetPredicate(self._multiqs | self._singleqs), MaxNQubitsPredicate(n_qubits), ] if connectivity_graph is None: arch = FullyConnected(n_qubits) else: arch = Architecture([(k, v) for k, l in connectivity_graph.items() for v in l]) if self._device_type == _DeviceType.QPU: assert self._characteristics is not None node_errs = {} edge_errs = {} schema = self._characteristics["braketSchemaHeader"] if schema == IONQ_SCHEMA: fid = self._characteristics["fidelity"] mean_1q_err = 1 - fid["1Q"]["mean"] mean_2q_err = 1 - fid["2Q"]["mean"] err_1q_cont = QubitErrorContainer(self._singleqs) for optype in self._singleqs: err_1q_cont.add_error((optype, mean_1q_err)) err_2q_cont = QubitErrorContainer(self._multiqs) for optype in self._multiqs: err_2q_cont.add_error((optype, mean_2q_err)) for node in arch.nodes: node_errs[node] = err_1q_cont for coupling in arch.coupling: edge_errs[coupling] = err_2q_cont elif schema == RIGETTI_SCHEMA: specs = self._characteristics["specs"] specs1q, specs2q = specs["1Q"], specs["2Q"] for node in arch.nodes: nodespecs = specs1q[f"{node.index[0]}"] err_1q_cont = QubitErrorContainer(self._singleqs) for optype in self._singleqs: err_1q_cont.add_error( (optype, 1 - nodespecs.get("f1QRB", 1))) err_1q_cont.add_readout(nodespecs.get("fRO", 1)) node_errs[node] = err_1q_cont for coupling in arch.coupling: node0, node1 = coupling n0, n1 = node0.index[0], node1.index[0] couplingspecs = specs2q[f"{min(n0,n1)}-{max(n0,n1)}"] err_2q_cont = QubitErrorContainer({OpType.CZ}) err_2q_cont.add_error( (OpType.CZ, 1 - couplingspecs.get("fCZ", 1))) edge_errs[coupling] = err_2q_cont self._tket_device = Device(node_errs, edge_errs, arch) if connectivity_graph is not None: self._req_preds.append(ConnectivityPredicate( self._tket_device)) else: self._tket_device = None self._rebase_pass = RebaseCustom( self._multiqs, Circuit(), self._singleqs, lambda a, b, c: Circuit(1).Rz(c, 0).Rx(b, 0).Rz(a, 0), ) self._squash_pass = SquashCustom( self._singleqs, lambda a, b, c: Circuit(1).Rz(c, 0).Rx(b, 0).Rz(a, 0), )
# Second create an error type for our multi qubit errors: cx_error = 0.01 cx_gate_errors = QubitErrorContainer({OpType.CX: cx_error}) # Initialise a Device for id_architecture with homogeneous qubits and links: id_device = Device( { node_0: single_qubit_gate_errors, node_1: single_qubit_gate_errors, node_2: single_qubit_gate_errors, node_3: single_qubit_gate_errors, }, { id_coupling_map[0]: cx_gate_errors, id_coupling_map[0][::-1]: cx_gate_errors, id_coupling_map[1]: cx_gate_errors, id_coupling_map[1][::-1]: cx_gate_errors, id_coupling_map[2]: cx_gate_errors, id_coupling_map[2][::-1]: cx_gate_errors, }, id_architecture, ) id_device # Quantum Devices are full of different information and so creating an accurate Device object can become tedious. # # Our supported backends have helper methods for creating Devices. This requires installation of qiskit and a valid IBMQ user logged in.
[18, 15], [19, 20], [19, 16], [20, 21], [20, 19], [21, 28], [21, 22], [21, 20], [22, 23], [22, 21], [23, 24], [23, 22], [23, 17], [24, 25], [24, 23], [25, 29], [25, 26], [25, 24], [26, 27], [26, 25], [27, 26], [27, 18], [28, 32], [28, 21], [29, 36], [29, 25], [30, 39], [30, 31], [31, 32], [31, 30], [32, 33], [32, 31], [32, 28], [33, 34], [33, 32], [34, 40], [34, 35], [34, 33], [35, 36], [35, 34], [36, 37], [36, 35], [36, 29], [37, 38], [37, 36], [38, 41], [38, 37], [39, 42], [39, 30], [40, 46], [40, 34], [41, 50], [41, 38], [42, 43], [42, 39], [43, 44], [43, 42], [44, 51], [44, 45], [44, 43], [45, 46], [45, 44], [46, 47], [46, 45], [46, 40], [47, 48], [47, 46], [48, 52], [48, 49], [48, 47], [49, 50], [49, 48], [50, 49], [50, 41], [51, 44], [52, 48]] ### devices: rigetti_device = Device(Architecture(rigetti_coupling)) google_sycamore_device = Device(Architecture(google_coupling)) ibm_rochester_device = Device(Architecture(ibm_coupling)) all_device = Device(Architecture(all_to_all_coupling)) def gen_tket_pass(optimisepass, backend: str): if backend == _BACKEND_FULL: return optimisepass elif backend == _BACKEND_RIGETTI: final_pass = RebaseQuil() device = rigetti_device elif backend == _BACKEND_GOOGLE: final_pass = RebaseCirq() device = google_sycamore_device elif backend == _BACKEND_IBM: