def metadata_save(qc: QuantumComputer, repo_path: str = None, filename: str = None) -> pd.DataFrame: ''' This helper function saves metadata related to your run on a Quantum computer. Basic data saved includes the date and time. Additionally information related to the quantum computer is saved: device name, topology, qubit labels, edges that have two qubit gates, device specs If a path is passed to this function information related to the Git Repository is saved, specifically the repository: name, branch and commit. :param qc: The QuantumComputer to run the experiment on. :param repo_path: path to repository e.g. '../' :param filename: The name of the file to write JSON-serialized results to. :return: pandas DataFrame ''' # Git related things if repo_path is not None: repo = Repo(repo_path) branch = repo.active_branch sha = repo.head.object.hexsha short_sha = repo.git.rev_parse(sha, short=7) the_repo = repo.git_dir the_branch = branch.name the_commit = short_sha else: the_repo = None the_branch = None the_commit = None metadata = { # Time and date 'Date': str(date.today()), 'Time': str(datetime.now().time()), # Git stuff 'Repository': the_repo, 'Branch': the_branch, 'Git_commit': the_commit, # QPU data 'Device_name': qc.name, 'Topology': qc.qubit_topology(), 'Qubits': list(qc.qubit_topology().nodes), 'Two_Qubit_Gates': list(qc.qubit_topology().edges), 'Device_Specs': qc.device.get_specs(), } if filename: pd.DataFrame(metadata).to_json(filename) return pd.DataFrame(metadata)
def get_qubit_registers_for_adder(qc: QuantumComputer, num_length: int, qubits: Optional[Sequence[int]] = None)\ -> Tuple[Sequence[int], Sequence[int], int, int]: """ Searches for a layout among the given qubits for the two n-bit registers and two additional ancilla that matches the simple layout given in figure 4 of [CDKM96]_. This method ignores any considerations of physical characteristics of the qc aside from the qubit layout. An error is thrown if the appropriate layout is not found. :param qc: the quantum resource on which an adder program will be executed. :param num_length: the length of the bitstring representation of one summand :param qubits: the available qubits on which to run the adder program. :return: the necessary registers and ancilla labels for implementing an adder program to add the numbers a and b. The output can be passed directly to :func:`adder` """ if qubits is None: unavailable = [] # assume this means all qubits in qc are available else: unavailable = [qubit for qubit in qc.qubits() if qubit not in qubits] graph = qc.qubit_topology().copy() for qubit in unavailable: graph.remove_node(qubit) # network x only provides subgraph isomorphism, but we want a subgraph monomorphism, i.e. we # specifically want to match the edges desired_layout with some subgraph of graph. To # accomplish this, we swap the nodes and edges of graph by making a line graph. line_graph = nx.line_graph(graph) # We want a path of n nodes, which has n-1 edges. Since we are matching edges of graph with # nodes of layout we make a layout of n-1 nodes. num_desired_nodes = 2 * num_length + 2 desired_layout = nx.path_graph(num_desired_nodes - 1) g_matcher = nx.algorithms.isomorphism.GraphMatcher(line_graph, desired_layout) try: # pick out a subgraph isomorphic to the desired_layout if one exists # this is an isomorphic mapping from edges in graph (equivalently nodes of line_graph) to # nodes in desired_layout (equivalently edges of a path graph with one more node) edge_iso = next(g_matcher.subgraph_isomorphisms_iter()) except IndexError: raise Exception( "An appropriate layout for the qubits could not be found among the " "provided qubits.") # pick out the edges of the isomorphism from the original graph subgraph = nx.Graph(graph.edge_subgraph(edge_iso.keys())) # pick out an endpoint of our path to start the assignment start_node = -1 for node in subgraph.nodes: if subgraph.degree(node) == 1: # found an endpoint start_node = node break return assign_registers_to_line_or_cycle(start_node, subgraph, num_length)
def test_device_stuff(): topo = nx.from_edgelist([(0, 4), (0, 99)]) qc = QuantumComputer( name='testy!', qam=None, # not necessary for this test device=NxDevice(topo), compiler=DummyCompiler()) assert nx.is_isomorphic(qc.qubit_topology(), topo) isa = qc.get_isa(twoq_type='CPHASE') assert sorted(isa.edges)[0].type == 'CPHASE' assert sorted(isa.edges)[0].targets == [0, 4]
def process_characterisation(qc: QuantumComputer) -> dict: """Convert a :py:class:`pyquil.api.QuantumComputer` to a dictionary containing Rigetti device Characteristics :param qc: A quantum computer to be converted :type qc: QuantumComputer :return: A dictionary containing Rigetti device characteristics """ specs = qc.device.get_specs() coupling_map = [[n, ni] for n, neigh_dict in qc.qubit_topology().adjacency() for ni, _ in neigh_dict.items()] node_ers_dict = {} link_ers_dict = {} t1_times_dict = {} t2_times_dict = {} device_node_fidelities = specs.f1QRBs() # type: ignore device_link_fidelities = specs.fCZs() # type: ignore device_fROs = specs.fROs() # type: ignore device_t1s = specs.T1s() # type: ignore device_t2s = specs.T2s() # type: ignore for index in qc.qubits(): error_cont = QubitErrorContainer({OpType.Rx, OpType.Rz}) error_cont.add_readout(1 - device_fROs[index]) # type: ignore t1_times_dict[index] = device_t1s[index] t2_times_dict[index] = device_t2s[index] error_cont.add_error( (OpType.Rx, 1 - device_node_fidelities[index])) # type: ignore # Rigetti use frame changes for Rz, so they effectively have no error. node_ers_dict[Node(index)] = error_cont for (a, b), fid in device_link_fidelities.items(): error_cont = QubitErrorContainer({OpType.CZ}) error_cont.add_error((OpType.CZ, 1 - fid)) # type: ignore link_ers_dict[(Node(a), Node(b))] = error_cont link_ers_dict[(Node(b), Node(a))] = error_cont arc = Architecture(coupling_map) characterisation = dict() characterisation["NodeErrors"] = node_ers_dict characterisation["EdgeErrors"] = link_ers_dict characterisation["Architecture"] = arc characterisation["t1times"] = t1_times_dict characterisation["t2times"] = t2_times_dict return characterisation
topology = nx.from_edgelist([ (10, 2), (10, 4), (10, 6), (10, 8), ]) device = NxDevice(topology) class MyLazyCompiler(AbstractCompiler): def quil_to_native_quil(self, program, *, protoquil=None): return program def native_quil_to_executable(self, nq_program): return nq_program my_qc = QuantumComputer( name='my-qvm', qam=QVM(connection=ForestConnection()), device=device, compiler=MyLazyCompiler(), ) nx.draw(my_qc.qubit_topology()) plt.title('5qcm', fontsize=18) plt.show() my_qc.run_and_measure(Program(X(10)), trials=5)