Documentation

Get started with QObserva in minutes. Complete guides, examples, and API reference.

Quick Start

Get QObserva running in 3 steps:

1. Install QObserva

pip install qobserva

Or install from source:

git clone https://github.com/your-org/qobserva.git
cd qobserva
pip install -e packages/qobserva_agent
pip install -e packages/qobserva_collector
pip install -e packages/qobserva_local
pip install -e packages/qobserva

2. Start QObserva

qobserva up

This starts the collector and dashboard. Open http://localhost:3000 in your browser.

3. Instrument Your Code

from qobserva import observe_run

@observe_run(
    project="my_project",
    tags={"sdk": "qiskit", "algorithm": "bell_state"}
)
def my_quantum_algorithm():
    # Your quantum code here
    result = execute_circuit()
    return result
⚠️ Important: SDK Tag Required

Always include tags={"sdk": "..."} in your decorator for reliable adapter selection. Supported values: "qiskit", "braket", "cirq", "pennylane", "pyquil", "dwave".

Python Version Compatibility

Recommended: Python 3.12 (supports all 6 SDKs)

SDK Python Version Notes
Qiskit 3.10+ Works with Python 3.10-3.14
Braket 3.10 - 3.13 Python 3.14+ NOT supported (Braket SDK uses Pydantic v1)
Cirq 3.10+ Works with Python 3.10-3.14
PennyLane 3.10+ Works with Python 3.10-3.14
pyQuil 3.10 - 3.12 Python 3.13+ NOT supported (PyQuil 4.x uses PyO3 0.20.3)
D-Wave 3.10+ Works with Python 3.10-3.14

SDK Examples

QObserva works with all major Python quantum SDKs. Here are quick examples:

Qiskit

from qobserva import observe_run
from qiskit import QuantumCircuit
from qiskit.primitives import StatevectorSampler

@observe_run(
    project="qiskit_test",
    tags={"sdk": "qiskit", "algorithm": "bell_state"}
)
def run():
    qc = QuantumCircuit(2, 2)
    qc.h(0)
    qc.cx(0, 1)
    qc.measure([0, 1], [0, 1])
    sampler = StatevectorSampler()
    return sampler.run([qc], shots=1024).result()

Requirements: qiskit>=1.2.0

Amazon Braket

from qobserva import observe_run
from braket.circuits import Circuit
from braket.devices import LocalSimulator

@observe_run(
    project="braket_test",
    tags={"sdk": "braket", "algorithm": "bell_state"}
)
def run():
    bell = Circuit().h(0).cnot(0, 1)
    device = LocalSimulator()
    return device.run(bell, shots=1024).result()

Requirements: amazon-braket-sdk>=1.80.0, Python 3.13 or earlier

Cirq

from qobserva import observe_run
import cirq

@observe_run(
    project="cirq_test",
    tags={"sdk": "cirq", "algorithm": "bell_state"},
    measurement_key="result"  # Required!
)
def run():
    q0, q1 = cirq.LineQubit(0), cirq.LineQubit(1)
    circuit = cirq.Circuit()
    circuit.append(cirq.H(q0))
    circuit.append(cirq.CNOT(q0, q1))
    circuit.append(cirq.measure(q0, q1, key='result'))
    return cirq.Simulator().run(circuit, repetitions=1024)

Requirements: cirq>=1.3.0. Note: Must specify measurement_key parameter.

PennyLane

from qobserva import observe_run
import pennylane as qml

@observe_run(
    project="pennylane_test",
    tags={"sdk": "pennylane", "algorithm": "bell_state"}
)
def run():
    dev = qml.device("default.qubit", wires=2, shots=1024)
    @qml.qnode(dev)
    def circuit():
        qml.Hadamard(wires=0)
        qml.CNOT(wires=[0, 1])
        return qml.counts()
    return circuit()

Requirements: pennylane>=0.40.0

pyQuil

from qobserva import observe_run
from pyquil import Program, get_qc
from pyquil.gates import H, CNOT, MEASURE

@observe_run(
    project="pyquil_test",
    tags={"sdk": "pyquil", "algorithm": "bell_state"}
)
def run():
    program = Program()
    ro = program.declare("ro", "BIT", 2)
    program += H(0)
    program += CNOT(0, 1)
    program += MEASURE(0, ro[0])
    program += MEASURE(1, ro[1])
    qc = get_qc("2q-qvm")
    program.wrap_in_numshots_loop(1024)
    return qc.run(program)

Requirements: pyquil>=4.0.0, Python 3.12 or earlier, Rust/Cargo

D-Wave

from qobserva import observe_run
import dimod

@observe_run(
    project="dwave_test",
    tags={"sdk": "dwave", "algorithm": "qubo"}
)
def run():
    Q = {(0, 0): -1, (0, 1): 1, (1, 2): 1}
    bqm = dimod.BinaryQuadraticModel.from_qubo(Q)
    return dimod.ExactSolver().sample(bqm)

Requirements: dimod>=0.12.20

API Reference

@observe_run Decorator

@observe_run(
    project: str,                    # Project name (e.g., "my_project")
    tags: dict,                      # Tags dict (must include "sdk")
    benchmark_id: str = None,        # Optional benchmark identifier
    benchmark_params: dict = None,   # Optional benchmark parameters
    measurement_key: str = None     # Required for Cirq
)

Parameters

Parameter Type Required Description
project str Yes Project name for grouping runs (e.g., "qiskit_test")
tags dict Yes Tags dict. Must include "sdk" key. Recommended: "algorithm", "test"
benchmark_id str No Benchmark identifier for comparison
benchmark_params dict No Algorithm-specific metrics (e.g., energy values, success rates)
measurement_key str Cirq only Measurement key matching cirq.measure() key

Common Use Cases

Algorithm Tagging

Tag your runs with algorithm names to enable algorithm-specific dashboards:

tags={
    "sdk": "qiskit",
    "algorithm": "vqe",      # Enables algorithm comparison
    "test": "variational"
}

Common algorithm tags: "bell_state", "grover", "vqe", "qaoa", "qubo", "qft"

Benchmark Parameters

Include algorithm-specific metrics for better analysis:

@observe_run(
    project="vqe_experiment",
    tags={"sdk": "qiskit", "algorithm": "vqe"},
    benchmark_id="h2_ground_state",
    benchmark_params={
        "energy": -1.137,
        "convergence_iterations": 10
    }
)

Viewing Results

After running your instrumented code:

  1. Open http://localhost:3000 in your browser
  2. Filter by project name (e.g., qiskit_test)
  3. View run details, compare runs, and analyze trends

More Documentation

For comprehensive guides, examples, and troubleshooting:

View Full Documentation on GitHub →