Mapomatic - Automatic mapping of compiled circuits to low-noise sub-graphs

Overview

mapomatic

Automatic mapping of compiled circuits to low-noise sub-graphs

Overview

One of the main painpoints in executing circuits on IBM Quantum hardware is finding the best qubit mapping. For a given circuit, one typically tries to pick the best initial_layout for a given target system, and then SWAP maps using that set of qubits as the starting point. However there are a couple of issues with that execution model. First, an initial_layout seletected, for example with respect to the noise characteristics of the system, need not be optimal for the SWAP mapping. In practice this leads to either low-noise layouts with extra SWAP gates inserted in the circuit, or optimally SWAP mapped circuits on (possibly) lousy qubits. Second, there is no way to know if the system you targeted in the compilation is actually the best one to execute the compiled circuit on. With 20+ quantum systems, it is hard to determine which device is actually ideal for a given problem.

mapomatic tries to tackle these issues in a different way. mapomatic is a post-compilation routine that finds the best low noise sub-graph on which to run a circuit given one or more quantum systems as target devices. Once compiled, a circuit has been rewritten so that its two-qubit gate structure matches that of a given sub-graph on the target system. mapomatic then searches for matching sub-graphs using the VF2 mapper in Qiskit (retworkx actually), and uses a heuristic to rank them based on error rates determined by the current calibration data. That is to say that given a single target system, mapomatic will return the best set of qubits on which to execute the compiled circuit. Or, given a list of systems, it will find the best system and set of qubits on which to run your circuit. Given the current size of quantum hardware, and the excellent performance of the VF2 mapper, this whole process is actually very fast.

Usage

To begin we first import what we need and load our IBM Quantum account.

import numpy as np
from qiskit import *
import mapomatic as mm

IBMQ.load_account()

Second we will select a provider that has one or more systems of interest in it:

provider = IBMQ.get_provider(group='deployed')

We then go through the usual step of making a circuit and calling transpile on a given backend:

qc = QuantumCircuit(5)
qc.h(0)
qc.cx(0,1)
qc.cx(0,2)
qc.cx(0,3)
qc.cx(0,4)
qc.measure_all()

Here we use optimization_level=3 as it is the best overall. It is also not noise-aware though, and thus can select lousy qubits on which to do a good SWAP mapping

trans_qc = transpile(qc, provider.get_backend('ibm_auckland'),optimization_level=3)

Now, a call to transpile inflates the circuit to the number of qubits in the target system. For small problems like the example here, this prevents us from finding the smaller sub-graphs. Thus we need to deflate the circuit down to just the number of active qubits:

small_qc = mm.deflate_circuit(trans_qc)

This deflated circuit, along with one or more backends can now be used to find the ideal system and mapping. Here we will look over all systems in the provider:

backends = provider.backends()

mm.best_mapping(small_qc, backends)

that returns a tuple with the target layout, system, and the computed error score:

([2, 1, 3, 5, 8], 'ibm_auckland', 0.09518597703355036)

You can then use the best layout in a new call to transpile which will then do the desired mapping for you. Alternatively, we can ask for the best mapping on all systems, yielding a list sorted in order from best to worse:

mm.best_mapping(small_qc, backends, successors=True)
[([2, 1, 3, 5, 8], 'ibm_auckland', 0.09518597703355036),
 ([7, 10, 4, 1, 0], 'ibm_hanoi', 0.11217956761629977),
 ([5, 6, 3, 1, 2], 'ibm_lagos', 0.1123755285308975),
 ([7, 6, 10, 12, 15], 'ibmq_mumbai', 0.13708593236124922),
 ([3, 2, 5, 8, 9], 'ibmq_montreal', 0.13762962991865924),
 ([2, 1, 3, 5, 8], 'ibm_cairo', 0.1423752001642351),
 ([1, 2, 3, 5, 6], 'ibmq_casablanca', 0.15623594190953083),
 ([4, 3, 5, 6, 7], 'ibmq_brooklyn', 0.16468576058762707),
 ([7, 6, 10, 12, 15], 'ibmq_guadalupe', 0.17186581811649904),
 ([5, 3, 8, 11, 14], 'ibmq_toronto', 0.1735555283027388),
 ([5, 4, 3, 1, 0], 'ibmq_jakarta', 0.1792325518776976),
 ([2, 3, 1, 0, 14], 'ibm_washington', 0.2078576175452339),
 ([1, 0, 2, 3, 4], 'ibmq_bogota', 0.23973220166838316),
 ([1, 2, 3, 5, 6], 'ibm_perth', 0.31268969778002176),
 ([3, 4, 2, 1, 0], 'ibmq_manila', 0.3182338194159915),
 ([1, 0, 2, 3, 4], 'ibmq_santiago', 1.0)]

Because of the stochastic nature of the SWAP mapping, the optimal sub-graph may change over repeated compilations.

Getting optimal results

Because the SWAP mappers in Qiskit are stochastic, the number of inserted SWAP gates can vary with each run. The spread in this number can be quite large, and can impact the performance of your circuit. It is thus beneficial to transpile many instances of a circuit and take the best one. For example:

trans_qc_list = transpile([qc]*20, provider.get_backend('ibm_auckland'), optimization_level=3)

best_cx_count = [circ.count_ops()['cx'] for circ in trans_qc_list]
best_cx_count
[10, 13, 10, 7, 7, 10, 10, 7, 10, 7, 10, 10, 10, 10, 5, 7, 6, 13, 7, 10]

We obviously want the one with minimum CNOT gates here:

best_idx = np.where(best_cx_count == np.min(best_cx_count))[0][0]
best_qc = trans_qc_list[best_idx] 

We can then use this best mapped circuit to find the ideal qubit candidates via mapomatic.

best_small_qc = mm.deflate_circuit(best_qc)
mm.best_mapping(best_small_qc, backends, successors=True)
[([11, 13, 14, 16, 19], 'ibm_auckland', 0.07634155667667142),
 ([2, 0, 1, 4, 7], 'ibm_hanoi', 0.0799012562006044),
 ([4, 6, 5, 3, 1], 'ibm_lagos', 0.09374259142721897),
 ([10, 15, 12, 13, 14], 'ibm_cairo', 0.0938958618334792),
 ([5, 9, 8, 11, 14], 'ibmq_montreal', 0.09663069814643488),
 ([10, 6, 7, 4, 1], 'ibmq_mumbai', 0.10253149958591112),
 ([10, 15, 12, 13, 14], 'ibmq_guadalupe', 0.11075230351892806),
 ([11, 5, 4, 3, 2], 'ibmq_brooklyn', 0.13179514610612808),
 ([0, 2, 1, 3, 5], 'ibm_perth', 0.13309987649094324),
 ([4, 6, 5, 3, 1], 'ibmq_casablanca', 0.13570907147053013),
 ([2, 0, 1, 3, 5], 'ibmq_jakarta', 0.14449169384159954),
 ([5, 9, 8, 11, 14], 'ibmq_toronto', 0.1495199193756318),
 ([2, 0, 1, 3, 4], 'ibmq_quito', 0.16858894163955718),
 ([0, 2, 1, 3, 4], 'ibmq_belem', 0.1783430267967986),
 ([0, 2, 1, 3, 4], 'ibmq_lima', 0.20380730100751476),
 ([23, 25, 24, 34, 43], 'ibm_washington', 0.23527393065514557)]
Comments
  • mm.matching is returning an empty list

    mm.matching is returning an empty list

                qc_transpiled = transpile(qc, optimization_level=opt_lvl)
                print(qc_transpiled.depth())
                print(dict(qc_transpiled.count_ops()))
                
                small_qc = mm.deflate_circuit(qc_transpiled)
                layouts = mm.matching_layouts(small_qc, device)
                print(layouts)
                scores = mm.evaluate_layouts(small_qc, layouts, device)
                print(scores)
                best_qc = transpile(small_qc, device, initial_layout=scores[0][0])
                print(best_qc.depth())
                print(dict(best_qc.count_ops()))
    

    layouts is giving me an empty list. Note: circuit depth is 3k... is it an issue?

    opened by aabennak 4
  • Marginally optimize interaction graph creation

    Marginally optimize interaction graph creation

    This commit tweaks the interaction graph creation so that it marginally more efficient. Instead of always having two calls to first create the nodes and then add the edges this commit changes the interaction graph creation to let retworkx create nodes at the same time it's adding edges. This is ever so slightly faster because it's all done on the rust side and we only need to copy and convert one object going over the Python<->Rust FFI boundary.

    opened by mtreinish 3
  • Include T1/T2 in the mapomatic score

    Include T1/T2 in the mapomatic score

    T1 and T2 data are available from the backend calibration data. By scheduling the circuit we could give an estimate of the error contribution of the qubits given the duration of the circuit

    opened by miamico 3
  • Return sorted lists by exact_mappings

    Return sorted lists by exact_mappings

    Return mappings as lists that are sorted for use by initial_layout

    Also had to bump call_limit to 1000 as otherwise VF2 was always missing a subgraph on the 27Q layout

    opened by nonhermitian 2
  • [WIP] Add idle errors to layout cost

    [WIP] Add idle errors to layout cost

    Schedules the circuit for each layout and computes the additional error due to idle periods from delay instructions. If the delay is before any other instruction then do not count it as qubits in ground state.

    opened by nonhermitian 1
  • Adjust CI trigger to run pre-merge on all pull requests

    Adjust CI trigger to run pre-merge on all pull requests

    This commit adjust the ci configuration for mapomatic to run pre-merge on all opened PRs to the main branch. Previously it was configured to only run on push events which would get triggered by any commit being pushed to an upstream branch. However, this wouldn't run CI on a pull request opened from a fork until after it was merged. By changing this to trigger on pull requests we enable running the tests before we merge to make sure all proposed changes don't cause a regresssion.

    opened by mtreinish 1
  • Update default call limit to 1e7

    Update default call limit to 1e7

    mapomatic was missing some layouts on larger topologies because the call limit was not large enough. Qiskit uses 3e7 at optimization_level=3. Here we set 1e7 to be on par with that.

    opened by nonhermitian 0
  • Add inflate circuit function

    Add inflate circuit function

    This allows for skipping the transpiler once the best layout is found. Directly inflating the circuit is 10x+ faster than going through the transpiler again at O0.

    • [x] Need tests
    opened by nonhermitian 0
  • Increase default call_limit to 1e5

    Increase default call_limit to 1e5

    We were missing some layout options by having not enough calls. This fixes that. At some point will need to figure out a heuristic for this. I am also wondering if the algorithm itself can be improved upon in our specific case because we do not have generic graphs, but rather repeatedly unit cells with symmetry.

    opened by nonhermitian 0
  • expose active qubits function

    expose active qubits function

    Getting active qubits from a circuit is something I am looking for again and again in the context of mapomatic. The internal function should be exposed.

    opened by nonhermitian 0
  • Add ability to specify custom cost functions easily

    Add ability to specify custom cost functions easily

    Adds the ability to define custom cost functions easily.

    The signiture is:

    def cost_func(circ, layouts, backend):
        """
        Parameters:
            circ (QuantumCircuit): circuit of interest
            layouts (list of lists): List of specified layouts
            backend (IBMQBackend): An IBM Quantum backend instance
    
        Returns:
            list: Tuples of layout and cost
        """
    
    
    opened by nonhermitian 0
  • Dynamic Circuit Error

    Dynamic Circuit Error

    I have a simple 2-qubit 2-bit dynamic circuit with 1 conditional rotation that executed on ibmq_manila just fine. But, when I add mapomatic I get:

    CircuitError: "Unknown classical resource specifier: 'CircuitInstruction(operation=Instruction(name='rz', num_qubits=1, num_clbits=0, params=[-3.141592653589793]), qubits=(Qubit(QuantumRegister(2, 'q'), 1),), clbits=())'."

    opened by bsiegelwax 2
  • Update test values that were broken by Terra transpilation changes

    Update test values that were broken by Terra transpilation changes

    Fixes #52 .

    The test was broken by https://github.com/Qiskit/qiskit-terra/pull/8552.

    Note that this change itself is broken by https://github.com/Qiskit/qiskit-terra/pull/9026, so the test will need to be updated again for the version of Terra that includes that change. Perhaps this indicates that the test should be modified. Apparently, the output of the call

    transpile(qc, FakeLima(), seed_transpiler=102442)
    

    can change with new versions of Terra. We should also consider pinning the version of Terra used by mapomatic.

    opened by kevinsung 1
  • Add support for BackendV2

    Add support for BackendV2

    import mapomatic
    from qiskit import QuantumCircuit, transpile
    from qiskit.providers.fake_provider import FakeBelemV2
    
    qc = QuantumCircuit(5)
    qc.h(0)
    qc.cx(0, 1)
    qc.cx(0, 2)
    qc.cx(0, 3)
    qc.cx(0, 4)
    qc.measure_all()
    
    backend = FakeBelemV2()
    trans_qc = transpile(qc, backend)
    
    print(mapomatic.best_overall_layout(trans_qc, [backend]))
    
    Traceback (most recent call last):
      File "/home/kevinsung/projects/scratch/mapomatic-backendv2.py", line 16, in <module>
        print(mapomatic.best_overall_layout(trans_qc, [backend]))
      File "/home/kevinsung/projects/mapomatic/mapomatic/layouts.py", line 190, in best_overall_layout
        config = backend.configuration()
    AttributeError: 'FakeBelemV2' object has no attribute 'configuration'
    
    opened by kevinsung 1
  • Failing test

    Failing test

    On main, running pytest gives

    ______________________________________________________________________________ test_best_mapping_ghz_state_full_device_multiple_qregs _______________________________________________________________________________
    
        def test_best_mapping_ghz_state_full_device_multiple_qregs():
            """Test best mappings with multiple registers"""
            qr_a = QuantumRegister(2)
            qr_b = QuantumRegister(3)
            qc = QuantumCircuit(qr_a, qr_b)
            qc.h(qr_a[0])
            qc.cx(qr_a[0], qr_a[1])
            qc.cx(qr_a[0], qr_b[0])
            qc.cx(qr_a[0], qr_b[1])
            qc.cx(qr_a[0], qr_b[2])
            qc.measure_all()
            trans_qc = transpile(qc, FakeLima(), seed_transpiler=102442)
            backends = [FakeBelem(), FakeQuito(), FakeLima()]
            res = mm.best_overall_layout(trans_qc, backends, successors=True)
            expected_res = [([0, 1, 2, 3, 4], 'fake_belem', 0.28117480552733065),
                            ([0, 1, 2, 3, 4], 'fake_lima', 0.2813874429560348),
                            ([2, 1, 0, 3, 4], 'fake_quito', 0.5101783470040677)]
            for index, expected in enumerate(expected_res):
    >           assert res[index][0] == expected[0]
    E           assert [2, 1, 0, 3, 4] == [0, 1, 2, 3, 4]
    E             At index 0 diff: 2 != 0
    E             Use -v to get more diff
    
    mapomatic/tests/test_best_layout.py:38: AssertionError
    

    This is also affecting the CI at https://github.com/Qiskit-Partners/mapomatic/actions/runs/3472401959/jobs/5804191803

    opened by kevinsung 0
  • Add support for Runtime backends

    Add support for Runtime backends

    Runtime backends have a different API than IBMQ backends. Example:

    import mapomatic
    from qiskit import QuantumCircuit
    from qiskit_ibm_runtime import QiskitRuntimeService
    
    qc = QuantumCircuit(5)
    qc.h(0)
    qc.cx(0, 1)
    qc.measure_all()
    
    service = QiskitRuntimeService()
    backend = service.backend("ibm_auckland")
    layout = mapomatic.best_overall_layout(qc, [backend])
    
    Traceback (most recent call last):
      File "/home/kjs/projects/scratch/mapomatic-update.py", line 12, in <module>
        layout = mapomatic.best_overall_layout(qc, [backend])
      File "/home/kjs/projects/mapomatic/mapomatic/layouts.py", line 203, in best_overall_layout
        best_out.append((layout, backend.name(), error))
    TypeError: 'str' object is not callable
    
    opened by kevinsung 1
Owner
Qiskit Partners
Qiskit Partners
Draw interactive NetworkX graphs with Altair

nx_altair Draw NetworkX graphs with Altair nx_altair offers a similar draw API to NetworkX but returns Altair Charts instead. If you'd like to contrib

Zachary Sailer 156 Feb 6, 2021
Generate graphs with NetworkX, natively visualize with D3.js and pywebview

webview_d3 This is some PoC code to render graphs created with NetworkX natively using D3.js and pywebview. The main benifit of this approac

byt3bl33d3r 68 Aug 18, 2022
BGraph is a tool designed to generate dependencies graphs from Android.bp soong files.

BGraph BGraph is a tool designed to generate dependencies graphs from Android.bp soong files. Overview BGraph (for Build-Graphs) is a project aimed at

Quarkslab 10 Dec 19, 2022
By default, networkx has problems with drawing self-loops in graphs.

By default, networkx has problems with drawing self-loops in graphs. It makes it hard to draw a graph with self-loops or to make a nicely looking chord diagram. This repository provides some code to draw self-loops nicely

Vladimir Shitov 5 Jan 6, 2022
Personal IMDB Graphs with Bokeh

Personal IMDB Graphs with Bokeh Do you like watching movies and also rate all of them in IMDB? Would you like to look at your IMDB stats based on your

null 2 Dec 15, 2021
A D3.js plugin that produces flame graphs from hierarchical data.

d3-flame-graph A D3.js plugin that produces flame graphs from hierarchical data. If you don't know what flame graphs are, check Brendan Gregg's post.

Martin Spier 740 Dec 29, 2022
Here are my graphs for hw_02

Let's Have A Look At Some Graphs! Graph 1: State Mentions in Congressperson's Tweets on 10/01/2017 The graph below uses this data set to demonstrate h

null 7 Sep 2, 2022
Generate knowledge graphs with interesting geometries, like lattices

Geometric Graphs Generate knowledge graphs with interesting geometries, like lattices. Works on Python 3.9+ because it uses cool new features. Get out

Charles Tapley Hoyt 5 Jan 3, 2022
Python ts2vg package provides high-performance algorithm implementations to build visibility graphs from time series data.

ts2vg: Time series to visibility graphs The Python ts2vg package provides high-performance algorithm implementations to build visibility graphs from t

Carlos Bergillos 26 Dec 17, 2022
Flame Graphs visualize profiled code

Flame Graphs visualize profiled code

Brendan Gregg 14.1k Jan 3, 2023
A simple interpreted language for creating basic mathematical graphs.

graphr Introduction graphr is a small language written to create basic mathematical graphs. It is an interpreted language written in python and essent

null 2 Dec 26, 2021
Kglab - an abstraction layer in Python for building knowledge graphs

Graph Data Science: an abstraction layer in Python for building knowledge graphs, integrated with popular graph libraries – atop Pandas, RDFlib, pySHACL, RAPIDS, NetworkX, iGraph, PyVis, pslpython, pyarrow, etc.

derwen.ai 466 Jan 9, 2023
An automatic prover for tautologies in Metamath

completeness An automatic prover for tautologies in Metamath This program implements the constructive proof of the Completeness Theorem for propositio

Scott Fenton 2 Dec 15, 2021
Automatic data visualization in atom with the nteract data-explorer

Data Explorer Interactively explore your data directly in atom with hydrogen! The nteract data-explorer provides automatic data visualization, so you

Ben Russert 65 Dec 1, 2022
A collection of over 5.1 million sub-domains and assets belonging to public bug bounty programs, compiled into a repo, for performing bulk operations.

?? Public Bug Bounty Targets Data By BugBountyResources A collection of over 5.1M sub-domains and assets belonging to bug bounty targets, all put in a

Bug Bounty Resources 87 Dec 13, 2022
Sub-tomogram-Detection - Deep learning based model for Cyro ET Sub-tomogram-Detection

Deep learning based model for Cyro ET Sub-tomogram-Detection High degree of stru

Siddhant Kumar 2 Feb 4, 2022
Re-implementation of the Noise Contrastive Estimation algorithm for pyTorch, following "Noise-contrastive estimation: A new estimation principle for unnormalized statistical models." (Gutmann and Hyvarinen, AISTATS 2010)

Noise Contrastive Estimation for pyTorch Overview This repository contains a re-implementation of the Noise Contrastive Estimation algorithm, implemen

Denis Emelin 42 Nov 24, 2022
Yunqi Chen 7 Oct 30, 2022
PyTorch implementation of SmoothGrad: removing noise by adding noise.

SmoothGrad implementation in PyTorch PyTorch implementation of SmoothGrad: removing noise by adding noise. Vanilla Gradients SmoothGrad Guided backpro

SSKH 143 Jan 5, 2023
Official implementation of "Open-set Label Noise Can Improve Robustness Against Inherent Label Noise" (NeurIPS 2021)

Open-set Label Noise Can Improve Robustness Against Inherent Label Noise NeurIPS 2021: This repository is the official implementation of ODNL. Require

Hongxin Wei 12 Dec 7, 2022