First, install Tranqu.
!pip install tranqu
Import libraries and initialize the Tranqu class.
from tranqu import Tranqu
tranqu = Tranqu()
Transpile using Tranqu¶
Let's transpile Qiskit circuits using Qiskit. (This is a trivial example)
First, we prepare a quantum circuit in Qiskit.
from qiskit import QuantumCircuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
Next, we will transpile using the transpile function of Tranqu.
We set the argument program to the previously created Qiskit QuantumCircuit as the input program.
Since the input program is written in Qiskit, we set the argument program_lib to "qiskit".
To use the Qiskit transpiler, we set the argument transpiler_lib to "qiskit".
The return value of the transpile function is an instance of the TranspileResult class, which contains the transpilation results.
result = tranqu.transpile(
program=circuit,
program_lib="qiskit",
transpiler_lib="qiskit",
)
By accessing the transpiled_program property of the TranspileResult class, you can view the quantum circuit after transpilation.
In this example, there is no change in the quantum circuit before and after transpilation.
Since the input program was a Qiskit quantum circuit, the transpilation result is also a Qiskit quantum circuit.
print(result.transpiled_program)
┌───┐ ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
By accessing the stats property of the TranspileResult class, you can view the statistical information before and after the transpilation.
print(result.stats)
{'before': {'n_qubits': 2, 'n_gates': 5, 'n_gates_1q': 3, 'n_gates_2q': 2, 'depth': 3}, 'after': {'n_qubits': 2, 'n_gates': 5, 'n_gates_1q': 3, 'n_gates_2q': 2, 'depth': 3}}
By accessing the virtual_physical_mapping property of the TranspileResult class, you can check how the virtual qubit (or bit) indices were mapped to physical qubit (or bit) indices during the transpilation.
The keys of qubit_mapping (or bit_mapping) represent the virtual indices, and the values represent the physical indices.
print(result.virtual_physical_mapping)
{'qubit_mapping': {0: 0, 1: 1}, 'bit_mapping': {0: 0, 1: 1}}
Set the transpiler options¶
Set the arguments for Qiskit's transpile function in dict format to the transpiler_options argument of Tranqu's transpile function.
This enables you to use the same arguments as those for Qiskit's transpile function.
In this example, since basis_gates is set, the quantum circuit after transpilation does not include any H gates.
options = {
"basis_gates": ["id", "sx", "x", "rz", "cx"]
}
result = tranqu.transpile(
program=circuit,
program_lib="qiskit",
transpiler_lib="qiskit",
transpiler_options=options,
)
print(result.transpiled_program)
global phase: π/4
┌─────────┐┌────┐┌─────────┐ ░ ┌─┐
q_0: ┤ Rz(π/2) ├┤ √X ├┤ Rz(π/2) ├──■───░─┤M├───
└─────────┘└────┘└─────────┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ────────────────────────────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/═════════════════════════════════════╩══╩═
0 1
By referring to stats, you can see that the number of gates has changed before and after transpilation.
print(result.stats)
{'before': {'n_qubits': 2, 'n_gates': 5, 'n_gates_1q': 3, 'n_gates_2q': 2, 'depth': 3}, 'after': {'n_qubits': 2, 'n_gates': 7, 'n_gates_1q': 5, 'n_gates_2q': 2, 'depth': 5}}
Transpile OpenQASM3 program using Qiskit transpiler.¶
Even if the input program and the transpiler are from different libraries, you can perform transpilation using Tranqu.
Here, we input a program in OpenQASM 3 format and transpile it using Qiskit.
Since the input is in OpenQASM 3, set the argument program_lib of the transpile function to "openqasm3".
program = """OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
h q[0];
cx q[0], q[1];
"""
result = tranqu.transpile(
program=program,
program_lib="openqasm3",
transpiler_lib="qiskit",
transpiler_options=options,
)
print(result.transpiled_program)
OPENQASM 3.0; include "stdgates.inc"; qubit[2] q; rz(pi/2) q[0]; sx q[0]; rz(pi/2) q[0]; cx q[0], q[1];
Transpile to be compatible with Qiskit's backend¶
You can transpile to be compatible with Qiskit's backend.
FakeManilaV2 is a 5-qubit backend.
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
backend = FakeManilaV2()
Set the argument device to Qiskit's Backend, and set the argument device_lib to "qiskit".
result = tranqu.transpile(
program=circuit,
program_lib="qiskit",
transpiler_lib="qiskit",
transpiler_options=options,
device=backend,
device_lib="qiskit",
)
print(result.transpiled_program)
global phase: π/4
┌─────────┐┌────┐┌─────────┐ ░ ┌─┐
q_0 -> 0 ┤ Rz(π/2) ├┤ √X ├┤ Rz(π/2) ├──■───░─┤M├───
└─────────┘└────┘└─────────┘┌─┴─┐ ░ └╥┘┌─┐
q_1 -> 1 ────────────────────────────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
ancilla_0 -> 2 ─────────────────────────────────────╫──╫─
║ ║
ancilla_1 -> 3 ─────────────────────────────────────╫──╫─
║ ║
ancilla_2 -> 4 ─────────────────────────────────────╫──╫─
║ ║
meas: 2/═════════════════════════════════════╩══╩═
0 1
oqtopus_device = {
"name": "fake_device",
"qubits": [
{
"id": 0,
"fidelity": 0.90,
"meas_error": {
"prob_meas1_prep0": 0.01,
"prob_meas0_prep1": 0.02,
},
"gate_duration": {"x": 60.0, "sx": 30.0, "rz": 0},
},
{
"id": 1,
"meas_error": {
"prob_meas1_prep0": 0.01,
"prob_meas0_prep1": 0.02,
},
"gate_duration": {"x": 60.0, "sx": 30.0, "rz": 0},
},
{
"id": 2,
"fidelity": 0.99,
"gate_duration": {"x": 60.0, "sx": 30.0, "rz": 0},
},
{
"id": 3,
"fidelity": 0.99,
"meas_error": {
"prob_meas1_prep0": 0.01,
"prob_meas0_prep1": 0.02,
},
},
],
"couplings": [
{
"control": 0,
"target": 2,
"fidelity": 0.8,
"gate_duration": {"cx": 60.0},
},
{"control": 0, "target": 1, "fidelity": 0.8},
{"control": 1, "target": 0, "fidelity": 0.25},
{"control": 1, "target": 3, "fidelity": 0.25},
{"control": 2, "target": 0, "fidelity": 0.25},
{"control": 2, "target": 3, "fidelity": 0.25},
{"control": 3, "target": 1, "fidelity": 0.9},
{"control": 3, "target": 2, "fidelity": 0.9},
],
"timestamp": "2024-10-31 14:03:48.568126",
}
Set the argument device to OQTOPUS device, and set the argument device_lib to "oqtopus".
result = tranqu.transpile(
program=circuit,
program_lib="qiskit",
transpiler_lib="qiskit",
transpiler_options=options,
device=oqtopus_device,
device_lib="oqtopus",
)
print(result.transpiled_program)
global phase: π/4
┌─────────┐┌────┐┌─────────┐ ░ ┌─┐
q_0 -> 0 ┤ Rz(π/2) ├┤ √X ├┤ Rz(π/2) ├──■───░─┤M├───
└─────────┘└────┘└─────────┘┌─┴─┐ ░ └╥┘┌─┐
q_1 -> 1 ────────────────────────────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
ancilla_0 -> 2 ─────────────────────────────────────╫──╫─
║ ║
ancilla_1 -> 3 ─────────────────────────────────────╫──╫─
║ ║
meas: 2/═════════════════════════════════════╩══╩═
0 1
If you set the optimization_level of the Qiskit transpiler to 2, it will be transpiled using fidelity.
options_level2 = {
"basis_gates": ["id", "sx", "x", "rz", "cx"],
"optimization_level": 2,
}
result = tranqu.transpile(
program=circuit,
program_lib="qiskit",
transpiler_lib="qiskit",
transpiler_options=options_level2,
device=oqtopus_device,
device_lib="oqtopus",
)
print(result.transpiled_program)
global phase: π/4
ancilla_0 -> 0 ──────────────────────────────────────────
┌───┐ ░ ┌─┐
q_1 -> 1 ────────────────────────────┤ X ├─░────┤M├
└─┬─┘ ░ └╥┘
ancilla_1 -> 2 ──────────────────────────────┼─────────╫─
┌─────────┐┌────┐┌─────────┐ │ ░ ┌─┐ ║
q_0 -> 3 ┤ Rz(π/2) ├┤ √X ├┤ Rz(π/2) ├──■───░─┤M├─╫─
└─────────┘└────┘└─────────┘ ░ └╥┘ ║
meas: 2/═════════════════════════════════════╩══╩═
0 1
Let's check the virtual_physical_mapping property.
print(result.virtual_physical_mapping)
{'qubit_mapping': {0: 3, 1: 1}, 'bit_mapping': {0: 0, 1: 1}}