Releases: qilimanjaro-tech/qililab
BSC-16
New features since last release
- Previously,
QProgram.set_offsetrequired both I and Q offsets (offset_path0andoffset_path1) to be of the same type (either both constants or both variables).
This restriction has been removed: it is now possible to mix constants and variables between I and Q.#1024qp = ql.QProgram() offset = qp.variable(label="offset", domain=ql.Domain.Voltage) with qp.for_loop(variable=offset, start=0, stop=1, step=0.1): qp.set_offset(bus="drive", offset_path0= offset, offset_path1=0.5) qp.set_offset(bus="drive", offset_path0=0.1, offset_path1=offset)
Improvements
-
Implemented a new driver for the Becker Nachrichtentechnik RSWU-SP16TR
#1020 -
For Autocalibration database, moved
sample_nameandcooldownfromAutocalMeasurement(independent experiment) toCalibrationRun(full calibration tree). This way the database does not include redundant information, as these variables do not change from one measurement to another, only in different calibration runs.
#1053 -
Added a
data_folderoption toget_db_managerto overwrite other base paths generated.
#1074
Bug fixes
- Fixed conflict with base path not being unique for parallel qprogram executions in StreamArray. To fix it, the path now includes the target index whenever there is one, which is the case for parallel executions. For any other existing case the behavior remains the same.
#1074
What's Changed
- Qhc 1132 driver for rswu sp16tr bsc by @elygoner in #1069
- [QHC-1279] Database measurement calibration table and multiple autocalibration qubits BSC branch by @jordivallsq in #1071
- [FIX] Measurement database folder processing for parallel qprogram with same folder path by @jordivallsq in #1074
Full Changelog: bsc-15...bsc-16
BSC-15
Improvements
- For Autocalibration database, moved
sample_nameandcooldownfromAutocalMeasurement(independent experiment) toCalibrationRun(full calibration tree). This way the database does not include redundant information, as these variables do not change from one measurement to another, only in different calibration runs.
#1053
Bug fixes
-
Calibration file: The
with_calibrationmethod inqprogramwas not storing the needed information to import blocks, this information is now being stored.
Theinsert_blockmethod instructured_qprogramhas been modified such that the block is flattened, hence each element is added separately, rather than adding the block. Adding the block directly was causing problems at compilation because adding twice in a singleqprogramthe same block meant they shared the same UUID.
#1050 -
QbloxDraw: Fixed two acquisition-related bugs:.
- Acquisition-only programs are now displayed correctly.
- Acquisition timing issues caused by wait instructions have been fixed.
#1051
-
QbloxDraw: Variable offsets can now be plotted.
#1049 -
Removed threading for
ExperimentExecutor(). This feature caused a deadlock on the execution if any error is raised inside it (mainly involving the ExperimentsResultsWriter). The threading has been removed as it was only necessary for parallel time tracking.
#1055
bsc-14
New features since last release
-
Hardware Loop over the time domain has been implemented for Qblox backend in
QProgram.
This allows sweeping wait times entirely in hardware, eliminating the need of software loops (which require uploading multiple Q1ASM).
The implementation leverages the different Q1ASM jump instructions to ensure correct execution of theQProgramsync operation. -
Variable expressions for time domain
Variable expressions are now supported inQProgramfor the time domain.
The supported formats are given in ns:constant + time variabletime variable + constantconstant - time variabletime variable - constant
Code example:
qp = QProgram()
duration = qp.variable(label="time", domain=Domain.Time)
with qp.for_loop(variable=duration, start=100, stop=200, step=10):
qp.wait(bus="drive", duration)
qp.sync()
qp.wait(bus="drive", duration - 50)
-
QbloxDraw: Replaced the fixed qualitative palette (10 colors) with the continuous "Turbo" colorscale. Previously, plotting more than 10 buses caused an index error due to the palette’s limited size. The new implementation samples the continuous colorscale at evenly spaced positions based on the number of buses.
#1039 -
Active reset for transmon qubits in QBlox
Implemented a feedback-based reset for QBlox: measure the qubit, and if it is in the
|1⟩state apply a corrective DRAG pulse; if it is already in|0⟩(ground state), do nothing. This replaces the relaxation time at the end of each experiment with a much faster, conditional reset.
This has been implemented as:qprogram.qblox.measure_reset(bus: str, waveform: IQPair, weights: IQPair, control_bus: str, reset_pulse: IQPair, trigger_address: int = 1, save_adc: bool = False)It is compiled by the QBlox compiler as:
latch_rst 4on the control_bus- play readout pulse
- acquire
- sync the readout and control buses
- wait 400 ns on the control bus (trigger-network propagation)
set_conditional(1, mask, 0, duration of the reset pulse)→ enable the conditional- Play the reset pulse on the control bus
set_conditional(0, 0, 0, 4)→ disable the conditional
For the control bus,latch_en 4is added to the top of the Q1ASM to enable trigger latching.
MeasureResetCalibratedhas been implemented to enable the use of active reset with a calibration file.
After retrieving the waveforms and weights from the calibration file, the measure reset can be called with:qprogram.qblox.measure_reset(bus='readout_bus', waveform='Measure', weights='Weights', control_bus='drive_bus', reset_pulse='Drag'). Unlike other methods, this one does not allow a mix of calibrated and non-calibrated parameters is not allowed. This method requires the calibration file to be used consistently acrosswaveform,weight, andreset_pulse; either for all three or for none. An error is raised if this condition is not met.
Notes- The 400 ns wait inside
measure_resetis the propagation delay of the Qblox trigger network. This figure is conservative as the official guideline is 388ns. - Users may supply any IQPair for the reset_pulse, though DRAG pulses are recommended to minimize leakage.
- After
measure_reset, users should insert a further wait as needed to allow the readout resonator to ring down before subsequent operations. - On compilation,
cluster.reset_trigger_monitor_count(address)is applied to zero the module’s trigger counter. And the qcodes parameters required to set up the trigger network are implemented by the QbloxQRM class. - The Qblox Draw class has been modified so that
latch_rstinstructions are interpreted as await, and allset_conditionalcommands are ignored.
#955
#1042
-
Introduced
electrical_delayas a new setting for the E5080b VNA driver. It is a pure software setting to be used in autoplotting and not a physical parameter of the device.
#1037 -
Introduced a Pydantic-powered
QililabSettingsthat centralizes runtime configuration, with the singletonget_settings()pulling values from multiple sources so teams can pick what fits their workflow. Settings still default to sensible values, but can be overridden directly in code by editing the fields (handy for tests or ad-hoc scripts), by exporting environment variables (for exampleQILILAB_EXPERIMENT_RESULTS_BASE_PATH=/data/qililab), or by dropping the same keys into a project-level.envfile that is auto-discovered and parsed.
#1025
Improvements
-
Upgraded requirement:
qpysequence>=0.10.8
#1044 -
Improved acquisition result handling in the QBlox Compiler.
Previously, each acquisition was assigned a unique acquisition index, which meant that a single qprogram could only contain up to 32 acquisitions per bus (due to QBlox’s limit of 32 indices).
Now, acquisitions at the same nested level reuse the same acquisition index while incrementing the bin index. This removes the 32-acquisition limit in most cases. AValueErroris raised only if more than 32 acquisitions occur at different nested levels.
Since the results retrieved from QBlox are now intertwined, a new function_unintertwined_qblox_resultshas been introduced inplatform. This function called by_execute_qblox_compilation_output methodandexecute_compilation_outputs_parallelseparates each acquisition into its own QbloxMeasurementResult object.
#998 -
Added support for real-time predistortion on Qblox hardware.
-
The outputs of a QCM can now set an FIR filter and up to four exponential filters (provided as a list). These parameters can be configured via the runcard (example below) and via platform.set_parameter/get_parameter.
-
The runcard has a new section under each QCM module:
filters: [...]configured by output. The section is optional. -
The states of a QCM filter are "enabled", "bypassed" and "delay_comp". Users can provide a boolean where True is mapped to "enabled" and False is mapped to "bypassed". When enabling a filter that could cause delay with other module outputs Qililab coerces the state to "delay_comp". This state ensures pulse timing remains consistent with filtered paths, keeping all outputs synchronized.
-
Parameters:
- Exponential Filters (given by exponential index)
- EXPONENTIAL_AMPLITUDE_0 ... EXPONENTIAL_AMPLITUDE_3
- EXPONENTIAL_TIME_CONSTANT_0 ... EXPONENTIAL_TIME_CONSTANT_3
- EXPONENTIAL_STATE_0 ... EXPONENTIAL_STATE_3
- FIR Filters:
- FIR_COEFF
- FIR_STATE
- Exponential Filters (given by exponential index)
-
Note: fir_coeff/FIR_COEFF must contain exactly 32 coefficients.
-
Below is an example of the filter part of the runcard:
filters: - output_id: 0 exponential_amplitude: [0.8, -1] exponential_time_constant: [6, 8] exponential_state: [True, True, False] fir_coeff: [0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8] fir_state: True - output_id: 1 exponential_amplitude: 0.31 exponential_time_constant: 9 exponential_state: False fir_coeff: [0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8] fir_state: FalseBelow is an example of set/get_parameter, the output_id must be provided:
platform.set_parameter(alias=bus_alias, parameter=Parameter.EXPONENTIAL_TIME_CONSTANT_0, value=300, output_id=0) platform.get_parameter(alias=bus_alias, parameter=Parameter.FIR_STATE, output_id=2) -
When setting/getting any parameter from the platform and giving the bus_alias, if an output_id or channel_id not associated with the bus is given, an exception is raised; and if an output_id instead of a channel_id (and vice versa) has been given an Exception is raised.
#981
-
-
This update introduces a new mechanism that allows the library to optionally import either full concrete implementations or lightweight stubs, depending on the user’s environment and installed dependencies. As part of this improvement, all Quantum Machines–related components have been reorganized under the
extra/quantum-machinesmodule hierarchy for better modularity and maintainability. Additionally, the Quantum Machines integration has been moved to the[project.optional-dependencies]section of the configuration, enabling users to install it only when needed.For example, to install the base library without any optional dependencies, run:
pip install qililab
or
uv sync
If you plan to use the Quantum Machines integration, you can include it during installation using the optional dependency group:
pip install qililab[quantum-machines]
or
uv sync --extra quantum-machines
-
Update qblox-instruments to 0.16.0 and qblox firmware to 0.11
#1015 -
This PR is the beginning of a series that will aim to reduce the length of the Q1ASM, which can be limiting for some experiments. This PR has two...
0.30.0
-
Removed all references to
Qibo. -
Removed the
pulsemodule. -
Restructured the
digitalmodule:- Introduced our own
CircuitTranspilerandCircuitToQProgramCompiler. CircuitTranspileris now responsible for decomposing circuits into the native gate set, handling logical and physical layouts, and performing optimizations.- It operates as a linear pipeline (
list[CircuitTranspilerPass]) designed to replace and improve upon the previous Router, Placer, and Optimizer that referenced Qibo implementations. - Each transpiler pass is required to have a concrete and well-defined purpose.
- Introduced our own
-
Implemented the following transpiler passes:
CancelIdentityPairsPassCircuitToCanonicalBasisPassFuseSingleQubitGatesPassCustomLayoutPassSabreLayoutPassSabreSwapPassCanonicalBasisToNativeSetPassAddPhasesToDragsFromRZAndCZPass
-
Refactored the IQ waveform class hierarchy:
- Added an abstract parent class
IQWaveform, from whichIQPairnow inherits. - Removed the
DRAGclass method fromIQPairand replaced it with a dedicatedIQDragclass. - This unification ensures all IQ waveforms are handled consistently and can be correctly serialized.
- Added an abstract parent class
-
Renamed
Draggate toRmwto align with standard literature and avoid confusion with pulse correction schemes (actual DRAG implemented inIQDrag).
0.29.3
Improvements
-
The tests for
QbloxDrawhave been modified such that the plots don't open on the user's browser when running pytests via VSCode.
#924 -
Github Actions now use
pytest-xdistplugin to run tests in parallel. To run tests in parallel locally useuv run pytest -n auto --dist loadfile. The--dist loadfileflag is mandatory to avoid conflicts between tests that edit shared data, and should be planned for removal in the future.
#925
Bug fixes
0.29.2
New features since last release
-
This update marks a significant transition in our workflow by adopting Astral's uv for dependency management, building, and publishing. With this change, our project now centralizes dependency definitions within a dedicated configuration file
pyproject.toml, ensuring consistent version locking and resolution across all environments. The legacy system for managing dependencies has been replaced with uv's streamlined approach, which simplifies the maintenance process and reduces potential inconsistencies.In addition, the build process has been completely overhauled. The old build scripts have been retired in favor of uv's build command, which compiles the source code, bundles assets, and prepares production-ready artifacts. This change not only standardizes the build process but also introduces enhanced logging and error handling, making it easier to diagnose any issues that arise during the build.
Publishing has also been improved with the integration of uv. The new process automates packaging and deployment, ensuring that artifacts are published in sync with the version specified in the configuration file. This automation minimizes manual intervention and helps maintain consistency in the release process.
Developers should install Astral's uv globally (for example, running
curl -LsSf https://astral.sh/uv/install.sh | sh). After installation, project management is handled through uv CLI. For additional details or troubleshooting, please refer to the official Astral's uv documentation at https://docs.astral.sh/uv/concepts/projects/. -
Implemented automatic mixers calibration for Qblox RF modules. There are to ways to use it. The first one is by setting a parameter in the runcard, which indicates when and which type of calibration is ran.
For the QRM-RF module the parameter is
out0_in0_lo_freq_cal_type_default, and the values it can take areoff,loandlo_and_sidebands. The first one is the default value, if set the instrument won't do any automatic mixers calibration on its own, to avoid unexpected behaviours by default. The second value will suppress the leakage in the LO, and the third one the LO plus the sidebands. The parameter that will trigger this autocalibration everytime is changed in the instrument isout0_in0_lo_freq.For the QCM-RF module the parameters in the runcard are
out0_lo_freq_cal_type_defaultandout1_lo_freq_cal_type_default, and the values are the same one that for the QRM-RF described above. The parameters that will trigger this autocalibration everytime is changed in the instrument areout0_lo_freqandout1_lo_freq.The second way to use this autocalibration is to trigger it manually using the
Platforminstance by calling its methodPlatform.calibrate_mixers(). As input parameters you will need to specify thealiasfor the bus where the RF instrument is, thecal_typewhich allows to specify one of the three values described above, and thechannel_idfor which mixer you would like to calibrate.channel_id = 0 cal_type = "lo" alias_drive_bus = "drive_line_q1_bus" platform.calibrate_mixers(alias=alias_drive_bus, cal_type=cal_type, channel_id=channel_id) channel_id = 0 cal_type = "lo_and_sidebands" alias_readout_bus = "readout_line_q1_bus" platform.calibrate_mixers(alias=alias_readout_bus, cal_type=cal_type, channel_id=channel_id)Warnings rise if a value that is not
off,loorlo_and_sidebandsare used.
#917 -
Implemented Crosstalk automatic implementation through the experiment class. The crosstalk can be added through the
Calibrationfile or by creating aCrosstalkMatrix. The crosstalk implementation inside theExperimentclass is:experiment = ql.Experiment(label="liveplot_test") flux_x = experiment.variable("flux_x", ql.Domain.Flux) flux_z = experiment.variable("flux_z", ql.Domain.Flux) experiment.set_crosstalk(crosstalk=crosstalk_matrix) # to see the values to be applied on the sample with experiment.for_loop(variable=flux_x, start=0, stop=0.4, step=0.01): with experiment.for_loop(variable=flux_z, start=0, stop=0.4, step=0.01): experiment.set_parameter(alias="flux_x1", parameter=ql.Parameter.FLUX, value=flux_x) experiment.set_parameter(alias="flux_z1", parameter=ql.Parameter.FLUX, value=flux_z) experiment.execute_qprogram(qp)Note that not giving a crosstalk matrix implies working with the identity. Warnings rise while creating the experiment to inform the user of this.
#899 -
Implemented crosstalk to the
platform.set_parameterfunction through the parameterParameter.FLUX. This flux parameter automatically applies the crosstalk calibration upon the implied fluxes and executes aset_parameterwithParameter.VOLTAGE,Parameter.CURRENTorParameter.OFFSETdepending on the instrument of the bus.
Two new functions have been implemented inside platform:add_crosstalk(), to add either theCrosstalkMatrixor theCalibrationfile andset_flux_to_zero(), to set all fluxes to 0 applying aset_parameter(bus, parameter.FLUX, 0)for all relevant busses
An example of this implementation would be:platform.add_crosstalk(crosstalk_matrix) platform.set_parameter("flux_ax_ac", ql.Parameter.FLUX, 0.1) platform.set_flux_to_zero() -
QBlox: An oscilloscope simulator has been implemented. It takes the sequencer as input, plots its waveforms and returns a dictionary (data_draw) containing all data points used for plotting.
The user can access the Qblox drawing feature in two ways:
-
Via platform (includes runcard knowledge)
with platform.session(): platform.draw(qprogram=qprogram)
Note that if it is used with a Quantum Machine runcard, a ValueError will be generated.
-
Via QProgram (includes runcard knowledge)
qp = QProgram() qprogram.draw()
Both methods compile the qprogram internally to generate the sequencer and call
QbloxDraw.draw(self, sequencer, runcard_data=None, averages_displayed=False) -> dict. #901 -
Improvements
-
Modified the
CrosstalkMatrixandFluxVectorclasses to fit for the crosstalk matrix implementation insideExperimentandPlatform. Now both classes update following the specifications and needs of experimentalists.
#899 -
Now the Rohde & Schwarz will return an error after a limit of frequency or power is reached based on the machine's version.
#897 -
QBlox: Added support for executing multiple QProgram instances in parallel via the new method
platform.execute_qprograms_parallel(qprograms: list[QProgram]). This method returns a list ofQProgramResultscorresponding to the input order of the provided qprograms. Note that an error will be raised if any submitted qprograms share a common bus.with platform.session(): results = platform.execute_qprograms_parallel([qprogram1, qprogram2, qprogram3])
Deprecations / Removals
- Remove the check of forcing GRES in slurm.
#907
Bug fixes
-
Correction of bugs following the implementation of the qblox drawing class. The user can now play the same waveform twice in one play, and when gains are set as 0 in the qprogram they are no longer replaced by 1 but remain at 0. Some improvements added, the RF modules are now scaled properly instead of on 1, when plotting through qprogram the y axis now reads Amplitude [a.u.], and the subplots have been removed, all the lines plot in one plot.
#918 -
D5a instrument now does not raise error when the value of the dac is higher or equal than 4, now it raises an error when is higher or equal than 16 (the number of dacs).
#908
0.29.0
New features since last release
-
We have introduced an optimization in the QbloxCompiler that significantly reduces memory usage when compiling square waveforms. The compiler now uses a heuristic algorithm that segments long waveforms into smaller chunks and loops over them. This optimization follows a two-pass search:
- First Pass: The compiler tries to find a chunk duration that divides the total waveform length evenly (i.e., remainder = 0).
- Second Pass: If no exact divisor is found, it looks for a chunk duration that leaves a remainder of at least 4 ns. This leftover chunk is large enough to be stored or handled separately.
Each chunk duration is restricted to the range ([100, 500]) ns, ensuring that chunks are neither too small (leading to excessive repetitions) nor too large (risking out-of-memory issues). If no duration within ([100, 500]) ns meets these remainder constraints, the compiler defaults to using the original waveform in its entirety.
#861
#895 -
Raises an error when the inputed value for the QDAC is outside of the bounds provided by QM. Done in 3 ways, runcard, set_parameter RAMPING_ENABLED and set_parameter RAMPING_RATE.
#865 -
Enable square waveforms optimization for Qblox.
#874 -
Implemented ALC, IQ wideband and a function to see the RS models inside the drivers for SGS100a.
#894
Improvements
-
Updated qm-qua to stable version 1.2.1. And close other machines has been set to True as now it closes only stated ports.
#854 -
Improvements to Digital Transpilation:
- Move
optimizeflag, for actual optional optimizations (& Improveoptimizeword use in methods names) - Make
Transpilation/execute/compileonly for single circuits (unify code standard acrossqililab) - Make
Transpilationefficient, by not constructing theCircuitclass so many times, between methods - Pass a transpilation
kwargsas a TypedDict instead of so many args inplatform/qililab'sexecute(...) - Improve documentation on transpilation, simplifying it in
execute()'s, and creating Transpilation new section.
- Move
-
Added optimizations for Digital Transpilation for Native gates:
- Make bunching of consecutive Drag Gates, with same phi's
- Make the deletion of gates with no amplitude
-
Improved the layout information display and Updated qibo version to the last version (0.2.15), which improves layout handling
#869 -
Now the QM qprogram compiler is able to generate the correct stream_processing while the average loop is inside any other kind of loop, before it was only able to be located on the outermost loop due to the way qprogram generated the stream_processing.
#880 -
The user is now able to only put one value when setting the offset of the bus when using Qblox in the qprogram. Qblox requires two values hence if only 1 value is given, the second will be set to 0, a warning will be given to the user.
#896 -
For Qblox compiler, all latched parameters are updated before a wait is applied. The update parameter has a minimum wait of 4 ns, which is removed from the wait. If the wait is below 8ns it is entirely replaced with the update parameter.
#898
Breaking changes
Deprecations / Removals
-
Removed weighted acquisitions for circuits.
#904 -
Removed quick fix for the timeout error while running with QM as it has been fixed.
#854
Documentation
Bug fixes
-
Addressed a known bug in the qblox where the first frequency and gain settings in a hardware loop are incorrect. The code now includes a workaround to set these parameters a second time to ensure proper functionality. This is a temporary fix awaiting for QBlox to resolve it.
#903 -
Fixed an issue where having nested loops would output wrong shape in QbloxMeasurementResult.
#853 -
Restore the vna driver as it was deleted.
#857 -
Fixed an issue where appending a configuration to an open QM instance left it hanging. The QM now properly closes before reopening with the updated configuration.
#851 -
Fixed an issue where turning off voltage/current source instruments would set to zero all dacs instead of only the ones specified in the runcard.
#819 -
Fixed the shareable trigger in the runcard to make every controller shareable while close other machines is set to false (current default) for QM. Improved shareable for OPX1000 as now it only requires to specify the flag on the fem. Now Octave name inside runcard requires to be the same as the one inside the configuration (now it has the same behavior as the cluster and opx controller).
#854 -
Ensured that turning on the instruments does not override the RF setting of the Rohde, which can be set to 'False' in the runcard.
#888
0.27.2
0.28.0
New features since last release
- Incorporated new check for maximum allowed attenuation in RF modules.
- Updated to latest qblox-instruments version. Changed some deprecated code from the new version and the QcmQrm into the more generic Module class.
- Added empty handlers for Blocks in QProgram compilers
- Support GRES in %%submit_job magic method
- Added intermediate frequency to single input lines on qm. The default is 0 (this prevents some bugs from qua-qm). Now it is possible to use the set_parameter IF and qm.set_frequency for buses with single_input.
-
A new
GetParameteroperation has been added to the Experiment class, accessible via the.get_parameter()method. This allows users to dynamically retrieve parameters during experiment execution, which is particularly useful when some variables do not have a value at the time of experiment definition but are provided later through external operations. The operation returns aVariablethat can be used seamlessly withinSetParameterandExecuteQProgram.Example:
experiment = Experiment() # Get a parameter's value amplitude = experiment.get_parameter(bus="drive_q0", parameter=Parameter.AMPLITUDE) # The returned value is of type `Variable`. It's value will be resolved during execution. # It can be used as usual in operations. # Use the variable in a SetOperation. experiment.set_parameter(bus="drive_q1", parameter=Parameter.AMPLITUDE, value=amplitude) # Use the variable in an ExecuteQProgram with the lambda syntax. def get_qprogram(amplitude: float, duration: int): square_wf = Square(amplitude=amplitude, duration=duration) qp = QProgram() qp.play(bus="readout", waveform=square_wf) return qp experiment.execute_qprogram(lambda: amplitude=amplitude: get_qprogram(amplitude, 2000)) -
Added offset set and get for quantum machines (both OPX+ and OPX1000). For hardware loops there is
qp.set_offset(bus: str, offset_path0: float, offset_path1: float | None)whereoffset_path0is a mandatory field (for flux, drive and readout lines) andoffset_path1is only used when changing the offset of buses that have to IQ lines (drive and readout). For software loops there isplatform.set_parameter(alias=bus_name, parameter=ql.Parameter.OFFSET_PARAMETER, value=offset_value). The possible arguments forql.Parameterare:DC_OFFSET(flux lines),OFFSET_I(I lines for IQ buses),OFFSET_Q(Q lines for IQ buses),OFFSET_OUT1(output 1 lines for readout lines),OFFSET_OUT2(output 2 lines for readout lines).
#791 -
Added the
Rampclass, which represents a waveform that linearly ramps between two amplitudes over a specified duration.from qililab import Ramp # Create a ramp waveform from amplitude 0.0 to 1.0 over a duration of 100 units ramp_wf = Ramp(from_amplitude=0.0, to_amplitude=1.0, duration=100)
-
Added the
Chainedclass, which represents a waveform composed of multiple waveforms chained together in time.from qililab import Ramp, Square, Chained # Create a chained waveform consisting of a ramp up, a square wave, and a ramp down chained_wf = Chained( waveforms=[ Ramp(from_amplitude=0.0, to_amplitude=1.0, duration=100), Square(amplitude=1.0, duration=200), Ramp(from_amplitude=1.0, to_amplitude=0.0, duration=100), ] )
-
Added
add_block()andget_block()methods to theCalibrationclass. These methods allow users to store a block of operations in a calibration file and later retrieve it. The block can be inserted into aQProgramorExperimentby callinginsert_block().from qililab import QProgram, Calibration # Create a QProgram and add operations qp = QProgram() # Add operations to qp... # Store the QProgram's body as a block in the calibration file calibration = Calibration() calibration.add_block(name="qp_as_block", block=qp.body) # Retrieve the block by its name calibrated_block = calibration.get_block(name="qp_as_block") # Insert the retrieved block into another QProgram another_qp = QProgram() another_qp.insert_block(block=calibrated_block)
-
Added routing algorithms to
qililabin function of the platform connectivity. This is done passingQiboownRoutersandPlacersclasses,
and can be called from different points of the stack.The most common way to route, will be automatically through
qililab.execute_circuit.execute(), or also fromqililab.platform.execute/compile(). Another way, would be doing the transpilation/routing directly from an instance of the Transpiler, with:qililab.digital.circuit_transpiler.transpile/route_circuit()(with this last one, you can route with a different topology from the platform one, if desired, defaults to platform)Example
from qibo import gates from qibo.models import Circuit from qibo.transpiler.placer import ReverseTraversal, Trivial from qibo.transpiler.router import Sabre from qililab import build_platform from qililab.circuit_transpiler import CircuitTranspiler # Create circuit: c = Circuit(5) c.add(gates.CNOT(1, 0)) ### From execute_circuit: # With defaults (ReverseTraversal placer and Sabre routing): probabilities = ql.execute(c, runcard="./runcards/galadriel.yml", placer= Trivial, router = Sabre, routing_iterations: int = 10,) # Changing the placer to Trivial, and changing the number of iterations: probabilities = ql.execute(c, runcard="./runcards/galadriel.yml", ### From the platform: # Create platform: platform = build_platform(runcard="<path_to_runcard>") # With defaults (ReverseTraversal placer, Sabre routing) probabilities = platform.execute(c, num_avg: 1000, repetition_duration: 1000) # With non-defaults, and specifying the router with kwargs: probabilities = platform.execute(c, num_avg: 1000, repetition_duration: 1000, placer= Trivial, router = (Sabre, {"lookahead": 2}), routing_iterations: int = 20)) # With a router instance: router = Sabre(connectivity=None, lookahead=1) # No connectivity needed, since it will be overwritten by the platform's one probabilities = platform.execute(c, num_avg: 1000, repetition_duration: 1000, placer=Trivial, router=router) ### Using the transpiler directly: ### (If using the routing from this points of the stack, you can route with a different topology from the platform one) # Create transpiler: transpiler = CircuitTranspiler(platform) # Default Transpilation (ReverseTraversal, Sabre and Platform connectivity): routed_circ, final_layouts = transpiler.route_circuit([c]) # With Non-Default Trivial placer, specifying the kwargs, for the router, and different coupling_map: routed_circ, final_layouts = transpiler.route_circuit([c], placer=Trivial, router=(Sabre, {"lookahead": 2}, coupling_map=<some_different_topology>)) # Or finally, Routing with a concrete Routing instance: router = Sabre(connectivity=None, lookahead=1) # No connectivity needed, since it will be overwritten by the specified in the Transpiler: routed_circ, final_layouts = transpiler.route_circuit([c], placer=Trivial, router=router, coupling_map=<connectivity_to_use>)
-
Added a timeout inside quantum machines to control the
wait_for_all_valuesfunction. The timeout is controlled through the runcard as shown in the example:instruments: - name: quantum_machines_cluster alias: QMM ... timeout: 10000 # optional timeout in seconds octaves: ...
-
Added
shareabletrigger inside runcard for quantum machines controller. The controller is defined in the runcard following this example:instruments: - name: con1 analog_outputs: - port: 1 offset: 0.0 shareable: True
Improvements
-
Legacy linting and formatting tools such as pylint, flake8, isort, bandit, and black have been removed. These have been replaced with Ruff, a more efficient tool that handles both linting and formatting. All configuration settings have been consolidated into the
pyproject.tomlfile, simplifying the project's configuration and maintenance. Integration config files likepre-commit-config.yamland.github/workflows/code_quality.ymlhave been updated accordingly. Several rules from Ruff have also been implemented to improve code consistency and quality across the codebase. Additionally, the development dependencies indev-requirements.txthave been updated to their latest versions, ensuring better compatibility and performance. #813 -
platform.execute_experiment()and the underlyingExperimentExecutorcan now handle experiments with multiple qprograms and multiple measurements. Parallel loops are also supported in both experiment and qprogram. The structure of the HDF5 results file as well as the functionality ofExperimentResultscl...
0.27.1
New features since last release
-
Introduced the possibility to run multiple shots and averages at the same time for
execute_anneal_programmethod.
#797 -
Introduced the
Experimentclass, which inherits fromStructuredProgram. This new class enables the ability to set parameters and execute quantum programs within a structured experiment. Added theset_parametermethod to allow setting platfform parameters andexecute_qprogrammethod to facilitate the execution of quantum programs within the experiment.
#782 -
Introduced the
ExperimentExecutorclass to manage and execute quantum experiments within the Qililab framework. This class provides a streamlined way to handle the setup, execution, and results retrieval of experiments.Temporary Constraints:
- The experiment must contain only one
QProgram. - The
QProgrammust contain a single measure operation. - Parallel loops are not supported.
#790
- The experiment must contain only one
-
Introduced the
platform.execute_experiment()method for executing experiments. This method simplifies the interaction with the ExperimentExecutor by allowing users to run experiments with a single call.Example:
# Define the QProgram qp = QProgram() gain = qp.variable(label='resonator gain', domain=Domain.Voltage) with qp.for_loop(gain, 0, 10, 1): qp.set_gain(bus="readout_bus", gain=gain) qp.measure(bus="readout_bus", waveform=IQPair(I=Square(1.0, 1000), Q=Square(1.0, 1000)), weights=IQPair(I=Square(1.0, 2000), Q=Square(1.0, 2000))) # Define the Experiment experiment = Experiment() bias_z = experiment.variable(label='bias_z voltage', domain=Domain.Voltage) frequency = experiment.variable(label='LO Frequency', domain=Domain.Frequency) experiment.set_parameter(alias="drive_q0", parameter=Parameter.VOLTAGE, value=0.5) experiment.set_parameter(alias="drive_q1", parameter=Parameter.VOLTAGE, value=0.5) experiment.set_parameter(alias="drive_q2", parameter=Parameter.VOLTAGE, value=0.5) with experiment.for_loop(bias_z, 0.0, 1.0, 0.1): experiment.set_parameter(alias="readout_bus", parameter=Parameter.VOLTAGE, value=bias_z) with experiment.for_loop(frequency, 2e9, 8e9, 1e9): experiment.set_parameter(alias="readout_bus", parameter=Parameter.LO_FREQUENCY, value=frequency) experiment.execute_qprogram(qp) # Execute the Experiment and display the progress bar. # Results will be streamed to an h5 file. The path of this file is returned from the method. path = platform.execute_experiment(experiment=experiment, results_path="/tmp/results/") # Load results results, loops = load_results(path)
-
Introduced a robust context manager
platform.session()for managing platform lifecycle operations. The manager automatically callsplatform.connect(),platform.initial_setup(), andplatform.turn_on_instruments()to set up the platform environment before experiment execution. It then ensures proper resource cleanup by invokingplatform.turn_off_instruments()andplatform.disconnect()after the experiment, even in the event of an error or exception during execution. If multiple exceptions occur during cleanup (e.g., failures in bothturn_off_instruments()anddisconnect()), they are aggregated into a singleExceptionGroup(Python 3.11+) or a custom exception for earlier Python versions.Example:
with platform.session(): # do stuff...
-
Add crosstalk compensation to
AnnealingProgramworkflow. Add methods toCrosstalkMatrixto ease crosstalk compensation in the annealing workflow
#775 -
Add default measurement to
execute_anneal_program()method. This method takes now a calibration file and parameters
to add the dispersive measurement at the end of the annealing schedule.
#778 -
Added a try/except clause when executing a QProgram on Quantum Machines cluster that controls the execution failing to perform a turning off of the instrument so the _qm object gets
removed. This, plus setting the close_other_machines=True by default allows to open more than one QuantumMachines VM at the same time to allow more than one experimentalist to work at the same time in the cluster.
#760 -
Added
__str__method to qprogram. The string is a readable qprogram.
#767 -
Added workflow for the execution of annealing programs.
Example:
import qililab as ql platform = ql.build_platform("examples/runcards/galadriel.yml") anneal_program_dict = [ {qubit_0": {"sigma_x" : 0, "sigma_y": 0, "sigma_z": 1, "phix":1, "phiz":1}, "qubit_1": {"sigma_x" : 0.1, "sigma_y": 0.1, "sigma_z": 0.1}, "coupler_0_1": {"sigma_x" : 1, "sigma_y": 0.2, "sigma_z": 0.2} }, {"qubit_0": {"sigma_x" : 0.1, "sigma_y": 0.1, "sigma_z": 1.1}, "qubit_1": {"sigma_x" : 0.2, "sigma_y": 0.2, "sigma_z": 0.2}, "coupler_0_1": {"sigma_x" : 0.9, "sigma_y": 0.1, "sigma_z": 0.1} }, {"qubit_0": {"sigma_x" : 0.3, "sigma_y": 0.3, "sigma_z": 0.7}, "qubit_1": {"sigma_x" : 0.5, "sigma_y": 0.2, "sigma_z": 0.01}, "coupler_0_1": {"sigma_x" : 0.5, "sigma_y": 0, "sigma_z": -1} } ] results = platform.execute_anneal_program(anneal_program_dict=anneal_program_dict, transpiler=lambda delta, epsilon: (delta, epsilon), averages=100_000)
Alternatively, each step of the workflow can be executed separately i.e. the following is equivalent to the above:
import qililab as ql platform = ql.build_platform("examples/runcards/galadriel.yml") anneal_program_dict = [...] # same as in the above example # intialize annealing program class anneal_program = ql.AnnealingProgram( platform=platform, anneal_program=anneal_program_dict ) # transpile ising to flux, now flux values can be accessed same as ising coeff values # eg. for phix qubit 0 at t=1ns anneal_program.anneal_program[1]["qubit_0"]["phix"] anneal_program.transpile(lambda delta, epsilon: (delta, epsilon)) # get a dictionary {control_flux: (bus, waveform) from the transpiled fluxes anneal_waveforms = anneal_program.get_waveforms() # from here on we can create a qprogram to execute the annealing schedule
-
Added
CrosstalkMatrixclass to represent and manipulate a crosstalk matrix, where each index corresponds to a bus. The class includes methods for initializing the matrix, getting and setting crosstalk values, and generating string representations of the matrix.Example:
# Create an empty crosstalk matrix crosstalk_matrix = CrosstalkMatrix() # Add crosstalk values, where the keys are in matrix shape [row][column] crosstalk_matrix["bus1"]["bus2"] = 0.9 crosstalk_matrix["bus2"]["bus1"] = 0.1 # Alternatively, create a matrix from a collection of buses. # All crosstalk values are initialized to 1.0 crosstalk_matrix = CrosstalkMatrix.from_buses({"bus1", "bus2", "bus3"}) # Get a formatted string representation of the matrix # bus1 bus2 bus3 # bus1 \ 1.0 1.0 # bus2 1.0 \ 1.0 # bus3 1.0 1.0 \ print(crosstalk_matrix)
-
Added the Qblox-specific
set_markers()method inQProgram. This method takes a 4-bit binary mask as input, where0means that the associated marker will be open (no signal) and1means that the associated marker will be closed (signal). The mapping between bit indexes and markers depends on the Qblox module that the compiledQProgramwill run on.Example:
qp = QProgram() qp.qblox.set_markers(bus='drive_q0', mask='0111')
-
Added
set_markers_override_enabled_by_portandset_markers_override_value_by_portmethods inQbloxModuleto set markers through QCoDeS, overriding Q1ASM values.
#747 -
Added
from_qprogrammethod to theCountsclass to compute the counts of quantum states obtained from aQProgram. TheCountsobject is designed to work for circuits that have only one measurement per bus at the end of the circuit execution. It is the user's responsibility to ensure that this method is used appropriately when it makes sense to compute the state counts for aQProgram. Note that probabilities can easily be obtained by calling theprobabilities()method. See an example below.Example:
from qililab.result.counts import Counts qp = QProgram() # Define instructions for QProgram # ... qp_results = platform.execute_qprogram(qp) # Platform previously defined counts_object = Counts.from_qprogram(qp_results) probs = counts_object.probabilities()
-
Added
threshold_rotationsargument tocompile()method inQProgram. This argument allows to use rotation angles on measurement instructions if not specified. Currently used to use the angle rotations specified on the runcard (if any) so the user does not have to explicitly pass it as argument to the measure instruction. Used for classification of results in Quantum Machines's modules. The following example shows how to specify this value on the runcard.
...