Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tutorials to examples #807

Open
wants to merge 15 commits into
base: 0.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 3 additions & 166 deletions doc/source/tutorials/calibration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,62 +26,7 @@ We start by initializing the platform, that reads the information written in the
respective runcard, a sequence composed of only a measurement and a sweeper
around the pre-defined frequency.

.. testcode:: python

import numpy as np
from qibolab import create_platform
from qibolab.pulses import PulseSequence
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
AcquisitionType,
)

# allocate platform
platform = create_platform("dummy")

# create pulse sequence and add pulse
sequence = PulseSequence()
readout_pulse = platform.create_MZ_pulse(qubit=0, start=0)
sequence.add(readout_pulse)

# allocate frequency sweeper
sweeper = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-2e8, +2e8, 1e6),
pulses=[readout_pulse],
type=SweeperType.OFFSET,
)

We then define the execution parameters and launch the experiment.

.. testcode:: python

options = ExecutionParameters(
nshots=1000,
relaxation_time=50,
averaging_mode=AveragingMode.CYCLIC,
acquisition_type=AcquisitionType.INTEGRATION,
)

results = platform.sweep(sequence, options, sweeper)

In few seconds, the experiment will be finished and we can proceed to plot it.

.. testcode:: python

import matplotlib.pyplot as plt

amplitudes = results[readout_pulse.serial].magnitude
frequencies = np.arange(-2e8, +2e8, 1e6) + readout_pulse.frequency

plt.title("Resonator Spectroscopy")
plt.xlabel("Frequencies [Hz]")
plt.ylabel("Amplitudes [a.u.]")

plt.plot(frequencies, amplitudes)
plt.show()
.. literalinclude:: ./includes/calibration/calibration0.py

.. image:: resonator_spectroscopy_light.svg
:class: only-light
Expand All @@ -105,64 +50,7 @@ typical qubit spectroscopy experiment is as follows:
So, mainly, the difference that this experiment introduces is a slightly more
complex pulse sequence. Therefore with start with that:

.. testcode:: python

import numpy as np
import matplotlib.pyplot as plt
from qibolab import create_platform
from qibolab.pulses import PulseSequence
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
AcquisitionType,
)

# allocate platform
platform = create_platform("dummy")

# create pulse sequence and add pulses
sequence = PulseSequence()
drive_pulse = platform.create_RX_pulse(qubit=0, start=0)
drive_pulse.duration = 2000
drive_pulse.amplitude = 0.01
readout_pulse = platform.create_MZ_pulse(qubit=0, start=drive_pulse.finish)
sequence.add(drive_pulse)
sequence.add(readout_pulse)

# allocate frequency sweeper
sweeper = Sweeper(
parameter=Parameter.frequency,
values=np.arange(-2e8, +2e8, 1e6),
pulses=[drive_pulse],
type=SweeperType.OFFSET,
)

Note that the drive pulse has been changed to match the characteristics required
for the experiment.

We can now proceed to launch on hardware:

.. testcode:: python

options = ExecutionParameters(
nshots=1000,
relaxation_time=50,
averaging_mode=AveragingMode.CYCLIC,
acquisition_type=AcquisitionType.INTEGRATION,
)

results = platform.sweep(sequence, options, sweeper)

amplitudes = results[readout_pulse.serial].magnitude
frequencies = np.arange(-2e8, +2e8, 1e6) + drive_pulse.frequency

plt.title("Resonator Spectroscopy")
plt.xlabel("Frequencies [Hz]")
plt.ylabel("Amplitudes [a.u.]")

plt.plot(frequencies, amplitudes)
plt.show()
.. literalinclude:: ./includes/calibration/calibration1.py

.. image:: qubit_spectroscopy_light.svg
:class: only-light
Expand Down Expand Up @@ -200,58 +88,7 @@ two states.
This experiment serves to assess the effectiveness of single-qubit calibration
and its impact on qubit states in the IQ plane.

.. testcode:: python

import numpy as np
import matplotlib.pyplot as plt
from qibolab import create_platform
from qibolab.pulses import PulseSequence
from qibolab.sweeper import Sweeper, SweeperType, Parameter
from qibolab.execution_parameters import (
ExecutionParameters,
AveragingMode,
AcquisitionType,
)

# allocate platform
platform = create_platform("dummy")

# create pulse sequence 1 and add pulses
one_sequence = PulseSequence()
drive_pulse = platform.create_RX_pulse(qubit=0, start=0)
readout_pulse1 = platform.create_MZ_pulse(qubit=0, start=drive_pulse.finish)
one_sequence.add(drive_pulse)
one_sequence.add(readout_pulse1)

# create pulse sequence 2 and add pulses
zero_sequence = PulseSequence()
readout_pulse2 = platform.create_MZ_pulse(qubit=0, start=0)
zero_sequence.add(readout_pulse2)

options = ExecutionParameters(
nshots=1000,
relaxation_time=50_000,
averaging_mode=AveragingMode.SINGLESHOT,
acquisition_type=AcquisitionType.INTEGRATION,
)

results_one = platform.execute_pulse_sequence(one_sequence, options)
results_zero = platform.execute_pulse_sequence(zero_sequence, options)

plt.title("Single shot classification")
plt.xlabel("I [a.u.]")
plt.ylabel("Q [a.u.]")
plt.scatter(
results_one[readout_pulse1.serial].voltage_i,
results_one[readout_pulse1.serial].voltage_q,
label="One state",
)
plt.scatter(
results_zero[readout_pulse2.serial].voltage_i,
results_zero[readout_pulse2.serial].voltage_q,
label="Zero state",
)
plt.show()
.. literalinclude:: ./includes/calibration/calibration2.py

.. image:: classification_light.svg
:class: only-light
Expand Down
109 changes: 15 additions & 94 deletions doc/source/tutorials/circuits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,123 +3,39 @@
Circuit execution
=================

Qibolab can be used as a ``qibo`` backend for executing executions. The purpose
Qibolab can be used as a ``qibo`` backend for executing circuits. The purpose
of this section is to show how to do it, without entering into the details of
circuits definition that we leave to the `Qibo
<https://qibo.science/qibo/stable/>`_ documentation.

.. testcode:: python

import numpy as np
import qibo
from qibo import Circuit, gates

np.random.seed(0)

# create a single qubit circuit
circuit = Circuit(1)

# attach Hadamard gate and a measurement
circuit.add(gates.GPI2(0, phi=np.pi / 2))
circuit.add(gates.M(0))

# execute on quantum hardware
qibo.set_backend("qibolab", platform="dummy")
hardware_result = circuit(nshots=5000)

# retrieve measured probabilities
freq = hardware_result.frequencies()
p0 = freq["0"] / 5000 if "0" in freq else 0
p1 = freq["1"] / 5000 if "1" in freq else 0
hardware = [p0, p1]

# execute with classical quantum simulation
qibo.set_backend("numpy")
simulation_result = circuit(nshots=5000)

simulation = simulation_result.probabilities(qubits=(0,))

.. literalinclude:: ./includes/circuits/circuits0.py

In this snippet, we first define a single-qubit circuit containing a single Hadamard gate and a measurement.
We then proceed to define the qibo backend as ``qibolab`` using the ``tii1q_b1`` platform.
Finally, we change the backend to ``numpy``, a simulation one, to compare the results with ideality.
After executing the script we can print our results that will appear more or less as:

.. testcode:: python

print(f"Qibolab: P(0) = {hardware[0]:.2f}\tP(1) = {hardware[1]:.2f}")
print(f"Numpy: P(0) = {simulation[0]:.2f}\tP(1) = {simulation[1]:.2f}")

Returns:
At the end of the script we can inpsect our printed our results:

.. testoutput:: python
:options: +NORMALIZE_WHITESPACE

Qibolab: P(0) = 0.49 P(1) = 0.51
Numpy: P(0) = 0.50 P(1) = 0.50

..
TODO: See https://github.com/qiboteam/qibolab/issues/828
and https://github.com/qiboteam/qibolab/pull/807

Clearly, we do not expect the results to be exactly equal due to the non
ideality of current NISQ devices.

.. note::
Qibo circuits and gates are backend agnostic. The same circuit can be executed on multiple backends, including simulation and quantum platforms.
Qibo circuits and gates are backend agnostic. The same circuit can be
executed on multiple backends, including simulation and quantum platforms.

A slightly more complex circuit, a variable rotation, will produce similar
results:

.. testcode:: python

import matplotlib.pyplot as plt
import numpy as np
import qibo
from qibo import Circuit, gates


def execute_rotation():
# create single qubit circuit
circuit = Circuit(1)

# attach Rotation on X-Pauli with angle = 0
circuit.add(gates.GPI2(0, phi=0))
circuit.add(gates.M(0))

# define range of angles from [0, 2pi]
exp_angles = np.arange(0, 2 * np.pi, np.pi / 16)

res = []
for angle in exp_angles:
# update circuit's rotation angle
circuit.set_parameters([angle])

# execute circuit
result = circuit.execute(nshots=4000)
freq = result.frequencies()
p0 = freq['0'] / 4000 if '0' in freq else 0
p1 = freq['1'] / 4000 if '1' in freq else 0

# store probability in state |1>
res.append(p1)

return res


# execute on quantum hardware
qibo.set_backend("qibolab", platform="dummy")
hardware = execute_rotation()

# execute with classical quantum simulation
qibo.set_backend("numpy")
simulation = execute_rotation()

# plot results
exp_angles = np.arange(0, 2 * np.pi, np.pi / 16)
plt.plot(exp_angles, hardware, label="qibolab hardware")
plt.plot(exp_angles, simulation, label="numpy")

plt.legend()
plt.ylabel("P(1)")
plt.xlabel("Rotation [rad]")
plt.show()
.. literalinclude:: ./includes/circuits/circuits1.py

Returns the following plot:

Expand All @@ -137,6 +53,9 @@ QASM Execution

Qibolab also supports the execution of circuits starting from a QASM string. The QASM circuit:

..
TODO: The OpenQASM code below does not correspond to the code above, I think.

.. testcode::

circuit = """// Generated by QIBO 0.2.4
Expand All @@ -158,6 +77,8 @@ can be executed by passing it together with the platform name to the :func:`qibo

result = execute_qasm(circuit, platform="dummy")

The return type of :func:`qibolab.execute_qasm` is `qibo.result.MeasurementOutcome`.
See `Qibo API <https://qibo.science/qibo/stable/api-reference>`_ for more details.

C-API
-----
Expand Down
Loading