Managing the knowledge base
At the heart of QruiseOS is the knowledge base: a comprehensive record of all your QPU data – past and present – so you can track performance trends, reproduce experiments, and maintain a single source of truth. Its role is to preserve:
- QPU configuration: an explicit record of device settings required to set up and run experiments (drive and readout frequencies, control-line bandwidths, etc.)
- gate definitions and calibrations: the logical operations you can perform (e.g. X\(\pi\), iSWAP, CZ, ...), together with their calibrated parameters (pulse durations, phases, etc.)
- experimental results: measured values such as resonance frequencies, \(\pi\)-pulse amplitudes, decay timescales, etc.
Note
To avoid implicit assumptions, these attributes are stored explicitly in the knowledge base so you or your colleagues can retrieve them at any point in the future.
Data modelling in the knowledge base¶
The knowledge base is designed to be very flexible in terms of what kind of data it contains – not all QPUs are made the same, and neither should your data. The ability to extend and customise what kind of information you want to store is crucial for development. On the other hand, this flexibility should not come at the expense of the ease of use.
Our solution is to define the data model as a collection of Python classes in the schema.py
file. With this approach, you can define arbitrary data types and relations between them. And all of that without the need for learning another domain-specific language! Moreover, the schema file can evolve with your QPU and workflows – you can extend it at any time to accommodate for new experiments, analyses, and properties you might want to keep track of.
You can choose between Qruise-provided schemas, e.g. for transmon QPUs, or write your own. You can read more about that in our Creating a schema user guide.
Retrieving current qubit data¶
Let's look at some typical qubit characterisation data stored in the knowledge base. First we need to create a session with create_session(load=True)
to load the existing records, and then check we're connected to the correct QPU and on the right branch.
from qruise.kb.session import create_session
# start session and load existing records from knowledge base
session = create_session(load=True)
# check QPU and branch
session.client.database, session.branch
We can retrieve the current attributes of a qubit Q1
by running the following:
Qubit(name='Q1', anhar=Quantity(std=254464.40386484083, unit='Hz', value=-327969913.1114249), anhar_coarse=Quantity(default=-230000000.0, range=Range(max_val=-70000000.0, min_val=-300000000.0), std=438376.2100868581, step=1000000.0, unit='Hz', value=-227486396.8502202, window=40000000.0), flux=Quantity(unit='V', value=0.0), flux_crosstalks={}, freq=Quantity(std=3662.132772416767, unit='Hz', value=4307918394.732011), freq_coarse=Quantity(event=PulsedSpectroscopy(_backend_id='PulsedSpectroscopy/clxlimvne0000vkqsyzd8hxww'), std=103114.5478234089, step=1000000.0, unit='Hz', value=3981005314.119921, window=80000000.0), index=0, lo_freq=Quantity(unit='Hz', value=4100000000.0), readout=ReadoutResonator(name='r1'), t1=Quantity(std=8.689329438925107e-07, step=2e-06, unit='s', value=2.4621991856170855e-05), t2star=Quantity(std=1.1273000325659814e-06, step=2e-06, unit='s', value=2.0436006805116355e-05), x180=GateCharacterization(amplitude=Quantity(event=AmplitudeRabi(_backend_id='AmplitudeRabi/ha6ii7udgyv1u4c77yia2dkp'), range=Range(max_val=0.8, min_val=0.05), std=0.0006713481234471507, step=0.01, unit='V', value=0.21384627866541256), drag=Quantity(std=0.014219276863106369, value=0.1365174934091704), duration=Quantity(unit='s', value=5e-08)), x180_amplitude_spectroscopy=Quantity(range=Range(max_val=0.8, min_val=0.05), std=0.0006713479362279471, step=0.01, unit='V', value=0.21384627869581443), x180_ef=GateCharacterization(amplitude=Quantity(std=0.000755780783562164, unit='V', value=0.15887324771995304), duration=Quantity(unit='s', value=5e-08), freq=Quantity(unit='Hz', value=3979948481.620586)), x90=GateCharacterization(amplitude=Quantity(range=Range(max_val=0.4, min_val=0.025), std=0.00013265883629368064, step=0.005, unit='V', value=0.10646839630742695), drag=Quantity(std=0.014219276863106369, value=0.1365174934091704), duration=Quantity(unit='S', value=5e-08)))
Retrieving historical qubit data¶
Every task executed is given a unique ID, allowing users to load it again later to inspect its configuration and results.
If we focus on the x180
attribute, we see there is an event registered alongside the amplitude
value within the Quantity
object.
GateCharacterization(amplitude=Quantity(event=AmplitudeRabi(_backend_id='AmplitudeRabi/ha6ii7udgyv1u4c77yia2dkp'), range=Range(max_val=0.8, min_val=0.05), std=0.0006713481234471507, step=0.01, unit='V', value=0.21384627866541256), drag=Quantity(std=0.014219276863106369, value=0.1365174934091704), duration=Quantity(unit='s', value=5e-08))
This allows you to trace the origin of the current value of the x180
amplitude
. In this case, it came from an AmplitudeRabi
task with the unique ID 'AmplitudeRabi/ha6ii7udgyv1u4c77yia2dkp'
. To load the amplitude
of x180
from this experiment, you can run the following:
# obtain experiment ID
experiment_id = qubit.x180.amplitude.event.get_id()
# load the experiment object
obj = session.load_document(experiment_id)
# access stored data
obj.data
The data is returned as an xarray.Dataset, as shown in the screen capture below.
If the data is not available, obj.data
will return None
.