Skip to main contentIBM Quantum Documentation Preview
This is a preview build of IBM Quantum® documentation. Refer to quantum.cloud.ibm.com/docs for the official documentation.

Get started with Orbit

See the API reference

Note

Qiskit Functions are an experimental feature available only to IBM Quantum® Premium Plan, Flex Plan, and On-Prem (via IBM Quantum Platform API) Plan users. They are in preview release status and subject to change.

For an overview of this function, see the Orbit documentation home.


Set up

Authenticate using your IBM Quantum Platform API key, and select the Orbit function as follows. This snippet assumes you've already saved your account to your local environment.

from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit import QuantumCircuit

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Verify that you have access to the function.
catalog.list()

Output:

[QiskitFunction(quantum-elements/orbit-dev),
 QiskitFunction(quantum-elements/orbit)]
# Load the function.
orbit = catalog.load("quantum-elements/orbit")
print("Orbit version:", orbit.version)

Output:

Orbit version: 0.3.23

Example

The following example builds a Bernstein-Vazirani circuit and submits it to Orbit two ways in one job: raw execution and Orbit defaults.

Use this pattern when you want a direct raw-versus-Orbit comparison.

Scaling Bernstein-Vazirani to larger sizes

Orbit can help preserve Bernstein-Vazirani success probability as the hidden-bitstring width grows. The representative scaling runs below compare raw execution with Orbit configurations on IBM Quantum hardware. On ibm_miami, raw success probability falls below 10% by a hidden-bitstring width of 9 (10 total qubits), while Orbit remains near 80–90%. On ibm_kingston, Orbit maintains non-trivial success probability at widths approaching 60, including approximately 15% at the largest size shown. Exact results depend on the backend calibration, circuit, and execution settings.

Bernstein-Vazirani success probability on ibm_miami: Orbit with and without measurement error mitigation maintains higher success probability than raw execution as hidden-bitstring width increases. Bernstein-Vazirani success probability on ibm_kingston: box plots compare raw execution with Orbit across increasing hidden-bitstring widths, with Orbit results shown through width 60.

To preserve QPU time, this tutorial does not rerun a size sweep. Instead, the executable example below reports shot counts for one fixed hidden-bitstring width, N = 50 (51 total qubits in this circuit), comparing raw execution with Orbit defaults in a single job.

Build a Bernstein-Vazirani circuit

Choose the hidden bitstring and construct the logical circuit. The extra qubit is the oracle target, while the input qubits are measured.

def create_bv_circuit(hidden_bitstring: str) -> QuantumCircuit:
    width = len(hidden_bitstring)
    circuit = QuantumCircuit(width + 1, width)

    circuit.x(width)
    circuit.h(range(width + 1))

    for input_qubit, bit in enumerate(reversed(hidden_bitstring)):
        if bit == "1":
            circuit.cx(input_qubit, width)

    circuit.barrier()
    circuit.h(range(width + 1))
    circuit.measure(range(width), range(width))
    return circuit


circuit_width = 50
hidden_bitstring = "1" * circuit_width
shots = 4096

bv_circuit = create_bv_circuit(hidden_bitstring)
bv_circuit.count_ops()

Output:

OrderedDict([('h', 102),
             ('cx', 50),
             ('measure', 50),
             ('x', 1),
             ('barrier', 1)])

Prepare the Orbit workload

Create one Sampler PUB, then duplicate it for the two comparison modes. Orbit receives the logical circuit and maps it to the selected backend during execution.

base_pub = (bv_circuit, None, shots)
comparison_labels = ["raw", "orbit"]
pubs = [base_pub] * len(comparison_labels)

Run raw and Orbit defaults

pub_options assigns the first PUB to raw mode, which skips Orbit optimization, DD, and MEM, and assigns the second PUB to Orbit defaults, which include optimized transpilation, automated dynamical decoupling (DD) insertion, and measurement error mitigation (MEM).

backend_name = "ibm_kingston"

job = orbit.run(
    primitive="sampler",
    pubs=pubs,
    backend_name=backend_name,
    options={
        "pub_options": [
            {"mode": "raw"},
            {"mode": "orbit"},
        ],
        "save_backend_info": True,
    },
)

# Check the job ID and status
print(job.job_id)
print(job.status())

Output:

2c427a80-4190-404c-b98a-6bbeb53fafcd
QUEUED

Retrieve the result and compare the raw and Orbit-enhanced circuits using counts. For Sampler results, get_counts() on the returned classical register provides the output histogram. When MEM succeeds for the Orbit PUB, this histogram is already MEM-corrected; the unmitigated counts remain available in the Orbit metadata under measurementErrorMitigation["rawCounts"].

def extract_counts(pub_result) -> dict[str, int]:
    data = getattr(pub_result, "data", None)
    if data is None:
        raise TypeError("pub_result.data is missing")

    for name in dir(data):
        if name.startswith("_"):
            continue
        register = getattr(data, name)
        get_counts = getattr(register, "get_counts", None)
        if callable(get_counts):
            counts = get_counts()
            if counts:
                return counts

    raise TypeError(
        "No classical register with get_counts() found in pub_result.data"
    )


result = job.result()
if len(result) != len(comparison_labels):
    raise RuntimeError(
        f"Expected {len(comparison_labels)} PUB results, received {len(result)}."
    )

comparison_counts = {
    label: extract_counts(pub_result)
    for label, pub_result in zip(comparison_labels, result, strict=True)
}
correct_counts = {
    label: counts.get(hidden_bitstring, 0)
    for label, counts in comparison_counts.items()
}

print(
    {
        "hidden_bitstring": hidden_bitstring,
        "correct_counts": correct_counts,
        "shots": shots,
    }
)

Output:

{'hidden_bitstring': '11111111111111111111111111111111111111111111111111', 'correct_counts': {'raw': 0, 'orbit': 370}, 'shots': 4096}

Make a visual comparison of the raw and Orbit-enhanced circuits.

import matplotlib.pyplot as plt

labels = list(correct_counts)
values = list(correct_counts.values())
fig, ax = plt.subplots(figsize=(7, 4))
bars = ax.bar(labels, values, width=0.55)
ax.bar_label(bars, labels=[f"{value:g}" for value in values], padding=3)
ax.set_xlabel("Circuit execution mode")
ax.set_ylabel("Correct hidden-bitstring count")
ax.set_title(f"{circuit_width}-qubit Bernstein-Vazirani on {backend_name}")
ax.set_ylim(0, max(1, 1.15 * max(values)))
ax.grid(axis="y", alpha=0.25)
fig.tight_layout()
plt.show()

Output:

Output of the previous code cell