Releases: qilimanjaro-tech/qililab
0.27.1
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 fromStructuredProgram
. This new class enables the ability to set parameters and execute quantum programs within a structured experiment. Added theset_parameter
method to allow setting platfform parameters andexecute_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
- 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
AnnealingProgram
workflow. Add methods toCrosstalkMatrix
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
-
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 inQProgram
. This method takes a 4-bit binary mask as input, where0
means that the associated marker will be open (no signal) and1
means that the associated marker will be closed (signal). The mapping between bit indexes and markers depends on the Qblox module that the compiledQProgram
will run on.Example:
qp = QProgram() qp.qblox.set_markers(bus='drive_q0', mask='0111')
-
Added
set_markers_override_enabled_by_port
andset_markers_override_value_by_port
methods inQbloxModule
to set markers through QCoDeS, overriding Q1ASM values.
#747 -
Added
from_qprogram
method to theCounts
class to compute the counts of quantum states obtained from aQProgram
. TheCounts
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 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_rotations
argument 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.
...
0.27.0
New features since last release
-
Added
Calibration
class to manage calibrated waveforms and weights for QProgram. Included methods to add (add_waveform
/add_weights
), check (has_waveform
/has_weights
), retrieve (get_waveform
/get_weights
), save (save_to
), and load (load_from
) calibration data.Example:
# Create a Calibration instance calibration = Calibration() # Define waveforms and weights drag_wf = IQPair.DRAG(amplitude=1.0, duration=40, num_sigmas=4.5, drag_coefficient=-2.5) readout_wf = ql.IQPair(I=ql.Square(amplitude=1.0, duration=200), Q=ql.Square(amplitude=0.0, duration=200)) weights = ql.IQPair(I=ql.Square(amplitude=1.0, duration=200), Q=ql.Square(amplitude=1.0, duration=200)) # Add waveforms to the calibration calibration.add_waveform(bus='drive_q0_bus', name='Xpi', waveform=drag_wf) calibration.add_waveform(bus='readout_q0_bus', name='Measure', waveform=readout_wf) # Add weights to the calibration calibration.add_weights(bus='readout_q0_bus', name='optimal_weights', weights=weights) # Save the calibration data to a file calibration.save_to('calibration_data.yml') # Load the calibration data from a file loaded_calibration = Calibration.load_from('calibration_data.yml')
The contents of
calibration_data.yml
will be:!Calibration waveforms: drive_q0_bus: Xpi: !IQPair I: &id001 !Gaussian {amplitude: 1.0, duration: 40, num_sigmas: 4.5} Q: !DragCorrection drag_coefficient: -2.5 waveform: *id001 readout_q0_bus: Measure: !IQPair I: !Square {amplitude: 1.0, duration: 200} Q: !Square {amplitude: 0.0, duration: 200} weights: readout_q0_bus: optimal_weights: !IQPair I: !Square {amplitude: 1.0, duration: 200} Q: !Square {amplitude: 1.0, duration: 200}
Calibrated waveforms and weights can be used in QProgram by providing their name.
qp = QProgram() qp.play(bus='drive_q0_bus', waveform='Xpi') qp.measure(bus='readout_q0_bus', waveform='Measure', weights='optimal_weights')
In that case, a
Calibration
instance must be provided when executing the QProgram. (see following changelog entries) -
Introduced
qililab.yaml
namespace that exports a singleYAML
instance for common use throughout qililab. Classes should be registered to this instance with the@yaml.register_class
decorator.from qililab.yaml import yaml @yaml.register_class class MyClass: ...
MyClass
can now be saved to and loaded from a yaml file.from qililab.yaml import yaml my_instance = MyClass() # Save to file with open(file="my_file.yml", mode="w", encoding="utf-8") as stream: yaml.dump(data=my_instance, stream=stream) # Load from file with open(file="my_file.yml", mode="r", encoding="utf8") as stream: loaded_instance = yaml.load(stream)
-
Added
serialize()
,serialize_to()
,deserialize()
,deserialize_from()
functions to enable a unified method for serializing and deserializing Qililab classes to and from YAML memory strings and files.import qililab as ql qp = QProgram() # Serialize QProgram to a memory string and deserialize from it. yaml_string = ql.serialize(qp) deserialized_qprogram = ql.deserialize(yaml_string) # Specify the class for deserialization using the `cls` parameter. deserialized_qprogram = ql.deserialize(yaml_string, cls=ql.QProgram) # Serialize to and deserialize from a file. ql.serialize_to(qp, 'qprogram.yml') deserialized_qprogram = ql.deserialize_from('qprogram.yml', cls=ql.QProgram)
-
Added Qblox support for QProgram's
measure
operation. The method can now be used for both Qblox
and Quantum Machines, and the expected behaviour is the same.readout_pair = IQPair(I=Square(amplitude=1.0, duration=1000), Q=Square(amplitude=0.0, duration=1000)) weights_pair = IQPair(I=Square(amplitude=1.0, duration=2000), Q=Square(amplitude=0.0, duration=2000)) qp = QProgram() # The measure operation has the same behaviour in both vendors. # Time of flight between readout pulse and beginning of acquisition is retrieved from the instrument's settings. qp.measure(bus="readout_bus", waveform=readout_pair, weights=weights_pair, save_adc=True)
-
Update Qibo version to
v.0.2.8
.
#732
Improvements
-
Introduced
QProgram.with_bus_mapping
method to remap buses within the QProgram.Example:
# Define the bus mapping bus_mapping = {"drive": "drive_q0"} # Apply the bus mapping to a QProgram instance mapped_qprogram = qprogram.with_bus_mapping(bus_mapping=bus_mapping)
-
Introduced
QProgram.with_calibration
method to apply calibrated waveforms and weights to the QProgram.Example:
# Load the calibration data from a file calibration = Calibration.load_from('calibration_data.yml') # Apply the calibration to a QProgram instance calibrated_qprogram = qprogram.with_calibration(calibration=calibration)
-
Extended
Platform.execute_qprogram
method to accept a calibration instance.# Load the calibration data from a file calibration = Calibration.load_from('calibration_data.yml') platform.execute_qprogram(qprogram=qprogram, calibration=calibration)
-
Added interfaces for Qblox and Quantum Machines to QProgram. The interfaces contain vendor-specific methods and parameters. They can be accessed by
qprogram.qblox
andqprogram.quantum_machines
properties. -
Added
time_of_flight
setting to Qblox QRM and QRM-RF sequencers.
Breaking changes
-
QProgram interface now contains methods and parameters that have common functionality for all hardware vendors. Vendor-specific methods and parameters have been move to their respective interface.
Examples:
# Acquire method has been moved to Qblox interface. Instead of running # qp.acquire(bus="readout_q0_bus", weights=weights) # you should run qp.qblox.acquire(bus="readout_q0_bus", weights=weights) # Play method with `wait_time` parameter has been moved to Qblox interface. Instead of running # qp.play(bus="readout_q0_bus", waveform=waveform, wait_time=40) # you should run qp.qblox.play(bus="readout_q0_bus", waveform=waveform, wait_time=40) # `disable_autosync` parameter has been moved to Qblox interface. Instead of running # qp = QProgram(disable_autosync=True) # you should run qp = QProgram() qp.qblox.disable_autosync = True # Measure method with parameters `rotation` and `demodulation` has been moved to Quantum Machines interface. Instead of running # qp.measure(bus="readout_q0_bus", waveform=waveform, weights=weights, save_adc=True, rotation=np.pi, demodulation=True) # you should run qp.quantum_machines.measure(bus="readout_q0_bus", waveform=waveform, weights=weights, save_adc=True, rotation=np.pi, demodulation=True)
-
time_of_flight
parameter must be added to Qblox QRM and QRM-RF sequencers's runcard settings.
Deprecations / Removals
0.26.2
0.26.1
0.25.1
0.25.0
New features since last release
-
Add FlatTop pulse shape
#680 -
Add FlatTop waveform
#680 -
Add support for multiple QRM modules
#680 -
Update qpysequence to 10.1
#680
Improvements
- The method
CalibrationNode._execute_notebook()
now changes the working directory to the notebook directory before the execution and restores the previous one after the papermill execution. It allows the notebooks now to use relative paths. Also, the initialization ofCalibrationNode
will now contain absolute paths for the attributesnb_folder
andnb_path
#693
Breaking changes
- Added support for Qblox cluster firmware v0.6.1 and qblox-instruments v0.11.2. This changes some of the i/o mappings in the runcard for qblox sequencers so with older versions is broken.
#680
Bug fixes
- Resolved an issue where attempting to execute a previously compiled QUA program on a newly instantiated Quantum Machine resulted in errors due to cache invalidation.
#706
0.24.0
New features since last release
- Introduced a new parameter,
disable_autosync
, to theQProgram
constructor. This parameter allows users to control the automatic synchronization behavior of loops within their quantum programs. By default, the parameter is set toFalse
, enabling the compiler to automatically insert synchronization operations at the conclusion of each loop. Users have the option to setdisable_autosync
toTrue
to indicate that they prefer to manage loop timing manually. This feature is particularly useful for operations on Qblox hardware, due to its constraints on dynamic synchronization. It's important to note that Quantum Machines will disregard this setting and adhere to the default synchronization behavior.
#694
Breaking changes
- The unit of measurement for phases within QProgram has been updated from degrees to radians.
#695
Bug fixes
- Resolved an issue encountered during the retrieval of results from QProgram executions on Qblox hardware, where acquisition data from other sequencers would unintentionally be deleted.
#691