- 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
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 aVariable
that can be used seamlessly withinSetParameter
andExecuteQProgram
.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_path0
is a mandatory field (for flux, drive and readout lines) andoffset_path1
is 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.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)
-
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), ] )
-
Added
add_block()
andget_block()
methods to theCalibration
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 aQProgram
orExperiment
by 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
qililab
in function of the platform connectivity. This is done passingQibo
ownRouters
andPlacers
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 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_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: ...
-
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
-
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 likepre-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 indev-requirements.txt
have been updated to their latest versions, ensuring better compatibility and performance. #813 -
platform.execute_experiment()
and the underlyingExperimentExecutor
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 ofExperimentResults
class have been changed accordingly. #796 -
Added pulse distorsions in
execute_qprogram
for QBlox in a similar methodology to the distorsions implemented in pulse circuits. The runcard needs to contain the same structure for distorsions as the runcards for circuits and the code will modify the waveforms after compilation (insideplatform.execute_qprogram
).Example (for Qblox)
buses: - alias: readout ... distortions: - name: exponential tau_exponential: 1. amp: 1. sampling_rate: 1. # Optional. Defaults to 1 norm_factor: 1. # Optional. Defaults to 1 auto_norm: True # Optional. Defaults to True - name: bias_tee tau_bias_tee: 11000 sampling_rate: 1. # Optional. Defaults to 1 norm_factor: 1. # Optional. Defaults to 1 auto_norm: True # Optional. Defaults to True - name: lfilter a: [] b: [] norm_factor: 1. # Optional. Defaults to 1 auto_norm: True # Optional. Defaults to True
-
The execution of
QProgram
has been split into two distinct steps: compilation and execution.- Compilation: Users can now compile a
QProgram
by calling:
platform.compile_qprogram( qprogram: QProgram, bus_mapping: dict[str, str] | None = None, calibration: Calibration | None = None )
This method can be executed without being connected to any instruments. It returns either a
QbloxCompilationOutput
or aQuantumMachinesCompilationOutput
, depending on the platform setup.- Execution: Once the compilation is complete, users can execute the resulting output by calling:
platform.execute_compilation_output( output: QbloxCompilationOutput | QuantumMachinesCompilationOutput, debug: bool = False )
If desired, both steps can still be combined into a single call using the existing method:
platform.execute_qprogram( qprogram: QProgram, bus_mapping: dict[str, str] | None = None, calibration: Calibration | None = None, debug: bool = False )
- Compilation: Users can now compile a
-
Introduced settable attributes
experiment_results_base_path
andexperiment_results_path_format
in thePlatform
class. These attributes determine the directory and file structure for saving experiment results during execution. By default, results are stored withinexperiment_results_base_path
following the format{date}/{time}/{label}.h5
. #819 -
Added a
save_plot=True
parameter to theplotS21()
method ofExperimentResults
. When enabled (default: True), the plot is automatically saved in the same directory as the experiment results. #819 -
Improved the transpiler, by making it more modular, and adding a
gate_cancellation()
stage before the transpilation to natives, this stage can be skipped, together with the oldoptimize_transpilation()
, if the flagoptimize=False
is passed. #823 -
Split execution of annealing programs into two steps: compilation and execution. #825
-
Added a try and except similar to the dataloss error to restart the measurement in case of random timeout issue for quantum machines. This is a temporary fix and will be deleted once the Quantum Machines team fix their issue.
-
Renamed the platform's
execute_anneal_program()
method toexecute_annealing_program()
and updated its parameters. The method now expectspreparation_block
andmeasurement_block
, which are strings used to retrieve blocks from theCalibration
. These blocks are inserted before and after the annealing schedule, respectively. #816 -
Major reorganization of the library structure and runcard functionality. Key updates include:
- Removed obsolete instruments, such as VNAs.
- Removed the
drivers
module. - Simplified the
Qblox
sequencer class hierarchy into two main classes:QbloxSequencer
andQbloxADCSequencer
. - Removed
SystemController
andReadoutSystemController
; buses now interface directly with instruments. - Introduced a new
channels
attribute to theBus
class, allowing specification of channels for each associated instrument. - Removed the
Chip
class and its related runcard settings. - Eliminated outdated settings, such as instrument firmware.
- Refactored runcard settings into a modular structure with four distinct groups:
instruments
andinstrument_controllers
for lab instrument setup.buses
for grouping instrument channels.digital
for digital compilation settings (e.g., Qibo circuits).analog
for analog compilation settings (e.g., annealing programs).
-
Fixed minor type bug in
Platform
. #846 -
Fixed minor type bug in
CrosstalkMatrix
. #825 -
Fixed typo in ExceptionGroup import statement for python 3.11+ #808
-
Fixed serialization/deserialization of lambda functions, mainly used in
experiment.execute_qprogram()
method. The fix depends on thedill
library which is added as requirement. #815 -
Fixed calculation of Arbitrary waveform's envelope when resolution is greater than 1ns. #837
-
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.Example:
buses: - alias: readout_q0_bus system_control: name: readout_system_control instruments: [QMM] port: readout_line_q0 distortions: [] instruments: - name: quantum_machines_cluster alias: QMM firmware: ... elements: - bus: readout_q0_bus rf_inputs: octave: octave1 port: 1 rf_outputs: octave: octave1 port: 1 time_of_flight: 160 smearing: 0 intermediate_frequency: 10.0e+6 threshold_rotation: 0.5 threshold: 0.03 ...
-
Added
thresholds
argument to_execute_qprogram_with_quantum_machines
method inPlatform
. This argument allows to threshold results after the execution of theQProgram
. It is also a new parameter that can be specified on the runcard for each readout bus. An example of the configuration of this parameter on the runcard can be found above. -
Added
filter
argument inside the qua config file compilation from runcards with qm clusters. This is an optional element for distorsion filters that includes feedforward and feedback, two distorion lists for distorsion compensation and fields in qua config filter. These filters are calibrated and then introduced as compensation for the distorsions of the pulses from external sources such as Bias T. The runcard now might include the new filters (optional):Example:
instruments: - name: quantum_machines_cluster alias: QMM firmware: 0.7.0 ... controllers: - name: con1 analog_outputs: - port: 1 offset: 0.0 filter: feedforward: [0.1,0.1,0.1] feedback: [0.1,0.1,0.1] ...
-
Added loopbacks in the octave config file for qua following the documentation at https://docs.quantum-machines.co/1.2.0/qm-qua-sdk/docs/Guides/octave/?h=octaves#setting-the-octaves-clock. By default only port 1 of the octave is linked with a local demodulator, to work with the rest of the ports at the back ports must be connected based on the Octave Block Diagram [https://docs.quantum-machines.co/1.2.0/qm-qua-sdk/docs/Hardware/octave/#octave-block-diagram\]. Where
Synth
is one of the possible 3 synths andDmd
is one of the 2 demodulators.Example:
- name: quantum_machines_cluster alias: QMM ... octaves: - name: octave1 port: 11252 ... loopbacks: Synth: Synth2 # Synth1, Synth2, Synth3 Dmd: Dmd2LO # Dmd1LO, Dmd2LO
-
Added delay variables to Qblox qprogram implementation. The delays are added in the runcard in nanoseconds and they can be positive or negative scalars (negative delays will make the rest of buses wait). The delay is a wait applied to each iteration of a loop where the bus is present.
Example:
buses: - alias: readout ... delay: 100
-
Improve Crosstalk matrix
from_buses
method so it can be a dictionary of buses and crosstalks coefficients. [#784]#784 -
Now platform.get_parameter works for QM without the need of connecting to the machine.
-
Added the option to get the time of flight and smearing information from the QM cluster #751
-
Improved the algorithm determining which markers should be ON during execution of circuits and qprograms. Now, all markers are OFF by default, and only the markers associated with the
outputs
setting of QCM-RF and QRM-RF sequencers are turned on. #747 -
Automatic method to implement the correct
upsampling_mode
when the output mode is selected asamplified
(fluxes), theupsampling_mode
is automatically defined aspulse
. In this mode, the upsampling is optimized to produce cleaner step responses. #783 -
Automatic method for
execute_qprogram
in quantum machines to restart the measurement in case theStreamProcessingDataLossError
is risen byqua-qm
, the new feature allows to try again the measurement a number of times equal to the value ofdataloss_tries
(default of three). We can define this value atexecute_qprogram(..., dataloss_tries = N)
and will only do its intended job in case of working with QM. #788
-
Big code refactor for the
calibration
module/directory, where allcomparisons
,check_parameters
,check_data()
,check_state()
,maintain()
,diagnose()
and other complex unused methods have been deleted, leaving only linear calibration.Also some other minor improvements like:
drift_timeout
is now a single one for the full controller, instead of a different one for each node.- Notebooks without an export are also accepted now (we will only raise error for multiple exports in a NB).
- Extended/Improved the accepted type for parameters to input/output in notebooks, thorught json serialization. #746
-
Variables in
QProgram
andExperiment
now require a label.qp = QProgram() gain = qp.variable(label="gain", domain=Domain.Voltage)
- Deleted all the files in
execution
andexperiment
directories (Already obsolete). #749
-
Hotfix to allow to serialise zeros in yaml. #799
-
get_parameter for QM did not work due to the lack of the variable
bus_alias in self.system_control.get_parameter
. The variable has been added to the function and now get parameter does not return a crash. #751 -
set_parameter for intermediate frequency in quantum machines has been adapted for both OPX+ and OPX1000 following the new requirements for OPX1000 with qm-qua job.set_intermediate_frequency. #764
-
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
-
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.
-
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.
-
Remove
qiboconnection
dependency from Qililab. It is not a requirement anymore. #732 -
Following the remove of Qiboconnection,
LivePlot
has been removed along with the creation of aPlatform
via API. #732 -
Remove the deprecated
path
argument frombuild_platform()
.
- Introduce the Two-Step pulse shape to improve readout #730
- Remove qiboconnection_api.block_device() and release_device() #728
- Hotfix for the 2readout problem #720
- Appended hardcoded Time of Flight #711
-
Add FlatTop pulse shape #680
-
Add FlatTop waveform #680
-
Add support for multiple QRM modules #680
-
Update qpysequence to 10.1 #680
- 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
- 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
- Added documentation for QProgram.
- 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
- 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
- The unit of measurement for phases within QProgram has been updated from degrees to radians. #695
- 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
- Fixed an issue when serializing / deserializing a QProgram so results are returned in a standard results class. #688
- Fixed an issue when serializing / deserializing a QProgram that contained an Arbitrary waveform or a DRAG pulse. #686
- Fixes an equality issue of QProgram's variables that resulted in a slightly different QProgram when serializing and then deserializing. #684
-
Allow execution of
QProgram
throughplatform.execute_qprogram
method for Quantum Machines hardware. #648 -
Allow multiple measurements of the same qubit in a single circuit. Also allow measurements in the middle of a circuit. #674
-
Wait times longer than 2**16-4 (QBLOX maximum wait time in a Q1ASM wait instruction) are now allowed in the middle of a circuit. #674
-
Add method to get sequencer channel id from qubit index and bus alias #678
-
Added
bus_mapping
parameter inQbloxCompiler.compile
method to allow changing the bus names of the compiled output. #648 -
Improved
QuantumMachinesCluster
instrument functionality. #648 -
Improved execution times of
QProgram
when used inside a software loop by using caching mechanism. #648 -
Added
DictSerializable
protocol andfrom_dict
utility function to enable (de)serialization (from)to dictionary for any class. #659 -
Added method to get the QRM
channel_id
for a given qubit. #664 -
Added Domain restrictions to
Drag
pulse,DragCorrection
waveform andGaussian
waveform. #679 -
Compilation for pulses is now done at platform instead of being delegated to each bus pointing to an awg instrument. This allows easier communication between
pulse_bus_schedules
so that they can be handled at the same time in order to tackle more complex tasks which were not possible otherwise. It also decouples, to a great extent, the instruments and instrument controllers (hardware) from higher level processes more typical of quantum control, which are involved in the pulse compilation to assembly program steps. #651 -
Changed save and load methods using
PyYAML
toruamel.YAML
. #661 -
Qprogram's qblox compiler now allows iterations over variables even if these variables do nothing. (eg. iterate over nshots) #666
-
Added the temporary parameter
wait_time
to QProgram'splay
method. This allows the user to emulate atime_of_flight
duration for measurement until this is added as a setting in runcard. #648 -
Fixed issue with Yokogawa GS200 instrument, that raised an error during initial_setup when the instrument's output was on. #648
-
Added Yokogawa
GS200
instrument and associated istrument controller. #619 -
Added QDevil
QDAC-II
instrument and associated istrument controller. #634 -
set_parameter()
can now be used without being connected to the instruments. #647
QuantumMachinesCluster
can be created by translating the runcard into the equivelant QUA config dictionary.initial_setup
,turn_on
andturn_off
methods have been edited to properly instatiate and calibrate the instrument. #620
QuantumMachinesManager
has been renamed toQuantumMachinesCluster
andQMMController
toQuantumMachinesClusterController
. #620
- Fixed bug #653, where saving the runcard would not include the reset parameter in the instrument controllers. #653
- Fixed bug #635, where trying to read/set the Intermediate frequency parameter was failing for Qblox RF modules. #635
-
Added real time results saving capability. #598
-
Raise an error if a user requests to run a circuit that is longer than
repetition_duration
#621 -
Add parameter to the SLURM magic method to configure the time limit of a job. #608
-
Add magic method to run python code as slurm jobs from Jupyter Notebooks. #600
-
Implemented
QuantumMachinesCompiler
class to compile QPrograms to QUA programs. #563 -
Implemented
platform.execute_qprogram()
method to execute a qprogram on Qblox hardware. #563 -
Added the two main classes need for automatic-calibration,
CalibrationController
andCalibrationNode
#554 -
Added the driver for Quantum Machines Manager and a new QuantumMachinesResult class to handle Quantum Machines instruments. #568
-
Implemented the
QuantumMachinesMeasurementResult
class to store data acquired from a single instrument. #596
-
Improved the UX for circuit transpilation by unifying the native gate and pulse transpiler under one
CircuitTranspiler
class, which has 3 methods:circuit_to_native
: transpiles a qibo circuit to native gates (Drag, CZ, Wait, M) and optionally RZ if optimize=False (optimize=True by default)circuit_to_pulses
: transpiles a native gate circuit to aPulseSchedule
transpile_circuit
: runs both of the methods above sequentiallyWait
gate moved from theutils
module tocircuit_transpilation_native_gates
#575
-
Added
infinite_loop()
method to QProgram. #563 -
Added
measure()
method to QProgram. #563 -
Various improvements in the compilation flow of
QbloxCompiler
. #563 -
Updated
qm-qua
library to latest1.1.5.1
. #596
-
Changed
resolution
parameter of waveforms'envelope()
method to integer. #563 -
Changed the way variables work within a QProgram. Variables are now instantiated based on the physical domain they affect. #563
- Removed the park gate since it is no longer needed #575
-
Fixed bug #626, where a regression bug was introduced by adding empty schedules to all flux buses, not only the ones with an AWG registered instrument, as it was intended originally. #628
-
Fixed bug #579, were now all
yaml.dumps
are done with ruamel, for not losing decimals precisons, and also following the previous bug due to the elimination ofruamel.yaml.round_trip_dump
, the version of ruamel in qililab got fixed, and imports where rewritten for more clarity #578
-
Fixed bug where executing multiple circuits with each measuring different qubits would launch measurements for previously measured qubits even if those did not have measurements on the circuit currently being executed. #576
-
ruamel 0.18.0 eliminated
ruamel.yaml.round_trip_dump
, so we changed its usage to the recommended version:ruamel.yaml.YAML().dump
#577
-
Changed gate settings serialization so that fields with None values are not in the resulting dictionary #562
-
Update qiboconnection to 0.12.0 #559
-
Added phase correction for CZ gates to the optimize step of translate circuit in
qililab.transpiler.transpiler
. Gates now can accept an optional dicionary with additional settings. As an example, the CZ phase correction can be added at options for each qubit:CZ(0,2): - bus: flux_line_q2_bus pulse: amplitude: 1.0 phase: 0 duration: 101 shape: name: snz t_phi: 1 b: 0.5 options: q0_phase_correction: 0.1 q2_phase_correction: 0.2
The default value for the
optimize
flag in qililab.transpiler.transpiler.translate_circuit has been changed fromFalse
toTrue
-
build_platform() has been extended: #533
Now appart from passing the runcard YAML file path, you can directly pass an already build dictionary.
Also the argument has changed names from
path
toruncard
. -
Buses serialization have been implemented: #515
When printing the runcard, in the buses part we will now have the normal Buses serialization, plus the parameters of the instruments associated to that bus, with the
to_dict/from_dict
methods.`Also the serialization includes using the
set/get_params
for setting/getting the instruments params. -
Distorsions and PulseShapes have been improved: #512
They now work for
amplitude=0
, and for negative andsnz
envelopes (both positive and negatives)It now also adds the
norm_factor
parameter for manual normalization to all the distortions (previously only in the lfilter distortion)And finally we also have added the option to skip the automatic normalization that we do, setting the parameter
auto_norm
toFalse
, (defaults toTrue
).Plus, added a lots of tests and documentation to both classes.
-
Fixed bug which did not allow gaussian waveforms to have amplitude 0 #471
-
Delete
Schema
class fromPlatform
andRuncardSchema
classes (and from the rest of qililab).Also
RuncardSchema
is now called simplyRuncard
(since its the class that maps our runcard files).This PR brings importants simplifications to the full qililab structure, now the runcard will have the following structure:
name: ... device_id: ... gates_settings: # Old `settings` without name & device_id ... chip: ... buses: ... instruments: ... instrument_controllers: ...
instead than the previous:
settings: name: ... device_id: ... ... schema: # Schema disappears from the platform. chip: ... buses: ... instruments: ... instrument_controllers: ...
Notice also how
settings
(and his respective classPlatformSettings
) has changed togates_settings
(and the class toGatesSettings
having the runcard string and the class the same name now, before they didn't). -
Simplify circuit gate to pulse transpilation. Previous hardware gates are removed. Now gates can be defined in the runcard as a list of
GateEvent
items with bus, wait time (optional) and pulse information. This allows more customization and freedom of what a certain gate does. The gate will be transpiled as long as the circuit gate's name and qubits match those of the gate in the runcard. An example of a custom gate for the circuit X gate at qubit 0 and a CZ gate at (0,1)X(0): - bus: drive_line_q0_bus # alias of the bus wait_time: 200 pulse: amplitude: 0.5 phase: 0 duration: 200 frequency: 0 shape: name: drag drag_coefficient: 1 num_sigmas: 2 - bus: flux_line_q0_bus # alias of the bus pulse: amplitude: 1.0 phase: 0 duration: 200 frequency: 0 shape: name: rectangular CZ(0,1): - bus: flux_line_q0_bus # park pulse pulse: amplitude: 0.5 phase: 0 duration: 241 frequency: 0 shape: name: rectangular - bus: flux_line_q1_bus # snz wait_time: 40 # wait 40ns after start of the park pulse pulse: amplitude: 1 phase: 0 duration: 201 frequency: 0 shape: name: snz b: 0.5 t_phi: 1
Experiments can access
GateEvent
items by using the gate and qubitalias
like previously and adding_item
to access aGateEvent
that is not the first event of the gate. For example,set_parameter(parameter='amplitude', value=0.8, alias='X(0)')
will set the amplitude in the gate setting above from 0.5 to 0.8. This is equivalent toalias='X(0)_0'
. Howeveralias='X(0)_1'
sets instead the amplitude of the second event (bus=flux_line_q0_bus
) from 1.0 to 0.8 #472 -
Rename Experiment and CircuitExperiment classes and dependencies: This branch renames the Experiment class to BaseExperiment and CircuitExperiment to Experiment. #482
-
Add a new Factory for the Buses and registered the current ones #487
-
Add the NEW_DRIVERS flag to choose between old and new instruments and bus drivers. #486
-
Add a new Factory for the InstrumentDrivers and registered the current ones #473
-
Add interfaces and drivers for Flux bus: This PR brings the qililab implementation of the Flux bus driver and unittests. #469
-
Add ReadoutBus class. #465
-
Fix: check whether cluster has submodules not present at init time. #477
-
Add interfaces and drivers for Voltage and Current sources: This PR brings the qililab implementation of the Keithly2600 and Yokowaga QCodes drivers and unittests. #438
-
Fix: add acquisitions and weights to Sequencer QRM #461
-
Add DriveBus and its interface for the new bus structure. 457
-
Add QProgram for developing quantum programs in the bus level, the operations
Play
,Sync
,Wait
,Acquire
,SetGain
,SetOffset
,SetFrequency
,SetPhase
,ResetPhase
, and the iteration blocksAcquireLoop
andLoop
. 452 -
Add QBloxCompiler for compiling QProgram into QPySequence. 481
-
Add waveforms for the new QProgram 456
-
Add interface for Voltage and Current sources #448
-
New AWG Interface + SequencerQCM, Pulsar, QCM-QRM drivers #442
-
New Digitiser interface + SequencerQRM driver #443
-
Add interface for the Attenuator. This is part of the "Promoting Modular Autonomy" epic. #432
-
Added new drivers for Local Oscillators inheriting from QCoDeS drivers and Local Oscillator interface. This is part of the "Promoting Modular Autonomy" epic. #437
-
Add qcodes_contrib_drivers (0.18.0) to requierements #440
-
Update qcodes to latest current version (0.38.1) #431
-
Added hotfix for bus delay issue from Hardware: This fix adds a delay for each pulse on a bus #439
-
Added
T1
portfolio experiment #409 -
The
ExecutionManager
can now be built from the loops of the experiment. This is done byalias
matching, the loops will be executed on the bus with the samealias
. Note that aliases are not unique, therefore the execution builder will use the first bus alias that matches the loop alias. An exception is raised if aloop.alias
does not match anybus.alias
specified in the runcard #320 -
Added
T2Echo
portfolio experiment #415 -
The
ExecutionManager
can now be built from the loops of the experiment. This is done byalias
matching, the loops will be executed on the bus with the samealias
. Note that aliases are not unique, therefore the execution builder will use the first bus alias that matches the loop alias. An exception is raised if aloop.alias
does not match anybus.alias
specified in the runcard #320 -
The
Experiment
class has been changed to support a more general definition of experiment by removing thecircuits
andpulse_schedules
. A new classCircuitExperiment
inherits from the newExperiment
class has the previous attributes and all the functionality the oldExperiment
had.experiment = Experiment(platform=platform, options=options) experiment_2 = CircuitExperiment(platform=platform, options=options, circuits=[circuit])
-
Added
threshold_rotation
parameter toAWGADCSequencer
. This adds a new parameter to the runcard of sequencers of that type, QRM sequencers in the case of Qblox. This value is an angle expressed in degrees from 0.0 to 360.0.awg_sequencers: - identifier: 0 chip_port_id: 1 intermediate_frequency: 1.e+08 weights_path0: [0.98, ...] weights_path1: [0.72, ...] weighed_acq_enabled: true threshold: 0.5 threshold_rotation: 45.0 # <-- new line
-
Add
ForLoop
iteration method to QProgram. 481 -
Add
Parallel
block to QProgram to allow parallel loops, and compilation support to QBloxCompiler. 496 -
Allow CZ gates to use different pulse shapes #406
-
Add support for the
Wait
gate -
Addeds support for the
Wait
gate #405 -
Added support for
Parameter.Gate_Parameter
inexperiment.set_parameter()
method. In this case, alias is a, convertable to integer, string that denotes the index of the parameter to change, as returned bycircuit.get_parameters()
method. #404 -
The
Chip
class now uses thealias
of each node to define node connections. #494Before:
chip: nodes: - name: qubit alias: qubit_0 id_: 0 qubit_index: 0 frequency: 4.92e+09 nodes: [2, 10, 20, 30]
Now:
chip: nodes: - name: qubit alias: qubit_0 qubit_index: 0 frequency: 4.92e+09 nodes: [qubit_2, resonator_q0, drive_line_q0, flux_line_q0]
-
ql.execute
now accepts a list of circuits! #549
- Old scripts using
Experiment
with circuits should be changed and useCircuitExperiment
instead. #334
id
andcategory
attributes have been removed fromqililab
. #494
-
Documentation for the Chip module: [#553] (#553)
Includes documentation for all public features of the Chip module
-
Documentation for the Pulse module: #532
Includes documentation for all public features of the Pulse module
-
Added documentation for platform module and the tutorial sections of Platform and Runcards: #531
-
Avoid creating empty sequences for buses that are no flux lines and do for flux ones that do not have any AWG instrument. #556
-
The
threshold
andthreshold_rotation
parameters of aQbloxQRM
can now be set usingPlatform.set_parameter
. #534 -
The
QbloxQRMRF
andQbloxQCMRF
do not save an empty list for the parameterout_offsets
in the saved runcard. #565 -
The
save_platform
now saves in the yaml file float values with the same precision as in thePlatform
object. #565
-
Added hotfixes for several issues encountered during the hackathon: #413
- Save experiments flag for experiments execute method
# Option 1 (Default, save_experiment=True) experiment = Experiment(platform=platform, circuits=circuits, options=options) experiment.execute() # Option 2 (Equivalent to option 1, save_experiment=False) experiment = Experiment(platform=platform, circuits=circuits, options=options) experiment.execute(save_experiment=False)
- Empty sequences to avoid creating repeated programs.
- Create empty programs for all qubit flux lines to activate calibrated offsets.
- Added method to get qubits from the chip
qubits = ql.Chip.qubits()
-
Added to the
draw()
method the option of specifying if you want:- modulation or not,
- plot the real, iamginary, absolute or any combination of these options
- specify the type of plotline to use, such as "-", ".", "--", ":"...
for the classes
Experimental
andExecutionManager
, like:
# Option 1 (Default) figure = sample_experiment.draw( real=True, imag=True, abs=False, modulation=True, linestyle="-" ) # Option 2 (Equivalent to option 1) figure = sample_experiment.draw() # Option 3 (something different, only plotting the envelopes without modulation) figure = sample_experiment.draw(modulation=False, linestyle=".") plt.show()
-
Added
lambda_2
attribute to thecosine.py
module containing theCosine
pulse_shape, modifying the previous A/2*(1-cos(x)). Into a more general A/2*(1-lambda_1cos(phi)-lambda_2cos(2phi)), giving a modified sinusoidal-gaussian.-
lambda_1 cosine A/2*(1-cos(x)): Starts at height 0 (phase=0), maximum height A (phase=pi) and ends at height 0 (phase=2pi). Which is a sinusoidal like gaussian.
-
lambda_2 cosine A/2*(1-cos(2x)): Starts at height 0 (phase=0), maximum height A (phase=pi/2) then another height 0 in the middle at phase=pi, then another maximum height A (phase=3/2pi) and ends at height 0 (phase=2pi).
For more info check the docstring and the following references:
- Supplemental material B. "Flux pulse parametrization" at [https://arxiv.org/abs/1903.02492%5C%5D],
- OPTIMAL SOLUTION: SMALL CHANGE IN θ at [https://arxiv.org/abs/1402.5467%5C%5D]
-
-
Added user integration for
pulse_distortions
. Now they can be used writing them in the Buses of the runcards:buses: - id_: 0 category: bus alias: feedline_bus system_control: id_: 0 name: readout_system_control category: system_control instruments: [QRM1, rs_1] port: 100 distortions: # <-- new line - name: bias_tee # <-- new line tau_bias_tee: 1.0 # <-- new line - name: lfilter # <-- new line a: [0.1, 1.1] # <-- new line b: [1.1, 1.3] # <-- new line - id_: 10 category: bus alias: drive_line_q0_bus system_control: id_: 10 name: system_control category: system_control instruments: [QCM-RF1] port: 10 distortions: [] # <-- new line
-
Added CZ gate support, 2 qubit gate support to
circuit_to_pulse
and corresponding definitions to the runcard.CZ implements a Sudden Net Zero (SNZ) pulse through the flux line as well as a parking gate (if defined in the runcard) to adjacent qubits with lower frequency than CZ's target qubit. For the parking gate, if the time is greater than the CZ pulse, the extra time is added as padding at the beginning/end of the pulse. The parameters for the CZ in the runcard are amplitude, duration of the halfpulse; and for the CZ's snz pulse b (impulse between halfpulses) and t_phi (time between halfpulses without accounting for b)
Example:
gates: 1: - name: Park amplitude: 1.0 phase: 0 duration: 103 shape: name: rectangular (0,2): - name: CZ amplitude: 1.0 phase: duration: 40 shape: name: snz b: 0.5 t_phi: 1
In the example above, if qubit 1 is connected to 2 and has lower frequency, there will be an attempt to apply a parking pulse. If a Park gate definition is found for qubit 1, then a parking pulse will be applied. The total duration of the CZ gate above will be 2*duration + t_phi + 2 = 83 (each b has 1ns duration and there are 2 bs). Thus the parking gate lasts for some extra 20ns which will result in 10ns 'pad time' in the parking gate before and after the SNZ pulse. Note that the order of the qubits in the CZ is important even if the gate is symmetric, because the second qubit will be the target for the SNZ pulse. #369
-
Added
cosine.py
module containing aCosine
child class ofpulse_shape
, which gives a sinusoidal like gaussian A/2*(1-cos(x)). The shape starts at height 0 (phase=0), maximum height A (phase=pi) and ends at height 0 (phase=2pi)pulse = Pulse( amplitude=..., phase=..., duration=..., frequency=..., pulse_shape=Cosine(), )
-
Added
pulse.pulse_distortion.lfilter_correction.py
module, which is another child class for thepulse.pulse_distortion
package.distorted_envelope = LFilter(norm_factor=1.2, a=[0.7, 1.3], b=[0.5, 0.6]).apply( original_envelopes )
Also adds a phase property to
PulseEvent
and implementsFactory.get
directly in thefrom_dict()
method of the parent classPulseDistortion
. -
Added
get_port_from_qubit_idx
method toChip
class. This method takes the qubit index and the line type as arguments and returns the associated port. #362 -
Added
pulse.pulse_distortion
package, which contains a modulepulse_distortion.py
with the base class to distort envelopes inPulseEvent
, and two modulesbias_tee_correction.py
andexponential_decay_correction.py
, each containing examples of distortion child classes to apply. This new feature can be used in two ways, directly from the class itself:distorted_envelope = BiasTeeCorrection(tau_bias_tee=1.0).apply(original_envelope)
or from the class PulseEvent (which ends up calling the previous one):
pulse_event = PulseEvent( pulse="example_pulse", start_time="example_start", distortions=[ BiasTeeCorrection(tau_bias_tee=1.0), BiasTeeCorrection(tau_bias_tee=0.8), ], ) distorted_envelope = pulse_event.envelope()
This would apply them like: BiasTeeCorrection_0.8(BiasTeeCorrection_1.0(original_pulse)), so the first one gets applied first and so on... (If you write their composition, it will be in reverse order respect the list)
Also along the way modified/refactored the to_dict() and from_dict() and envelope() methods of PulseEvent, Pulse, PulseShape... since they had some bugs, such as:
- the dict() methods edited the external dictionaries making them unusable two times.
- the maximum of the envelopes didn't correspond to the given amplitude.
-
The
QbloxQCMRF
module has been added. To use it, please use theQCM-RF
name inside the runcard:- name: QCM-RF alias: QCM-RF0 id_: 2 category: awg firmware: 0.7.0 num_sequencers: 1 out0_lo_freq: 3700000000 # <-- new line out0_lo_en: true # <-- new line out0_att: 10 # <-- new line out0_offset_path0: 0.2 # <-- new line out0_offset_path1: 0.07 # <-- new line out1_lo_freq: 3900000000 # <-- new line out1_lo_en: true # <-- new line out1_att: 6 # <-- new line out1_offset_path0: 0.1 # <-- new line out1_offset_path1: 0.6 # <-- new line awg_sequencers: ...
-
The
QbloxQRMRF
module has been added. To use it, please use theQRM-RF
name inside the runcard:- name: QRM-RF alias: QRM-RF0 id_: 0 category: awg firmware: 0.7.0 num_sequencers: 1 out0_in0_lo_freq: 3000000000 # <-- new line out0_in0_lo_en: true # <-- new line out0_att: 34 # <-- new line in0_att: 28 # <-- new line out0_offset_path0: 0.123 # <-- new line out0_offset_path1: 1.234 # <-- new line acquisition_delay_time: 100 awg_sequencers: ...
-
The
get_bus_by_qubit_index
method ofPlatform
class now returns a tuple of three buses:flux_bus, control_bux, readout_bus
. #362 -
Arbitrary mapping of I/Q channels to outputs is now possible with the Qblox driver. When using a mapping that is not possible in hardware, the waveforms of the corresponding paths are swapped (in software) to allow it. For example, when loading a runcard with the following sequencer mapping a warning should be raised:
awg_sequencers: - identifier: 0 output_i: 1 output_q: 0
>>> platform = build_platform(name=runcard_name) [qililab] [0.16.1|WARNING|2023-05-09 17:18:51]: Cannot set `output_i=1` and `output_q=0` in hardware. The I/Q signals sent to sequencer 0 will be swapped to allow this setting.
Under the hood, the driver maps
path0 -> output0
andpath1 -> output1
. When applying an I/Q pulse, it sends the I signal throughpath1
and the Q signal throughpath0
. #324 -
The versions of the
qblox-instruments
andqpysequence
requirements have been updated to0.9.0
#337 -
Allow uploading negative envelopes on the
QbloxModule
class. #356 -
The parameter
sync_en
of the Qblox sequencers is now updated automatically when uploading a program to a sequencer. This parameter can no longer be set usingset_parameter
. #353 -
The Qblox RF modules now support setting their LO frequency using the generic
Parameter.LO_FREQUENCY
parameter. #455
-
Remove the
awg_iq_channels
from theAWG
class. This mapping was already done within each sequencer. #323 -
Remove the
get_port
method from theChip
class. #362
- Add
_set_markers
method to theQbloxModule
class and enable all markers. For the RF modules, this command enables the outputs/inputs of the instrument. We decided to enable all markers by default to be able to use them later if needed. #361
-
Added Drag gate support to
circuit_to_pulse
so that Drag gates are implemented as drag pulses #312 -
Added
experiment/portfolio/
submodule, which will contain pre-defined experiments and their analysis. #189 -
Added
ExperimentAnalysis
class, which contains methods used to analyze the results of an experiment. #189 -
Added
Rabi
portfolio experiment. Here is a usage example:loop_range = np.linspace(start=0, stop=1, step=0.1) rabi = Rabi(platform=platform, qubit=0, range=loop_range) rabi.turn_on_instruments() bus_parameters = {Parameter.GAIN: 0.8, Parameter.FREQUENCY: 5e9} rabi.bus_setup(bus_parameters, control=True) # set parameters of control bus x_parameters = {Parameter.DURATION: 40} rabi.gate_setup(x_parameters, gate="X") # set parameters of X gate rabi.build_execution() results = rabi.run() # all the returned values are also saved inside the `Rabi` class! post_processed_results = rabi.post_process_results() fitted_parameters = rabi.fit() fig = rabi.plot()
-
Added
FlippingSequence
portfolio experiment. #245 -
Added
get_bus_by_qubit_index
method to thePlatform
class. #189 -
Added
circuit
module, which contains everything related to Qililab's internal circuit representation.The module contains the following submodules:
circuit/converters
: Contains classes that can convert from external circuit representation to Qililab's internal circuit representation and vice versa.circuit/nodes
: Contains classes representing graph nodes that are used in circuit's internal graph data structure.circuit/operations
: Contains classes representing operations that can be added to the circuit. #175
-
Added
Circuit
class for representing quantum circuits. The class stores the quantum circuit as a directed acyclic graph (DAG) using therustworkx
library for manipulation. It offers methods to add operations to the circuit, calculate the circuit's depth, and visualize the circuit. Example usage:# create a Circuit with two qubits circuit = Circuit(2) # add operations for qubit in [0, 1]: circuit.add(qubit, X()) circuit.add(0, Wait(t=100)) circuit.add(0, X()) circuit.add((0, 1), Measure()) # print depth of circuit print(f"Depth: {circuit.depth}") # print circuit circuit.print() # draw circuit's graph circuit.draw()
-
Added
OperationFactory
class to register and retrieve operation classes based on their names. #175 -
Added
CircuitTranspiler
class for calculating operation timings and transpiling quantum circuits into pulse operations. Thecalculate_timings()
method annotates operations in the circuit with timing information by evaluating start and end times for each operation. Theremove_special_operations()
method removes special operations (Barrier, Wait, Passive Reset) from the circuit after the timings have been calculated. Thetranspile_to_pulse_operations()
method then transpiles the quantum circuit operations into pulse operations, taking into account the calculated timings. Example usage:# create the transpiler transpiler = CircuitTranspiler(settings=platform.settings) # calculate timings circuit_ir1 = transpiler.calculate_timings(circuit) # remove special operations circuit_ir2 = transpiler.remove_special_operations(circuit_ir1) # transpile operations to pulse operations circuit_ir3 = transpiler.transpile_to_pulse_operations(circuit_ir2)
-
Added
QiliQasmConverter
class to convert a circuit from/to QiliQASM, an over-simplified QASM version. Example usage:# Convert to QiliQASM qasm = QiliQasmConverter.to_qasm(circuit) print(qasm) # Parse from QiliQASM parsed_circuit = QiliQasmConverter.from_qasm(qasm)
-
Pulses with different frequencies will be automatically sent and read by different sequencers (multiplexed readout). #242
-
Added an optional parameter "frequency" to the "modulated_waveforms" method of the Pulse and PulseEvent classes, allowing for specification of a modulation frequency different from that of the Pulse. #242
-
Added
values
andchannel_id
attribute to theLoop
class. Here is an example on how a loop is created now:new_loop = Loop(alias="loop", parameter=Parameter.POWER, values=np.linspace(1, 10, 10))
-
Gate settings can be set for each qubit individually, or tuple of qubits in case of two-qubit gates. Example of updated runcard schema:
gates: 0: - name: M amplitude: 1.0 phase: 0 duration: 6000 shape: name: rectangular - name: X amplitude: 1.0 phase: 0 duration: 100 shape: name: gaussian num_sigmas: 4 1: - name: M amplitude: 1.0 phase: 0 duration: 6000 shape: name: rectangular - name: X amplitude: 1.0 phase: 0 duration: 100 shape: name: gaussian num_sigmas: 4 (0,1): - name: CPhase amplitude: 1.0 phase: 0 duration: 6000 shape: name: rectangular
To change settings with set_parameter methods, use the alias format "GATE(QUBITs)". For example:
platform.set_parameter(alias="X(0)", parameter=Parameter.DURATION, value=40) platform.set_parameter(alias="CPhase(0,1)", parameter=Parameter.DURATION, value=80)
-
Weighted acquisition is supported. The weight arrays are set as sequencer parameters
weights_i
andweights_q
, and the weighed acquisition can be enabled setting the sequencer parameterweighed_acq_enabled
totrue
. Note: theintegration_length
parameter will be ignored ifweighed_acq_enabled
is set totrue
, and the length of the weights arrays will be used instead.
awg_sequencers:
- identifier: 0
chip_port_id: 1
intermediate_frequency: 1.e+08
weights_i: [0.98, ...] # <-- new line
weights_q: [0.72, ...] # <-- new line
weighed_acq_enabled: true # <-- new line
threshold: 0.5 # <-- new line
-
Result
,Results
andAcquisitions
classes implement thecounts
method, which returns a dictionary-like object containing the counts of each measurement based in the discretization of the instrument via thethreshold
sequencer parameter. Alternatively, theprobabilities
method can also be used, which returns a normalized version of the same counts object.>>> counts = result.counts() Counts: {'00': 502, '01': 23, '10': 19, '11': 480} >>> probabilities = result.probabilities() Probabilities: {'00': 0.49023438, '01': 0.02246094, '10': 0.01855469, '11': 0.46875}
-
Return an integer (instead of the
Port
class) when callingChip.get_port
. This is to avoid using the privateid_
attribute of thePort
class to obtain the port index. #189 -
The asynchronous data handling used to save results and send data to the live plotting has been improved. Now we are saving the results in a queue, and there is only ONE thread which retrieves the results from the queue, sends them to the live plotting and saves them to a file. #282
-
The asynchronous data handling used to save results and send data to the live plotting has been improved. Previously we only had ONE active thread retrieving the results and saving them but was created and killed after processing one result of the total
Results
object. Now we are creating the thread just ONCE, so threading is handled at theExperiment
level instead of what was done previously at theExution_Manager
level. #298
-
draw()
method ofCircuit
uses Graphviz internally. To be able to call the method Graphviz must be installed. In Ubuntu-based distros a simplesudo apt-get install graphviz
is sufficient. For detailed installation information for your OS please consult Graphviz's installation page. #175 -
gates
property of runcard must be updated to provide a list of gate settings for each qubit individually. #292
-
The
Execution
class has been removed. Its functionality is now added to theExecutionManager
class. Please useExecutionManager
instead. TheExecutionBuilder
returns now an instance ofExecutionManager
. #246 -
The
LoopOptions
class has been removed. It was used to create a numpy array and store this array in thevalues
attribute which is now in theLoop
class. #254 -
The
plot_y_label
argument of theExperimentOptions
class has been removed. #282 -
All the
probabilities
methods that returned apandas.DataFrame
return now adict[str, float]
. All the methods related to the construction of such dataframes have been removed. #283
-
Fixed bug where acquisition data was not deleted when compiling the same sequence twice. #264
-
Fixed bug where the live plotting created a new plot when using parallel loops. #282
-
Added
Experiment.compile
method, which return the compiled experiment for debugging purposes. #225>>> sequences = experiment.compile()
This method returns a list of dictionaries (one dictionary per circuit executed in the experiment). Each dictionary contains the sequences uploaded for each bus:
>>> sequences[0] {'drive_line_bus': [qpysequence_1], 'feedline_input_output_bus': [qpysequence_2]}
This experiment is using two buses (
drive_line_bus
andfeedling_input_output_bus
), which have a list of the uploaded sequences (in this case only 1).We can then obtain the program of such sequences:
>>> sequences[0]["drive_line_bus"][0]._program setup: move 1000, R0 wait_sync 4 average: reset_ph play 0, 1, 4 long_wait_1: move 3, R1 long_wait_1_loop: wait 65532 loop R1, @long_wait_1_loop wait 3400 loop R0, @average stop: stop
-
Added support for setting output offsets of a qblox module. #199
-
Added gate to native gate transpiler. Contains method
translate_circuit
which translates a qibo.Circuit to a qibo.Circuit with native gates (Drag pulses, C-phase), virtual Zs and measurement gates only. #244 -
Added optimizer for the native gate transpiler. Shifts all Z, RZ gates to the left and removes them before measurement. #269
-
Added support for the execution of pulses with durations that are not multiples of 4. For this, a new
minimum_clock_time
attribute has been added to the runcard:settings: id_: 0 category: platform name: spectro_v_flux device_id: 9 minimum_clock_time: 4 # <-- new line!
When a pulse has a duration that is not a multiple of the
minimum_clock_time
, a padding of 0s is added after the pulse to make sure the next pulse falls within a multiple of 4. #227 -
Change name
PulseScheduledBus
toBusExecution
. #225 -
Separate
generate_program_and_upload
into two methods:compile
andupload
. From now on, when executing a single circuit, all the pulses of each bus will be compiled first, and then uploaded to the instruments. #225 -
Make
nshots
andrepetition_duration
dynamic attributes of theQbloxModule
class. When any of these two settings changes, the cache is emptied to make sure new programs are compiled. #225 -
Added methods to
PulseBusSchedule
for multiplexing: #236frequencies()
returns a list of the frequencies in the schedule.with_frequency(frequency: float)
returns a newPulseBusSchedule
containing only those events at that frequency.
-
Pulse
,PulseEvent
,PulseShapes
and child classes are now immutable. #236
-
An
out_offsets
attribute has been added to the settings of aQbloxModule
object. This attribute contains a list of the offsets applied to each output. The runcard should be updated to contain this new information:instruments: - name: QRM alias: QRM id_: 1 category: awg firmware: 0.7.0 num_sequencers: 1 acquisition_delay_time: 100 out_offsets: [0.3, 0, 0, 0] # <-- this new line needs to be added to the runcard! awg_sequencers: ...
- Removed the
AWG.frequency
attribute because it was not used. #225 - Removed
ReadoutPulse
andReadoutEvent
.Pulse
andPulseEvent
have to be used instead. #236 - Removed
add(Pulse)
method fromPulseBusSchedule
andPulseSchedule
.add_event(PulseEvent)
has to be used instead. #236
-
Fixed a bug in
PulseBusSchedule.waveforms
where the pulse time was recorded twice. #227 -
Fixed bug where the
QbloxModule
uploaded the same sequence to all the sequencers. #225
-
Cast
chip
dictionary into theChipSchema
class and remove unusedInstrumentControllerSchema
class. #187 -
Upload sequence directly to Qblox instruments, without having to save & load a
json
file. #197 -
Changed
schedule_index_to_load
argument toidx
for more readability. #192 -
Refactored the
Experiment
class, creating a method for each step of the workflow. TheExperiment.execute
method will run all these methods in order: #192connect
: Connect to the instruments and block the device.initial_setup
: Apply runcard settings to the instruments.build_execution
:- Translate the circuit into pulses.
- Create the
Execution
class (which contains all the buses with the pulses to execute). - Initialize the live plotting.
- Create the
Results
class and theresults.yml
file (where the results will be stored).
turn_on_instruments
: Turn on the instruments (if necessary).run
: Iterate over all the loop values, and for each step:- Generate the program.
- Upload the program.
- Execute the program.
- Save the result to the
results.yml
file. - Send data to live plotting.
- Save the result to the
Experiment.results
attribute.
turn_off_instruments
: Turn off the instruments (if necessary).disconnect
: Disconnect from the platform and release the device.remote_save_experiment
: Ifremote_save = True
, save the experiment and the results to the database.
-
When translating a Circuit into pulses, the target qubit/resonator frequency is now used to initialize the corresponding pulse. #209
-
Removed context manager from
Execution
class. Users will be responsible for turning off and disconnecting the instruments when not using theexecute
method directly! #192 -
Removed the
ExecutionOptions
class. Now the user can freely choose which steps of the workflow to execute. #192 -
Removed the
Platform.connect_and_initial_setup
method. #192 -
Move
connection
anddevice_id
information into thePlatform
class. Now users should adddevice_id
inside the runcard and add aconnection
argument when callingbuild_platform
: #211platform = build_platform(name=runcard_name, connection=connection) platform.connect(manual_override=False)
-
Removed the frequency argument from the
Pulse.modulated_waveforms
method (and all the methods that uses this method internally). Removefrequency
property from certain buses. #209 -
The
Pulse.frequency
argument is now mandatory to initialize a pulse. #209
-
Removed the
ExecutionPreparation
class and theresults_data_management.py
file, and replace it with aprepare_results
method inside theExperiment
class. #192 -
Removed unused
connect
,disconnect
andsetup
methods from theExecution
class. These are used in theExperiment
class, which call the corresponding methods of thePlatform
class. #192 -
Removed the
RemoteAPI
class. This class didn't add any functionality. #192 -
Removed all the
Bus
andSystemControl
types. Now there is only a genericBus
, that can contain aSystemControl
,ReadoutSystemControl
(which contain a list of instruments to control) orSimulatedSystemControl
, which is used to control simulated quantum systems. #210
-
Fixed wrong timing calculation in Q1ASM generation #186
-
Fix bug where calling
set_parameter
withParameter.DRAG_COEFFICIENT
would raise an error. #187 -
The
qibo
version has been downgraded to0.1.10
to allow installation on Mac laptops. #185 -
Fixed the
Platform.get_element
method: #192- Now calling
get_element
of a gate returns aGateSettings
instance, instead of aPlatformSettings
instance. - Add try/except over the
chip.get_node_from_alias
method to avoid getting an error if the node doesn't exist.
- Now calling
- Platform should be stateful for connections (#143)
- updates to Qibo v0.1.11, fixes breaking changes (#139)
- Duplicate hardware_average property & bus.run() in parallel error (#136)
- Experimentally validated & debugged Spectroscopy (#134)
- Experiment: added remote experiment saving (#112)
- enums: transform them to string enums (#125)
- remove hardcoded modulation from Pulse (#127)
- Update qblox and qpysequence
- custom exceptions
- Results: fixed no-results dataframe generation (#109)
- [qili-243] sequence class (#104)
- negative-wait (#106)
- [QILI-201] multibus support (#101)
- remove master drag coefficient (#98)
- pulse events (#93)
- qilisimulator integration (#79)
- qilisimulator integration (#77)
- [QILI-169] load 2D results (#69)
- [QILI-187 ] 🐛 loops minimum length taking the passed value instead of the self (#57)
- [QILI-186] ♻️ renamed beta to drag_coefficient (#56)
- [QILI-185] add option to NOT reset an instrument (#54)
- [QILI-184] ✨ New daily directory generated for results data (#50)
- [QILI-183] 🐛 accept float master duration gate (#49)
- [QILI-182] 🐛 uses deepcopy before pop dict key (#48)
- set beta serialization correctly (#47)
- reference clock after reset and using only the necessary sequencers
- setup: versioning
- [QILI-181] 🐛 fixed values types when they are numpy (#38)
- [QILI-178] set beta master (#37)
- [QILI-180] 🐛 check for multiple loops correctly (#36)
- [QILI-177] 🐛 make sure amplitude and duration are float or int (#35)
- [QILI-174] loop support multiple parameters (#34)
- [QILI-176] set master value for gate amplitude and duration (#33)
- [QILI-168] Set voltage normalization (#32)
- New features from TII trip (#31)
- [QILI-81] Implement schema class (#5)
- [QILI-46] Implement instrument classes (#9)
- [QILI-48] Implement platform, backend and settings classes (#8)