Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b237fde
restoring results in emulator
lballerio Feb 2, 2026
0b0fe9f
fixing emulator for hamiltonian tomography
lballerio Feb 5, 2026
9cf64e7
checking right evolution
lballerio Feb 9, 2026
79c3eae
fixed emulator for multiple sweepers in INTEGRATION acquisition type …
lballerio Feb 11, 2026
0dc9a69
emulator fix for compatible results from hamiltonian tomography and p…
lballerio Feb 16, 2026
7476154
adding debug mode for qm controller - used for solving QM error (toge…
lballerio Feb 16, 2026
953fea8
adding classical crosstalk simulation in emulator backend
lballerio Mar 2, 2026
1c8ded3
adding crosstalk for cross rabi experiment
lballerio Mar 3, 2026
232b2b5
finishing adding crosstalk and cross resonance recalibration with cro…
lballerio Mar 4, 2026
070d867
added confusion matrix in emulator (to be improved); also moved trans…
lballerio Mar 9, 2026
5c1c32e
Adding first draft for emulator documentation
lballerio Mar 9, 2026
408bf0c
adding crosstalk for flux channels and refactor how to handle empty c…
lballerio Mar 10, 2026
4dc135b
updating fixed-frequency emulator calibiration
lballerio Mar 10, 2026
e119656
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 10, 2026
793bd52
refactoring crosstalk - now both flux and drive crosstalk are defined…
lballerio Mar 11, 2026
608e646
fixing bug in results.py (measurement position handling in function r…
lballerio Mar 11, 2026
d38d592
ireintroducing engine field
lballerio Mar 12, 2026
da12486
debugging of the emulator for passing all tests - additionally modifi…
lballerio Mar 13, 2026
6123f7b
bug fixing in emulator doc
lballerio Mar 16, 2026
0ca25af
adding logs for density matricesin the simulation
lballerio Mar 18, 2026
d2e61ea
fixing for tests
lballerio Jun 3, 2026
3054fa9
deleting useless comments or variables
lballerio Jun 3, 2026
f42c07a
adding confusion matrix for singleshot
lballerio Jul 9, 2026
4db8a0d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 9, 2026
dea8764
fixing bug in test
lballerio Jul 9, 2026
bc19399
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 9, 2026
6ab7f75
adding confusion matrix and improving matrix multiplication implement…
lballerio Jul 11, 2026
22c2ef3
Merge branch 'rebasing_emulator_fix_to_main' of https://github.com/qi…
lballerio Jul 11, 2026
cb2de37
restoring succeed tests, fixing bug in _singleshot_results and _cycli…
lballerio Jul 13, 2026
cdb2fcf
change crosstalk validation in emulator
Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/source/main-documentation/emulator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,11 @@ To accurately resolve qubit dynamics and capture all contributions from the time
At present, state collapse is not implemented in the Qibolab emulator. As a result, this tool is not suitable for simulating mid-circuit measurements, and should be restricted to circuits in which all measurements occur simultaneously at the end of the computation. In physical systems, mid-circuit measurement induces wavefunction collapse; if the measured qubit is entangled with others, this process introduces correlations that condition the state of the remaining system. The current emulator does not account for such measurement-induced correlations, leading to intrinsically inaccurate results in these scenarios.

An additional limitation arises from the handling of measurement ordering. In certain experimental protocols, the temporal order of measurements may vary across parameter sweeps, while the :paramref:`PulseSequence` object remains fixed and does not reflect such reordering. This discrepancy may introduce inconsistencies during the execution of :func:`qibolab._core.instruments.emulator.results.results`, which assumes alignment between the time-ordering structure and the acquisition pulses defined in the pulse sequence. If this alignment is violated, acquisition events may be incorrectly matched to simulation time steps, ultimately resulting in invalid outputs.


Additionally, Qibolab emulator implements a confusion matrix for simulating mislabeling of the qubits states. At the time being we are assuming an uncorrelated model, in which the total confusion matrix is simply the Kroneker product between single qubits confusion matrix, which indeed does not take into account correlations between qubits due to quantum effects.

This confusion matrix is applied directly to the states probabilities computed from the solution density matrix; then, if we call :math:`M_i` the matrix corresponding to the i-th qubit and :math:`P_{raw}` the output probability vector of the pde solver, we'll have that the 'corrected' probability vector :math:`P_{corr}` is:
.. math::

P_{corr} = \left( \bigotimes_i M_i \right) P_{raw}
2 changes: 1 addition & 1 deletion doc/source/tutorials/emulator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ the section ``configs``. Here is an example
"settings": {"nshots": 1024, "relaxation_time": 0},
"configs": {
"hamiltonian": {
"transmon_levels": 2,
"qubits": {
"0": {
"transmon_levels": 2,
"frequency": 5e9,
"sweetspot": 0.02,
"anharmonicity": -200e6,
Expand Down
76 changes: 53 additions & 23 deletions src/qibolab/_core/instruments/emulator/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@
from qibolab._core.components import Config
from qibolab._core.components.configs import AcquisitionConfig
from qibolab._core.execution_parameters import AveragingMode, ExecutionParameters
from qibolab._core.identifier import Result
from qibolab._core.identifier import ChannelId, Result
from qibolab._core.instruments.abstract import Controller
from qibolab._core.instruments.emulator.hamiltonians import (
DriveEmulatorConfig,
FluxEmulatorConfig,
)
from qibolab._core.pulses import (
Delay,
Pulse,
PulseId,
PulseLike,
VirtualZ,
)
Expand Down Expand Up @@ -146,7 +151,7 @@ def play(
sequences: list[PulseSequence],
options: ExecutionParameters,
sweepers: list[ParallelSweepers],
) -> dict[int, Result]:
) -> dict[PulseId, Result]:

if (
options.averaging_mode is AveragingMode.SINGLESHOT
Expand All @@ -170,7 +175,7 @@ def _play_sequence(
sequence: tuple[int, PulseSequence],
options: ExecutionParameters,
sweepers: list[ParallelSweepers],
):
) -> dict[PulseId, Result]:
"""
Generate results from an emulated quantum sequence execution.
Executes a sweep of the quantum sequence and processes the results
Expand Down Expand Up @@ -255,6 +260,11 @@ def _evolve(
collapse_operators=config.dissipation(self.engine),
time_hamiltonian=time_hamiltonian,
)
# we need to invert np.unique() call because otherwise there will be some mismatch
# between different sweeps of the same sequence;
# for example if we measure one qubit always at the same time but we sweep on time
# over the other, at some point the 2 measurement times might coincide so states.shape
# will be different
states = np.stack([s.full() for s in results.states[1:]])[index]

self._dump_simulation(
Expand Down Expand Up @@ -322,21 +332,39 @@ def hamiltonian(
pulses: Iterable[PulseLike],
config: Config,
hamiltonian: HamiltonianConfig,
hilbert_space_index: int,
channel: ChannelId,
engine: SimulationEngine,
sampling_rate: float,
) -> tuple[Operator, list[Modulated]]:
n = hamiltonian.transmon_levels
op = engine.expand(
op=config.operator(n=n, engine=engine),
targets=hilbert_space_index,
dims=hamiltonian.dims,
)

hilbert_space_index = index(channel, hamiltonian)
ham_qubit = hamiltonian.qubits[hilbert_space_index]

if isinstance(config, (DriveEmulatorConfig, FluxEmulatorConfig)):
op = sum(
engine.expand(
op=o,
dims=hamiltonian.dims,
targets=hamiltonian.hilbert_space_index(int(q)),
)
for (q, o) in config.operator(
hamiltonian=hamiltonian, channel=channel, engine=engine
)
)

else:
op = engine.expand(
op=config.operator(n=ham_qubit.transmon_levels, engine=engine),
dims=hamiltonian.dims,
targets=hilbert_space_index,
)

waveforms = (
waveform(pulse, config, hamiltonian.qubits[hilbert_space_index], sampling_rate)
waveform(pulse, config, ham_qubit, sampling_rate)
for pulse in pulses
if isinstance(pulse, (Pulse, Delay, VirtualZ))
)

return (op, [w for w in waveforms if w is not None])


Expand All @@ -347,19 +375,21 @@ def hamiltonians(
sampling_rate: float,
) -> Iterable[tuple[Operator, list[Modulated]]]:
hconfig = cast(HamiltonianConfig, configs["hamiltonian"])
return (
hamiltonian(
sequence.channel(ch),
configs[ch],
hconfig,
index(ch, hconfig),
engine,
sampling_rate,
)
for ch in sequence.channels

hamiltonians_array = ()
for ch in sequence.channels:
# TODO: drop the following, and treat acquisitions just as empty channels
if not isinstance(configs[ch], AcquisitionConfig)
)
if not isinstance(configs[ch], AcquisitionConfig):
new_terms = hamiltonian(
sequence.channel(ch),
configs[ch],
hconfig,
ch,
engine,
sampling_rate,
)
hamiltonians_array += (new_terms,)
return hamiltonians_array


def channel_coefficients(
Expand Down
Loading