Skip to content

Releases: qilimanjaro-tech/qililab

BSC-16

20 Feb 11:46
747cd9c

Choose a tag to compare

New features since last release

  • Previously, QProgram.set_offset required both I and Q offsets (offset_path0 and offset_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.
    qp = 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)
    
    #1024

Improvements

  • Implemented a new driver for the Becker Nachrichtentechnik RSWU-SP16TR
    #1020

  • For Autocalibration database, moved sample_name and cooldown from AutocalMeasurement (independent experiment) to CalibrationRun (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_folder option to get_db_manager to 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

19 Jan 11:04
79bbeae

Choose a tag to compare

Improvements

  • For Autocalibration database, moved sample_name and cooldown from AutocalMeasurement (independent experiment) to CalibrationRun (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_calibration method in qprogram was not storing the needed information to import blocks, this information is now being stored.
    The insert_block method in structured_qprogram has 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 single qprogram the same block meant they shared the same UUID.
    #1050

  • QbloxDraw: Fixed two acquisition-related bugs:.

    1. Acquisition-only programs are now displayed correctly.
    2. 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

08 Dec 09:36
6e9b1c7

Choose a tag to compare

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 the QProgram sync operation.

  • Variable expressions for time domain
    Variable expressions are now supported in QProgram for the time domain.
    The supported formats are given in ns:

    • constant + time variable
    • time variable + constant
    • constant - time variable
    • time 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)

#950

  • 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:

    1. latch_rst 4 on the control_bus
    2. play readout pulse
    3. acquire
    4. sync the readout and control buses
    5. wait 400 ns on the control bus (trigger-network propagation)
    6. set_conditional(1, mask, 0, duration of the reset pulse) → enable the conditional
    7. Play the reset pulse on the control bus
    8. set_conditional(0, 0, 0, 4) → disable the conditional
      For the control bus, latch_en 4 is added to the top of the Q1ASM to enable trigger latching.

    MeasureResetCalibrated has 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 across waveform, weight, and reset_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_reset is 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_rst instructions are interpreted as a wait, and all set_conditional commands are ignored.
      #955
      #1042
  • Introduced electrical_delay as 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 QililabSettings that centralizes runtime configuration, with the singleton get_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 example QILILAB_EXPERIMENT_RESULTS_BASE_PATH=/data/qililab), or by dropping the same keys into a project-level .env file 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. A ValueError is 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_results has been introduced in platform. This function called by _execute_qblox_compilation_output method and execute_compilation_outputs_parallel separates 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
    • 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: False
      

      Below 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-machines module 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

    #995

  • 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...

Read more

0.30.0

05 Dec 08:06
216e01b

Choose a tag to compare

⚠️ Provisional release notes for 0.30.0, serving as a bridge until the branch is merged into main. Content is subject to refinement.

  • Removed all references to Qibo.

  • Removed the pulse module.

  • Restructured the digital module:

    • Introduced our own CircuitTranspiler and CircuitToQProgramCompiler.
    • CircuitTranspiler is 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.
  • Implemented the following transpiler passes:

    • CancelIdentityPairsPass
    • CircuitToCanonicalBasisPass
    • FuseSingleQubitGatesPass
    • CustomLayoutPass
    • SabreLayoutPass
    • SabreSwapPass
    • CanonicalBasisToNativeSetPass
    • AddPhasesToDragsFromRZAndCZPass
  • Refactored the IQ waveform class hierarchy:

    • Added an abstract parent class IQWaveform, from which IQPair now inherits.
    • Removed the DRAG class method from IQPair and replaced it with a dedicated IQDrag class.
    • This unification ensures all IQ waveforms are handled consistently and can be correctly serialized.
  • Renamed Drag gate to Rmw to align with standard literature and avoid confusion with pulse correction schemes (actual DRAG implemented in IQDrag).

0.29.3

07 Apr 13:07
510a5e1

Choose a tag to compare

Improvements

  • The tests for QbloxDraw have 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-xdist plugin to run tests in parallel. To run tests in parallel locally use uv run pytest -n auto --dist loadfile. The --dist loadfile flag is mandatory to avoid conflicts between tests that edit shared data, and should be planned for removal in the future.
    #925

Bug fixes

  • For the Rohde & Schwarz SGS100A, a missing line in the driver prevented the settings from updating when set. This issue has been fixed.
    #927

  • Qblox-instruments version updated to the right one.
    #929

0.29.2

01 Apr 13:13
eca50d9

Choose a tag to compare

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/.

    #923

  • 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 are off, lo and lo_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 is out0_in0_lo_freq.

    For the QCM-RF module the parameters in the runcard are out0_lo_freq_cal_type_default and out1_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 are out0_lo_freq and out1_lo_freq.

    The second way to use this autocalibration is to trigger it manually using the Platform instance by calling its method Platform.calibrate_mixers(). As input parameters you will need to specify the alias for the bus where the RF instrument is, the cal_type which allows to specify one of the three values described above, and the channel_id for 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, lo or lo_and_sidebands are used.
    #917

  • Implemented Crosstalk automatic implementation through the experiment class. The crosstalk can be added through the Calibration file or by creating a CrosstalkMatrix. The crosstalk implementation inside the Experiment class 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_parameter function through the parameter Parameter.FLUX. This flux parameter automatically applies the crosstalk calibration upon the implied fluxes and executes a set_parameter with Parameter.VOLTAGE, Parameter.CURRENT or Parameter.OFFSET depending on the instrument of the bus.
    Two new functions have been implemented inside platform: add_crosstalk(), to add either the CrosstalkMatrix or the Calibration file and set_flux_to_zero(), to set all fluxes to 0 applying a set_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()
    

    #899

  • 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:

    1. 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.

    2. 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 CrosstalkMatrix and FluxVector classes to fit for the crosstalk matrix implementation inside Experiment and Platform. 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 of QProgramResults corresponding 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])

    #906

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

17 Mar 15:02
030200b

Choose a tag to compare

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:

    1. First Pass: The compiler tries to find a chunk duration that divides the total waveform length evenly (i.e., remainder = 0).
    2. 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 optimize flag, for actual optional optimizations (& Improve optimize word use in methods names)
    • Make Transpilation/execute/compile only for single circuits (unify code standard across qililab)
    • Make Transpilation efficient, by not constructing the Circuit class so many times, between methods
    • Pass a transpilation kwargs as a TypedDict instead of so many args in platform/qililab's execute(...)
    • Improve documentation on transpilation, simplifying it in execute()'s, and creating Transpilation new section.

    #862

  • 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

    #863

  • 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

17 Jan 14:15
24b13da

Choose a tag to compare

Improvements

  • Updated to qcodes_contrib_drivers 0.22.0 version.
    #875

0.28.0

09 Dec 13:43
13f5d59

Choose a tag to compare

New features since last release

  • Incorporated new check for maximum allowed attenuation in RF modules.

#843

  • Updated to latest qblox-instruments version. Changed some deprecated code from the new version and the QcmQrm into the more generic Module class.

#836

  • Added empty handlers for Blocks in QProgram compilers

#839

  • Support GRES in %%submit_job magic method

#828

  • 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.

#807

  • A new GetParameter operation 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 a Variable that can be used seamlessly within SetParameter and ExecuteQProgram.

    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))
    

    #814

  • 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) where offset_path0 is a mandatory field (for flux, drive and readout lines) and offset_path1 is only used when changing the offset of buses that have to IQ lines (drive and readout). For software loops there is platform.set_parameter(alias=bus_name, parameter=ql.Parameter.OFFSET_PARAMETER, value=offset_value). The possible arguments for ql.Parameter are: 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 Ramp class, 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)

    #816

  • Added the Chained class, 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),
        ]
    )

    #816

  • Added add_block() and get_block() methods to the Calibration class. These methods allow users to store a block of operations in a calibration file and later retrieve it. The block can be inserted into a QProgram or Experiment by calling insert_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)

    #816

  • Added routing algorithms to qililab in function of the platform connectivity. This is done passing Qibo own Routers and Placers classes,
    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 from qililab.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>)

#821

  • Added a timeout inside quantum machines to control the wait_for_all_values function. 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:
        ...

    #826

  • Added shareable trigger 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
    

#844

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.toml file, simplifying the project's configuration and maintenance. Integration config files like pre-commit-config.yaml and .github/workflows/code_quality.yml have been updated accordingly. Several rules from Ruff have also been implemented to improve code consistency and quality across the codebase. Additionally, the development dependencies in dev-requirements.txt have been updated to their latest versions, ensuring better compatibility and performance. #813

  • platform.execute_experiment() and the underlying ExperimentExecutor can 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 of ExperimentResults cl...

Read more

0.27.1

16 Sep 10:08
235f4ca

Choose a tag to compare

New features since last release

  • Introduced the possibility to run multiple shots and averages at the same time for execute_anneal_program method.
    #797

  • Introduced the Experiment class, which inherits from StructuredProgram. This new class enables the ability to set parameters and execute quantum programs within a structured experiment. Added the set_parameter method to allow setting platfform parameters and execute_qprogram method to facilitate the execution of quantum programs within the experiment.
    #782

  • Introduced the ExperimentExecutor class 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 QProgram must contain a single measure operation.
    • Parallel loops are not supported.
      #790
  • 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)

    #790

  • Introduced a robust context manager platform.session() for managing platform lifecycle operations. The manager automatically calls platform.connect(), platform.initial_setup(), and platform.turn_on_instruments() to set up the platform environment before experiment execution. It then ensures proper resource cleanup by invoking platform.turn_off_instruments() and platform.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 both turn_off_instruments() and disconnect()), they are aggregated into a single ExceptionGroup (Python 3.11+) or a custom exception for earlier Python versions.

    Example:

    with platform.session():
      # do stuff...

    #792

  • Add crosstalk compensation to AnnealingProgram workflow. Add methods to CrosstalkMatrix to 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

    #767

  • Added CrosstalkMatrix class 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 in QProgram. This method takes a 4-bit binary mask as input, where 0 means that the associated marker will be open (no signal) and 1 means that the associated marker will be closed (signal). The mapping between bit indexes and markers depends on the Qblox module that the compiled QProgram will run on.

    Example:

    qp = QProgram()
    qp.qblox.set_markers(bus='drive_q0', mask='0111')

    #747

  • Added set_markers_override_enabled_by_port and set_markers_override_value_by_port methods in QbloxModule to set markers through QCoDeS, overriding Q1ASM values.
    #747

  • Added from_qprogram method to the Counts class to compute the counts of quantum states obtained from a QProgram. The Counts object 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 a QProgram. Note that probabilities can easily be obtained by calling the probabilities() 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()

    #743

  • Added threshold_rotations argument to compile() method in QProgram. 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.
    ...

Read more