Skip to content

a local noisy simulator using characterization data #224

@splch

Description

@splch

What feature would you like to see added? Why is it valuable?

having a local noisy aer simulator that uses the characterization data to be accurate enough for testing

What is the expected behavior, in detail?

instead of using any local simulator, use an ionqsimulator("qpu.forte-1") or smth!

Can you help make this feature a reality? By yourself? If not, what support would you need?

from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import UnitaryGate
from qiskit.quantum_info import Operator
from qiskit_aer import AerSimulator
from qiskit_aer.noise import (
    NoiseModel,
    depolarizing_error,
    thermal_relaxation_error,
    ReadoutError,
)
from qiskit_ionq import IonQProvider

IONQ_LABELS = {"gpi", "gpi2", "ms", "zz"}
BACKEND_ID = "qpu.aria-1"


# 1. Fetch the latest characterization
def latest_characterization() -> dict:
    provider = IonQProvider()  # uses $IONQ_API_KEY
    backend = provider.get_backend(BACKEND_ID, gateset="native")
    char = backend.client.get_calibration_data(BACKEND_ID)[0]
    return char


# 2. Build a Qiskit‑Aer NoiseModel from the characterization data
def noise_model_from_char(char: dict) -> NoiseModel:
    t1, t2 = char["timing"]["t1"], char["timing"]["t2"]
    t1q, t2q = char["timing"]["1q"], char["timing"]["2q"]

    p1 = 1 - char["fidelity"]["1q"]["median"]
    p2 = 1 - char["fidelity"]["2q"]["median"]
    ps = 1 - char["fidelity"]["spam"]["median"]

    # elementary errors
    err1q = depolarizing_error(p1, 1).compose(thermal_relaxation_error(t1, t2, t1q))
    base_tr = thermal_relaxation_error(t1, t2, t2q)
    err2q = depolarizing_error(p2, 2).compose(base_tr.tensor(base_tr))
    err_ro = ReadoutError([[1 - ps, ps], [ps, 1 - ps]])

    noise = NoiseModel()
    noise.add_basis_gates(["unitary"])  # tell Aer we will send custom unitaries

    noise.add_all_qubit_quantum_error(err1q, ["gpi", "gpi2", "rz"])
    for q0, q1 in map(tuple, char["connectivity"]):
        noise.add_quantum_error(err2q, ["ms"], [q0, q1])
    noise.add_all_qubit_readout_error(err_ro)
    return noise


# 3. Convert IonQ native gates into Aer‑legal UnitaryGate ops
def ionq_to_unitary(circ: QuantumCircuit) -> QuantumCircuit:
    """Return a copy where every IonQ native gate is a UnitaryGate."""
    out = QuantumCircuit(*circ.qregs, *circ.cregs, name=circ.name)
    for inst, qargs, cargs in circ.data:
        if inst.name in IONQ_LABELS:
            mat = Operator(inst).data
            inst = UnitaryGate(mat, label=inst.name)
        out.append(inst, qargs, cargs)
    return out


# 4. Get a simulator with noise model and characterization
def get_simulator() -> tuple[AerSimulator, dict]:
    char = latest_characterization()
    noise = noise_model_from_char(char)
    sim = AerSimulator(noise_model=noise)
    return sim, char


# 5. Example usage
from qiskit import QuantumCircuit, transpile
from qiskit.visualization import plot_histogram

sim, char = get_simulator()
backend = IonQProvider().get_backend(BACKEND_ID, gateset="native")

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

t_native = transpile(qc, backend, optimization_level=3)
t_aer = ionq_to_unitary(t_native)

result = sim.run(t_aer, shots=1024).result()
counts = result.get_counts()
print("IonQ-Aria-1 noisy counts:", counts)
plot_histogram(counts)
Image

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions