This issue will cover an in-depth explanation of Qililab, comparing it to the Qibolab library, looking at their similarities and differences, and discussing possible advantages/disadvantages of both implementations.
Class comparison
Backend
This class is used by Qibo to know which gate and circuit to instantiate when building a Circuit
.
In Qililab we no longer use this class: given that the class Experiment
is the one in charge of executing Circuit
classes, we can safely remove this dependence from Qibo. Consequently, we don't need to call qibo.set_backend()
anymore.
Platform
In both Qililab and Qibolab libraries the goal of the class Platform
is to represent the structure of the chip being used and of all the instruments connected to it. However, we moved all the non-descriptive methods (connect()
, setup()
, execute()
...) to the new class Experiment
. Please find below the class diagrams for the Platform
and Experiment
. The code may slightly differ from the class diagrams because it is still a work in progress.
Platform
![image](https://user-images.githubusercontent.com/99880052/169810937-b30da5c7-369d-4e7f-aa47-f60a43940bfe.png)
As you may have seen in the diagram, we introduce the idea of Bus
, which is a representation of a line in the lab: it contains a SystemControl
class (a class describing the instrument/s needed to control and readout the qubit) and a Qubit
/ Resonator
class (objects describing the qubit/resonator that the line is connected to).
The ChipPlaceHolder
class is not yet implemented. Ideally this class should describe all the information of the chip and how it is connected to the instruments (to which bus is each element connected).
Note: Whenever we need to use a group of classes (such as a list of Bus
objects) we represent it as a new class (Buses
). This gives us more freedom to control them.
Experiment
Note: In the following class diagram Experiment
is referenced as CalibrationTest
.
![image](https://user-images.githubusercontent.com/99880052/169811121-8d478ecb-60de-40a7-9ef5-dc4db6966c12.png)
The important ideas to take from this class diagram are the following:
- The
Experiment
class' constructor takes 3 arguments: a list of Circuit
or PulseSequence
classes, a platform name and (optionally) experiment settings.
- The platform name is used to build the
Platform
object.
- The
Platform
, PulseSequence
and ExperimentSettings
are used to build the Execution
class. This class contains a list of BusExecution
classes, which contain all the pulses that will run for a specific bus.
- Running the execution returns a class containing the results (similar to the
HardwareState
class of Qibolab).
Some advantages of using this Platform
+ Experiment
representation:
- The
Platform
class is platform agnostic: there is no need to modify the source code and add a new platform class every time we want to add a new platform (using a new lab, changing instruments...). We just need to modify the YAML file containing the description of the Platform, and all the needed classes will instantiate automatically.
- Given that a
BusExecution
class is instantiated if and only if there is a PulseSequence
on that specific bus, the 'Experiment' class only connects to the instruments needed for the execution. This will become relevant when running sequences on multiple qubit chips, given that a customer might want to run, for example, a 3-qubit sequence on a 5-qubit chip.
- Multiple circuits can be executed without having to connect and disconnect every time.
- Given that the 'Experiment' class knows what platform is being used, it can execute a sequence of pulses while changing any parameter/s of the platform, which is very useful for characterization and tuning of the qubit. One clear example is tuning the RX and RY gates by running an AllXY experiment while changing the gain of the QCM:
![image](https://user-images.githubusercontent.com/99880052/169870659-83698892-603e-4dc9-977a-b6a9a7cb0d70.png)
Instruments
The classes used to control the Qblox and Rohde&Schwarz instruments are, of course, very similar. We however introduced several layers of abstraction, which simplifies a lot the task of adding new instruments. Another advantage of using abstract classes is that we can connect Qililab with our FluxQubitSimulator
very easily, by just adding a SimulatedSystemControl
class (which is agnostic to the simulated qubit and thus it only needs to be added once) and adding a new YAML defining the simulated platform.
I don't think adding the simulator in Qibolab is very straightforward, because the AbstractPlatform
class is not instrument agnostic:
- It has a hard coded local oscillator, which is not needed when using an integrated system control or a simulator.
- It has a hard coded QRM and QCM, which is not needed when using a simulator.
- In the
execute_pulse_sequence
method of the MultiqubitPlatform
class you use instrument-specific methods such as upload()
, which I believe is only used with Qblox instruments.
Gates
Qibolab defines gate classes that inherit from qibo.gates
, and uses the method to_u3_params
to translate each gate into a pulse. Qililab uses a class CircuitToPulses
that is responsible of translating a Qibo Circuit
class into a PulseSequence
.
Qilimanjaro Code Guidelines
Leaving the code functionality aside, in Qilimanjaro we are putting a lot of effort in writing clear, understandable and maintainable code. For this reason, we constituted the Qilimanjaro Code Guidelines, and we make use of the pre-commit framework to make sure that Qililab follows them thoroughly.
These are some of our code guidelines:
- OOP: Use an Object Oriented Programming approach to develop code as DRY as possible.
- Code readability and maintainability:
- A class has one single responsibility.
- Use a file per class.
- A function cannot have more than three parameters.
- Use of dataclasses.
- Use keyword arguments when calling functions
func(a=a, b=b)
.
- Typing: All functions are typed for the input parameters and the returning output statement.
- Testing: Use of unit, integration and end-to-end tests that cover more than 80% of the code.
- Documentation: We document our functions following Google style docstrings.
- We follow the S.O.L.I.D. principles.
- We avoid as much code smells as possible.
- We make use of design patterns as much as possible.