GithubHelp home page GithubHelp logo

qiskit / qiskit-transpiler-service Goto Github PK

View Code? Open in Web Editor NEW
2.0 4.0 3.0 1.48 MB

Home Page: https://pypi.org/project/qiskit-transpiler-service/

License: Apache License 2.0

Makefile 0.01% Jupyter Notebook 95.60% Python 4.40%

qiskit-transpiler-service's Introduction

qiskit_transpiler_service

A library to use Qiskit Transpiler service and the AI transpiler passes.

Note The Qiskit transpiler service and the AI transpiler passes use different experimental services that are only available for IBM Quantum Premium Plan users. This library and the releated services are an alpha release, subject to change.

Installing the qiskit-transpiler-service

To use the Qiskit transpiler service, install the qiskit-transpiler-service package:

pip install qiskit-transpiler-service

By default, the package tries to authenticate to IBM Quantum services with the defined Qiskit API token, and uses your token from the QISKIT_IBM_TOKEN environment variable or from the file ~/.qiskit/qiskit-ibm.json (under the section default-ibm-quantum).

Note: This library requires Qiskit 1.0 by default.

How to use the library

Using the Qiskit Transpiler service

The following examples demonstrate how to transpile circuits using the Qiskit transpiler service with different parameters.

  1. Create a circuit and call the Qiskit transpiler service to transpile the circuit with ibm_sherbrooke as the backend_name, 3 as the optimization_level, and not using AI during the transpilation.

    from qiskit.circuit.library import EfficientSU2
    from qiskit_transpiler_service.transpiler_service import TranspilerService
    
    circuit = EfficientSU2(101, entanglement="circular", reps=1).decompose()
    
    cloud_transpiler_service = TranspilerService(
        backend_name="ibm_sherbrooke",
        ai='false',
        optimization_level=3,
    )
    transpiled_circuit = cloud_transpiler_service.run(circuit)

Note: you only can use backend_name devices you are allowed to with your IBM Quantum Account. Apart from the backend_name, the TranspilerService also allows coupling_map as parameter.

  1. Produce a similar circuit and transpile it, requesting AI transpiling capabilities by setting the flag ai to 'true':

    from qiskit.circuit.library import EfficientSU2
    from qiskit_transpiler_service.transpiler_service import TranspilerService
    
    circuit = EfficientSU2(101, entanglement="circular", reps=1).decompose()
    
    cloud_transpiler_service = TranspilerService(
        backend_name="ibm_sherbrooke",
        ai='true',
        optimization_level=1,
    )
    transpiled_circuit = cloud_transpiler_service.run(circuit)

Using the AIRouting pass manually

The AIRouting pass acts both as a layout stage and a routing stage. It can be used within a PassManager as follows:

from qiskit.transpiler import PassManager
from qiskit_transpiler_service.ai.routing import AIRouting
from qiskit.circuit.library import EfficientSU2

ai_passmanager = PassManager([
  AIRouting(backend_name="ibm_sherbrooke", optimization_level=2, layout_mode="optimize")
])

circuit = EfficientSU2(101, entanglement="circular", reps=1).decompose()

transpiled_circuit = ai_passmanager.run(circuit)

Here, the backend_name determines which backend to route for, the optimization_level (1, 2, or 3) determines the computational effort to spend in the process (higher usually gives better results but takes longer), and the layout_mode specifies how to handle the layout selection. The layout_mode includes the following options:

  • keep: This respects the layout set by the previous transpiler passes (or uses the trivial layout if not set). It is typically only used when the circuit must be run on specific qubits of the device. It often produces worse results because it has less room for optimization.
  • improve: This uses the layout set by the previous transpiler passes as a starting point. It is useful when you have a good initial guess for the layout; for example, for circuits that are built in a way that approximately follows the device's coupling map. It is also useful if you want to try other specific layout passes combined with the AIRouting pass.
  • optimize: This is the default mode. It works best for general circuits where you might not have good layout guesses. This mode ignores previous layout selections.

Using the AI circuit synthesis passes

The AI circuit synthesis passes allow you to optimize pieces of different circuit types (Clifford, Linear Function, Permutation) by re-synthesizing them. The typical way one would use the synthesis pass is the following:

from qiskit.transpiler import PassManager

from qiskit_transpiler_service.ai.routing import AIRouting
from qiskit_transpiler_service.ai.synthesis import AILinearFunctionSynthesis
from qiskit_transpiler_service.ai.collection import CollectLinearFunctions
from qiskit.circuit.library import EfficientSU2

ai_passmanager = PassManager([
  AIRouting(backend_name="ibm_cairo", optimization_level=3, layout_mode="optimize"),  # Route circuit
  CollectLinearFunctions(),  # Collect Linear Function blocks
  AILinearFunctionSynthesis(backend_name="ibm_cairo")  # Re-synthesize Linear Function blocks
])

circuit = EfficientSU2(10, entanglement="full", reps=1).decompose()

transpiled_circuit = ai_passmanager.run(circuit)

The synthesis respects the coupling map of the device: it can be run safely after other routing passes without "messing up" the circuit, so the overall circuit will still follow the device restrictions. By default, the synthesis will replace the original sub-circuit only if the synthesized sub-circuit improves the original (currently only checking CNOT count), but this can be forced to always replace the circuit by setting replace_only_if_better=False.

The following synthesis passes are available from qiskit_transpiler_service.ai.synthesis:

  • AICliffordSynthesis: Synthesis for Clifford circuits (blocks of H, S and CX gates). Currently up to 9 qubit blocks.
  • AILinearFunctionSynthesis: Synthesis for Linear Function circuits (blocks of CX and SWAP gates). Currently up to 9 qubit blocks.
  • AIPermutationSynthesis: Synthesis for Permutation circuits (blocks of SWAP gates). Currently available for 65, 33, and 27 qubit blocks.

We expect to gradually increase the size of the supported blocks.

All passes use a thread pool to send several requests in parallel. By default it will use as max threads as number of cores plus four (default values for ThreadPoolExecutor python object). However, you can set your own value with the max_threads argument at pass instantation. For example, the following line will instantiate the AILinearFunctionSynthesis pass allowing it to use a maximum of 20 threads.

AILinearFunctionSynthesis(backend_name="ibm_cairo", max_threads=20)  # Re-synthesize Linear Function blocks using 20 threads max

You can also set the environment variable AI_TRANSPILER_MAX_THREADS to the desired number of maximum threads, and all synthesis passes instantiated after that will use that value.

For sub-circuit to be synthesized by the AI synthesis passes, it must lay on a connected subgraph of the coupling map (this can be ensured by just doing a routing pass previous to collecting the blocks, but this is not the only way to do it). The synthesis passes will automatically check if a the specific subgraph where the sub-circuit lays is supported, and if it is not supported it will raise a warning and just leave the original sub-circuit as it is.

To complement the synthesis passes we also provide custom collection passes for Cliffords, Linear Functions and Permutations that can be imported from qiskit_transpiler_service.ai.collection:

  • CollectCliffords: Collects Clifford blocks as Instruction objects and stores the original sub-circuit to compare against it after synthesis.
  • CollectLinearFunctions: Collects blocks of SWAP and CX as LinearFunction objects and stores the original sub-circuit to compare against it after synthesis.
  • CollectPermutations: Collects blocks of SWAP circuits as Permutations.

These custom collection passes limit the sizes of the collected sub-circuits so that they are supported by the AI synthesis passes, so it is recommended to use them after the routing passes and before the synthesis passes to get a better optimization overall.

Customize logs

The library is prepared to let the user log the messages they want. For that, users only have to add the following code to their code:

import logging

logging.getLogger("qiskit_transpiler_service").setLevel(logging.X)

where X can be: NOTSET, DEBUG, INFO, WARNING, ERROR or CRITICAL

Citation

If you use any AI-powered feature from the Qiskit transpiler service in your research, use the following recommended citation:

@misc{2405.13196,
Author = {David Kremer and Victor Villar and Hanhee Paik and Ivan Duran and Ismael Faro and Juan Cruz-Benito},
Title = {Practical and efficient quantum circuit synthesis and transpiling with Reinforcement Learning},
Year = {2024},
Eprint = {arXiv:2405.13196},
}

qiskit-transpiler-service's People

Contributors

1ucian0 avatar cbjuan avatar chrisamirani avatar eric-arellano avatar y4izus avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

qiskit-transpiler-service's Issues

Circuits returned by service are not unitarily equivilent to the inputs

A simple example shows that the transpiler service is returning circuits that are not unitarily equivalent to the input. E.g.

from qiskit import *
from qiskit.quantum_info import Operator
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_transpiler_service.transpiler_service import TranspilerService
import mapomatic as mm

service = QiskitRuntimeService()
backend = service.backend('ibm_kyoto')

pm = TranspilerService(
    backend_name=backend.name,
    ai="true",
    optimization_level=1,
)

qc = QuantumCircuit(2)
qc.swap(0,1)

trans_qc = pm.run(qc)

# This should be True, but is False. 
Operator.from_circuit(qc) == Operator.from_circuit(mm.deflate_circuit(trans_qc))

# The Qiskit transpiler returns True here

ans = transpile(qc, backend, optimization_level=0)
Operator.from_circuit(qc) == Operator.from_circuit(mm.deflate_circuit(ans))

The unitary matrix returned by the service is very far from the target

The target unitary is

Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
          [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
          [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
          [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]],
         input_dims=(2, 2), output_dims=(2, 2))

but the service gives

Operator([[ 7.07106781e-01+7.07106781e-01j,
           -7.85046229e-17+7.85046229e-17j,
           -1.36845553e-48+1.74315280e-32j,
           -1.57009246e-16-1.57009246e-16j],
          [-4.35788200e-33+2.17894100e-32j,
           -1.57009246e-16+0.00000000e+00j,
            7.07106781e-01+7.07106781e-01j,
           -1.96261557e-17+9.81307787e-17j],
          [-9.81307787e-17+1.96261557e-17j,
            7.07106781e-01+7.07106781e-01j,
            1.57009246e-16+0.00000000e+00j,
            2.17894100e-32-4.35788200e-33j],
          [ 1.57009246e-16+1.57009246e-16j,
           -1.36845553e-48-1.74315280e-32j,
           -1.37383090e-16+5.88784672e-17j,
            7.07106781e-01+7.07106781e-01j]],
         input_dims=(2, 2), output_dims=(2, 2))

Allow max polling time to be configurable

Related to #21.

Instead of hard-coding the timeout values for polling the API for new transpilation results, we should be able to configure the timeout via config (config file, environment variable or others).

Increase the polling timeout to check transpilation results

Recently, the Qiskit transpiler service has upgraded the transpilation-related limits. Now the max transpilation time allowed is 10 minutes

Let's upgrade the client timeout to that value (600s). In the future, we'd need to improve how we manage the polling in this client.

Service error is raised if circuit contains a barrier instruction in the middle

The service will yield a TranspilerError: 'Service error.' if there is a barrier in the middle.

This circuit will work if the barrier is removed, but otherwise raises:

def trivial_bvlike_circuit(N):
    """A trivial circuit that should boil down
    to just a X and Z gate since they commute out

    Parameters:
        N (int): Number of qubits

    Returns:
        QuantumCircuit: Output circuit
    """
    qc = QuantumCircuit(N)
    for kk in range(N - 1):
        qc.cx(kk, N - 1)
    qc.x(N - 1)
    qc.z(N - 2)
    qc.barrier()
    for kk in range(N - 2, -1, -1):
        qc.cx(kk, N - 1)
    return qc

Circuits from service should track the global phase

The issue that raised #11 is due to the fact that it seems the transpiler service is not keeping track of the global phase and/or attaching that phase to the output circuits. This would be nice to have since those global phases become local phases if the circuits are used as sub-circuits, and it would also make the transpiler service inline with what Qiskit itself does.

Error `to_matrix not defined for this`

Sometimes we get this to_matrix not defined for this error. To reproduce this we can use the following code:

from qiskit_transpiler_service.ai.synthesis import AILinearFunctionSynthesis
from qiskit_transpiler_service.ai.collection import CollectLinearFunctions
from qiskit_transpiler_service.ai.routing import AIRouting
from qiskit.transpiler import PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.circuit.library import EfficientSU2
import numpy as np

circuit = QuantumCircuit(4)
for i in range(4):
    circuit.h(i)
for i in range(3):
    circuit.rxx(np.pi/4, i, i+1)
    circuit.rzz(np.pi/3, i, i+1)
    circuit.cx(i, i+1)

runtime_service = QiskitRuntimeService()

backend_name = "ibm_sherbrooke"
backend = runtime_service.get_backend(backend_name)

# Base Qiskit lvl3 transpiler
lvl3_plus_ai = generate_preset_pass_manager(optimization_level=3, backend=backend) # Here you include your backend object (ibm_sherbrooke)

# We replace the base routing with the AI-routing
lvl3_plus_ai.layout = None
lvl3_plus_ai.routing = PassManager([
    AIRouting(backend_name=backend_name, optimization_level=3, layout_mode="optimize") # AI Routing
])

# We include synthesis as a post-routing process
lvl3_plus_ai.post_routing = PassManager([
    CollectLinearFunctions(),  # Collect linear functions
    AILinearFunctionSynthesis(backend_name=backend_name)  # Optimize linear functions
])

ai_transpiled_circ = lvl3_plus_ai.run(circuit)

Handle HTTPErrors without a response in json format

We need to handle the general case where we have a HTTPError without a JSON coming in the response field.
In this function:

def _get_error_msg_from_response(exc: requests.exceptions.HTTPError):
resp = exc.response.json()
detail = resp.get("detail")
# Default message
msg = "Internal error."
if isinstance(detail, str):
msg = detail
elif isinstance(detail, list):
detail_input = detail[0]["input"]
detail_msg = detail[0]["msg"]
if detail_input and detail_msg:
msg = f"Wrong input '{detail_input}'. {detail_msg}"
return msg

The only case that I think we can be sure it comes with json info is the UNPROCESSABLE_ENTITY given the way they are created on the service.

Rename this repo to qiskit-ibm-transpiler

As an effort to standardize the naming of all Qiskit client repos that communicate with IBM Quantum services, we decided to rename this to qiskit-ibm-transpiler.

We need to ensure we maintain backward compatibility (e.g. import qiskit_transpiler_service would still work with a deprecation warning).

Expose to the user errors coming from the service

Right now if a transpilation fails in the service, the user does not have any info on why, even if it is something he can act on.
Once we have some required changes in the service, we should get that info from the response and show it to the user.

A clear example is when the circuit exceeds the number of gates allowed by the service. In. that case the user only receives a Service Error message.

Unable to turn off logging

When using the transpiler service, a lot of logs are printed, including the QASM representation of the input circuit, e.g.

INFO:qiskit_transpiler_service.transpiler_service:Requesting transpile to the service
INFO:qiskit_transpiler_service.ai.service_wrapper:Getting status of task a05e526f-61dd-469f-9738-fbdd9bbe001b ...
INFO:backoff:Backing off request_status(...) for 1.0s ({'state': 'PENDING', 'result': None})
...

Setting logging.getLogger("qiskit_transpiler_service").setLevel(logging.WARNING) had no effect.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.