qiboteam__qibocal METASEP

doc/source/_static/css/style.css METASEP
.wy-side-nav-search {
    background-color: #6400FF;
}

.wy-nav-top {
    background-color: #6400FF;
}

doc/source/getting-started/runcard.rst METASEP
.. _runcard:

How to write your runcard?
==========================

In ``qibocal`` we adopt a declarative programming paradigm, i.e. the user should specify directly
what he wants to do without caring about the underlying implementation.

This paradigm is implemented in ``qibocal`` in the form of runcards. A runcard will contain all
the essential information to run a specific task.

In the case of the ``qq`` command a possible runcard should look like this:

.. code-block:: yaml

    platform: tii5q

    qubits: [0]

    format: pickle

    actions:
        first_routine:
            arg1: ...
            arg2: ...

        second_routine:
            arg1: ...
            arg2: ...


First, the user will need to specify some global parameters including:
    * ``platform``: the platform name.
    * ``qubits``: the qubit(s) that we are calibrating.
    * ``format``: the format for storing the measurements.

After those the user will simply populate ``actions`` with all the routines
that he will like to run followed by their respective arguments.

doc/source/getting-started/interface.rst METASEP
Interface
=========

In this section we present the different commands implemented in ``qibocal`` and how to use them.

``qq``
^^^^^^
``qq`` is the base command in ``qibocal``. It can be launched from the command line using:

.. code-block::

    qq <runcard> -o <output_folder>

It will run all the calibration routines specified in the ``<runcard>`` file and save all results
in the ``<output_folder>``. The runcard layout is specified in the :ref:`this <runcard>` section.
If no ``<output_folder>`` is specified ``qq`` will automatically create a default folder containing
the current date and the username.


``qq-live``
^^^^^^^^^^^

``qq-live`` is the command dedicated to live-plotting. It can be launched from the command line using:

.. code-block::

    qq-live <output_folder>

where ``<output_folder>``  is the directory where all the measurements are saved after running ``qq``.
``qq-live`` will produce specific plots depending on the calibration routines executed. If the ``qq`` command
is still running ``qq-live`` will instead produce a live-plotting.

``qq-upload``
^^^^^^^^^^^^^

doc/source/getting-started/installation.rst METASEP
Installation instructions
=========================

.. _installing-qibocal:

Qibocal
^^^^^^^

Installing with pip
"""""""""""""""""""

Installing with conda
"""""""""""""""""""""

Installing from source
""""""""""""""""""""""

In order to install ``qibocal`` from source, you can simply clone the GitHub repository
with:

.. code-block::

      git clone https://github.com/qiboteam/qibocal.git
      cd qibocal
      pip install .

You can also use `poetry <https://python-poetry.org/>`_ to install ``qibocal`` from source:

.. code-block::

      git clone https://github.com/qiboteam/qibocal.git
      cd qibocal
      poetry install

doc/source/getting-started/index.rst METASEP
Getting started
===============

In this section we present the basic aspects of the Qibocal design and provide installation instructions.

.. toctree::
    :maxdepth: 1

    installation
    interface
    runcard

doc/source/developer-guides/index.rst METASEP
Developer guides
================

In this section we provide a code overview and contribution guidelines for
developers.

.. toctree::
    :maxdepth: 2

    general
    contributing

doc/source/developer-guides/general.rst METASEP
Code overview
=============

doc/source/developer-guides/contributing.rst METASEP
How to contribute?
==================

If you are looking to contribute make sure to install ``qibocal`` using poetry.

.. code-block::

      git clone https://github.com/qiboteam/qibocal.git
      cd qibocal
      poetry install

We also invite you to use `precommit <https://pre-commit.com/>`_ to enforce automation
and standardize tools for all the developers. Once you have downloaded the ``qibocal`` package
you can install the pre-commit hooks by typing:


.. code-block::

      pre-commit install

inside the installation directory.

doc/source/api-reference/qibocal.rst METASEP
Calibration routines
--------------------

In ``qibocal`` we provide the following calibration routines:


.. automodule:: qibocal.calibrations.characterization.resonator_spectroscopy
   :members:
   :undoc-members:
   :show-inheritance:


.. automodule:: qibocal.calibrations.characterization.qubit_spectroscopy
   :members:
   :undoc-members:
   :show-inheritance:


.. automodule:: qibocal.calibrations.characterization.rabi_oscillations
   :members:
   :undoc-members:
   :show-inheritance:

.. automodule:: qibocal.calibrations.characterization.ramsey
   :members:
   :undoc-members:
   :show-inheritance:

.. automodule:: qibocal.calibrations.characterization.t1
   :members:
   :undoc-members:
   :show-inheritance:

.. automodule:: qibocal.calibrations.characterization.flipping
   :members:
   :undoc-members:
   :show-inheritance:

.. automodule:: qibocal.calibrations.characterization.allXY
   :members:
   :undoc-members:
   :show-inheritance:

.. automodule:: qibocal.calibrations.characterization.calibrate_qubit_states
   :members:
   :undoc-members:
   :show-inheritance:


Utils
^^^^^

.. automodule:: qibocal.calibrations.characterization.utils
   :members:
   :undoc-members:
   :show-inheritance:


Data structure
--------------

In ``qibocal`` all the data are stored using the :class:`qibocal.data.Dataset` which provide
different formats for storing the data including `pickle <https://docs.python.org/3/library/pickle.html>`_
and `csv <https://docs.python.org/3/library/csv.html>`_.

.. autoclass:: qibocal.data.Dataset
    :members:
    :member-order: bysource

doc/source/api-reference/index.rst METASEP
.. _Components:

API reference
=============

In this section we present the main Qibocal components and primitives included in
the public API.

.. toctree::
    :maxdepth: 3

    qibocal

serverscripts/web/index.html METASEP
<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>Qibocal reports</title>
  <link rel="icon" href="data:;base64,iVBORw0KGgo=">

  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
  <link rel="stylesheet" href="styles.css">

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
  <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script>

  <script type="text/javascript">
  $(document).ready(function() {
    $.getJSON("index.json", function(json){
      var data = json['data'];
      var global_table = $('#global').DataTable({
        "data": data,
        "order": [[2, "desc"]],
        "dom": "frtipl"
      });
    });
  });
  </script>
</head>

<body>

  <header class="navbar navbar-dark sticky-top p-0">
    <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="/"><span class="fs-4">Qibocal Reports</span></a>
  </header>

  <div class="container-fluid">

    <main class="col-md-10 mx-sm-auto col-lg-8 px-md-4">
      <div
        class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
        <h2>Uploaded Reports</h2>
      </div>
      <p>
        Please select a report from the table below:
      </p>

      <table id="global" class="table table-striped table-sm">
        <thead>
          <tr>
            <th scope="col">Title</th>
            <th scope="col">Date</th>
            <th scope="col">Platform</th>
            <th scope="col">Start-time (UTC)</th>
            <th scope="col">End-time (UTC)</th>
          </tr>
        </thead>
      </table>

    </main>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
    crossorigin="anonymous"></script>
</body>

</html>

doc/source/index.rst METASEP
.. title::
      Qibocal

What is Qibocal?
================

Qibocal is a python module that implements Quatum Characterization Validation and Verification using
`Qibo <https://github.com/qiboteam/qibo>`_ and `Qibolab <https://github.com/qiboteam/qibolab>`_.

Qibocal key features:

* Automatization of calibration routines.
* Declarative inputs using runcard.
* Generation of a report.

This documentation refers to qibocal |release|.

Contents
========

.. toctree::
    :maxdepth: 2
    :caption: Introduction

    getting-started/index

.. toctree::
    :maxdepth: 2
    :caption: Main documentation

    api-reference/index
    developer-guides/index


Indices and tables
==================

* :ref:`genindex`
* :ref:`search`

doc/source/conf.py METASEP
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys

from recommonmark.transform import AutoStructify

sys.path.insert(0, os.path.abspath(".."))

import qibocal

# -- Project information -----------------------------------------------------

project = "qibocal"
copyright = "2022, The Qibo team"
author = "The Qibo team"

# The full version, including alpha/beta/rc tags
release = qibocal.__version__


# -- General configuration ---------------------------------------------------

# https://stackoverflow.com/questions/56336234/build-fail-sphinx-error-contents-rst-not-found
master_doc = "index"

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.doctest",
    "sphinx.ext.coverage",
    "sphinx.ext.napoleon",
    "sphinx.ext.intersphinx",
    "recommonmark",
    "sphinx_markdown_tables",
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# Markdown configuration

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = {".rst": "restructuredtext", ".txt": "markdown", ".md": "markdown"}

autosectionlabel_prefix_document = True
# Allow to embed rst syntax in  markdown files.
enable_eval_rst = True


# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]

# -- Intersphinx  -------------------------------------------------------------

intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}

# -- Autodoc ------------------------------------------------------------------
#
autodoc_member_order = "bysource"

# Adapted this from
# https://github.com/readthedocs/recommonmark/blob/ddd56e7717e9745f11300059e4268e204138a6b1/docs/conf.py
# app setup hook
def setup(app):
    app.add_config_value("recommonmark_config", {"enable_eval_rst": True}, True)
    app.add_transform(AutoStructify)
    app.add_css_file("css/style.css")

runcards/simulation.yml METASEP
backend: qibojit

qubits: [0, 1, 2]

format: pickle

actions:
    test:
      nshots: 100
      points: 1

runcards/dummy.yml METASEP
#TODO: dummy platform not working!

platform: dummy

qubits: [0]

format: csv

actions:
  resonator_spectroscopy:
    lowres_width: 5_000_000
    lowres_step: 2_000_000
    highres_width: 1_500_000
    highres_step: 200_000
    precision_width: 1_500_000
    precision_step: 100_000
    software_averages: 1
    points: 5

runcards/actions_qq_5q.yml METASEP
platform: tii5q

qubits: [2]

format: csv

actions:
  # resonator_spectroscopy:
  #   lowres_width: 5_000_000
  #   lowres_step: 2_000_000
  #   highres_width: 1_500_000
  #   highres_step: 200_000
  #   precision_width: 1_500_000
  #   precision_step: 100_000
  #   software_averages: 1
  #   points: 1

  # resonator_spectroscopy_flux:
  #   freq_width: 3_000_000
  #   freq_step: 200_000
  #   current_max: +0.000 # absolute max is 40 mA
  #   current_min: -10.e-3 # absolute min is -40 mA
  #   current_step: 3.e-3
  #   fluxline: qubit # Ask alvaro automatic way to obtain fluxline from runcard
  #   software_averages: 1
  #   points: 10

  # resonator_spectroscopy_flux_matrix:
  #   freq_width: 3_000_000
  #   freq_step: 200_000
  #   current_max: +0.000 # absolute max is 40 mA
  #   current_min: -10.e-3 # absolute min is -40 mA
  #   current_step: 3.e-3
  #   fluxlines: [1, 2] # Ask alvaro automatic way to obtain fluxline from runcard
  #   software_averages: 2
  #   points: 10

  # resonator_punchout:
  #   freq_width: 10_000_000
  #   freq_step: 500_000
  #   min_att: 10
  #   max_att: 40
  #   step_att: 10 # attenuation must be a multiple of 2
  #   software_averages: 1
  #   points: 10

  # dispersive_shift:
  #   freq_width: 5_000_000
  #   freq_step: 200_000
  #   software_averages: 1
  #   points: 5

  # qubit_spectroscopy:
  #   fast_start: -50_000_000
  #   fast_end: 50_000_000
  #   fast_step: 500_000
  #   precision_start: -500_000
  #   precision_end: 500_000
  #   precision_step: 100_000
  #   software_averages: 1
  #   points: 10

  # qubit_spectroscopy_flux:
  #   freq_width: 50_000_000
  #   freq_step: 200_000
  #   current_min: -0.0012 # -0.0025 # absolute min is -40 mA
  #   current_max: -0.0004 # +0.001 # absolute max is 40 mA
  #   current_step: 0.00002 # 0.0001
  #   fluxline: 1 # Ask alvaro automatic way to obtain fluxline from runcard
  #   software_averages: 2
  #   points: 10

  rabi_pulse_length:
    pulse_duration_start: 4 # minimum 4ns
    pulse_duration_end: 200
    pulse_duration_step: 4
    software_averages: 1
    points: 10

  # rabi_pulse_gain:
  #   pulse_gain_start: 0 # -1.0<=g<=1.0
  #   pulse_gain_end: 1
  #   pulse_gain_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_amplitude:
  #   pulse_amplitude_start: 0 # 0<=a<=1
  #   pulse_amplitude_end: 1
  #   pulse_amplitude_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_length_and_gain:
  #   pulse_duration_start: 4 # minimum 4ns
  #   pulse_duration_end: 200
  #   pulse_duration_step: 4
  #   pulse_gain_start: 0 # -1.0<=g<=1.0
  #   pulse_gain_end: 1
  #   pulse_gain_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_length_and_amplitude:
  #   pulse_duration_start: 4 # minimum 4ns
  #   pulse_duration_end: 200
  #   pulse_duration_step: 4
  #   pulse_amplitude_start: 0 # -1.0<= amplitude <=1.0
  #   pulse_amplitude_end: 1
  #   pulse_amplitude_step: 0.02
  #   software_averages: 1
  #   points: 10

  # ramsey:
  #   delay_between_pulses_start: 4 # must be a multiple of 4 incl 0
  #   delay_between_pulses_end: 3000
  #   delay_between_pulses_step: 16 # must be a multiple of 4
  #   software_averages: 1
  #   points: 5

  # ramsey_frequency_detuned:
  #   t_start: 4
  #   t_end: [3000] #t_end (optimal) = 3.5 * T2
  #   t_step: 16
  #   n_osc: 4
  #   points: 5 #minimum set to 5 for live fitting

  # t1:
  #   delay_before_readout_start: 4
  #   delay_before_readout_end: 200_000
  #   delay_before_readout_step: 500
  #   software_averages: 1
  #   points: 5

  # flipping:
  #   niter: 60
  #   step: 1
  #   points: 1

  # calibrate_qubit_states:
  #   niter: 1024
  #   points: 1

  # allXY_iteration:
  #   beta_start: -0.09
  #   beta_end: 0.2
  #   beta_step: 0.01
  #   software_averages: 1
  #   points: 1

  # allXY:
  #   beta_param: Null
  #   software_averages: 1
  #   points: 1

  # drag_pulse_tunning:
  #   beta_start: -1
  #   beta_end: 1
  #   beta_step: 0.01
  #   points: 1

runcards/actions_qq_1q.yml METASEP
platform: tii1q

qubits: [0]

format: csv

actions:
  # resonator_spectroscopy:
  #   lowres_width: 5_000_000
  #   lowres_step: 2_000_000
  #   highres_width: 1_500_000
  #   highres_step: 200_000
  #   precision_width: 1_500_000
  #   precision_step: 100_000
  #   software_averages: 1
  #   points: 5

  # resonator_punchout:
  #   freq_width: 10_000_000
  #   freq_step: 200_000
  #   min_att: 0
  #   max_att: 60
  #   step_att: 2 # attenuation must be a multiple of 2
  #   software_averages: 1
  #   points: 10

  # qubit_spectroscopy:
  #   fast_start: -5_000_000
  #   fast_end: 5_000_000
  #   fast_step: 200_000
  #   precision_start: -500_000
  #   precision_end: 500_000
  #   precision_step: 20_000
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_amplitude:
  #   pulse_amplitude_start: 0 # 0<=a<=1
  #   pulse_amplitude_end: 1
  #   pulse_amplitude_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_gain:
  #   pulse_gain_start: 0 # -1.0<=g<=1.0
  #   pulse_gain_end: 1
  #   pulse_gain_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_length_and_amplitude:
  #   pulse_duration_start: 4 # minimum 4ns
  #   pulse_duration_end: 200
  #   pulse_duration_step: 4
  #   pulse_amplitude_start: 0 # -1.0<= amplitude <=1.0
  #   pulse_amplitude_end: 1
  #   pulse_amplitude_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_length_and_gain:
  #   pulse_duration_start: 4 # minimum 4ns
  #   pulse_duration_end: 200
  #   pulse_duration_step: 4
  #   pulse_gain_start: 0 # -1.0<=g<=1.0
  #   pulse_gain_end: 1
  #   pulse_gain_step: 0.02
  #   software_averages: 1
  #   points: 10

  # rabi_pulse_length:
  #   pulse_duration_start: 4 # minimum 4ns
  #   pulse_duration_end: 200
  #   pulse_duration_step: 4
  #   software_averages: 1
  #   points: 10

  # allXY_iteration:
  #   beta_start: -1
  #   beta_end: 1
  #   beta_step: 0.2
  #   software_averages: 1
  #   points: 1

  allXY:
    beta_param: Null
    software_averages: 1
    points: 1

  # calibrate_qubit_states:
  #   niter: 1024
  #   points: 1

  # dispersive_shift:
  #   freq_width: 5_000_000
  #   freq_step: 200_000
  #   software_averages: 1
  #   points: 5

  # flipping:
  #   niter: 10
  #   step: 2

  # ramsey_frequency_detuned:
  #   t_start: 4
  #   t_end: [2000] #t_end (optimal) = 3.5 * T2 (limited by HW to 8000ns)
  #   t_step: 16
  #   n_osc: 4
  #   software_averages: 1
  #   points: 1

  # ramsey:
  #   delay_between_pulses_start: 0 # must be a multiple of 4 incl 0
  #   delay_between_pulses_end: 4000
  #   delay_between_pulses_step: 16 # must be a multiple of 4
  #   software_averages: 1
  #   points: 1

  # RO_matrix:
  #   niter: 10 # set niter = 1024 to collect good statistics

  # ro_pulse_phase:
  #   pulse_phase_start: 0
  #   pulse_phase_end: 6.28318
  #   pulse_phase_step: 0.1
  #   software_averages: 1
  #   points: 10

  # t1:
  #   delay_before_readout_start: 4
  #   delay_before_readout_end: 50_000
  #   delay_before_readout_step: 500
  #   software_averages: 1
  #   points: 5

  # spin_echo:
  #   delay_between_pulses_start: 4
  #   delay_between_pulses_end: 4000
  #   delay_between_pulses_step: 20
  #   software_averages: 1
  #   points: 5

  # spin_echo_3pulses:
  #   delay_between_pulses_start: 4
  #   delay_between_pulses_end: 30000
  #   delay_between_pulses_step: 20
  #   software_averages: 1
  #   points: 5

doc/Makefile METASEP
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS    ?=
SPHINXBUILD   ?= sphinx-build
SOURCEDIR     = source
BUILDDIR      = build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

serverscripts/qibocal-update-on-change.py METASEP
#!/usr/bin/env python
import argparse

import curio
import inotify.adapters
import inotify.constants
from curio import subprocess


async def main(folder, exe_args):
    i = inotify.adapters.Inotify()
    i.add_watch(folder)

    for event in i.event_gen(yield_nones=False):
        if event is not None:
            (header, _, _, _) = event
            if (
                (header.mask & inotify.constants.IN_CREATE)
                or (header.mask & inotify.constants.IN_DELETE)
                or (header.mask & inotify.constants.IN_MODIFY)
            ):
                await subprocess.run(exe_args)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("folder")
    parser.add_argument("exe_args", nargs="+")
    args = parser.parse_args()
    curio.run(main(args.folder, args.exe_args))

serverscripts/qibocal-index-reports.py METASEP
"""qibocal-index-reports.py
Generates a JSON index with reports information.
"""
import json
import pathlib
import sys
from collections import ChainMap

import yaml

ROOT = "/home/users/qibocal/qibocal-reports"
ROOT_URL = "http://login.qrccluster.com:9000/"
OUT = "/home/users/qibocal/qibocal-reports/index.json"
DEFAULTS = {
    "title": "-",
    "date": "-",
    "platform": "-",
    "start-time": "-",
    "end-time": "-",
}
REQUIRED_FILE_METADATA = {"title", "date", "platform", "start-time" "end-time"}


def meta_from_path(p):
    meta = ChainMap(DEFAULTS)
    yaml_meta = p / "meta.yml"
    yaml_res = {}
    if yaml_meta.exists():
        with yaml_meta.open() as f:
            try:
                yaml_res = yaml.safe_load(f)
            except yaml.YAMLError as e:
                print(f"Error processing {yaml_meta}: {e}", file=sys.stderr)
    meta = meta.new_child(yaml_res)
    return meta


def register(p):
    path_meta = meta_from_path(p)
    title, date, platform, start_time, end_time = (
        path_meta["title"],
        path_meta["date"],
        path_meta["platform"],
        path_meta["start-time"],
        path_meta["end-time"],
    )
    url = ROOT_URL + p.name
    titlelink = f'<a href="{url}">{title}</a>'
    return (titlelink, date, platform, start_time, end_time)


def make_index():
    root_path = pathlib.Path(ROOT)
    data = []
    for p in root_path.iterdir():
        if p.is_dir():
            try:
                res = register(p)
                data.append(res)
            except:
                print("Error processing folder", p, file=sys.stderr)
                raise

    with open(OUT, "w") as f:
        json.dump({"data": data}, f)


if __name__ == "__main__":
    make_index()

src/qibocal/web/templates/template.html METASEP
<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  {% if report %}
  <title>{{ report.title }}</title>
  {% else %}
  <title>Qibocal {{version}}</title>
  {% endif %}

  <link rel="icon" type="image/x-icon" href="/_favicon.ico?v=2.6.0">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">

  {% if is_static %}
  <script type="text/javascript">window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
  <!-- Reading the plotly js library from online to save space (not the latest version) -->
  <!-- This is required for the report plots to remain interactive -->
  <script type="text/javascript" src="https://cdn.plot.ly/plotly-latest.min.js"></script>
  {{ css_styles }}
  {% else %}
  <link rel="stylesheet" href="/static/styles.css">
  {% endif %}

</head>

<body>

  <header class="navbar navbar-dark sticky-top p-0">
    <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="/"><span class="fs-4">Qibocal Reports</span></a>
    <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse"
      data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
  </header>

  <div class="container-fluid">

    <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
      <div class="position-sticky pt-4 p-2 sidebar-sticky">
        <ul class="list-unstyled ps-0">
          {% if report %}
          <li class="mb-1">
            <buttom class="btn btn-toggle d-inline-flex align-items-center rounded border-0" data-bs-toggle="collapse"
              data-bs-target="#home-collapse" aria-expanded="true">
              Home
            </buttom>
            <div class="collapse show" id="home-collapse">
              <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
                <li><a href="#" class="link-dark d-inline-flex text-decoration-none rounded">
                    Timestamp</a></li>
                <li><a href="#summary" class="link-dark d-inline-flex text-decoration-none rounded">
                    Summary</a></li>
              </ul>
            </div>
          </li>
          <li class="mb-1">
            <button class="btn btn-toggle d-inline-flex align-items-center rounded border-0" data-bs-toggle="collapse"
              data-bs-target="#actions-collapse" aria-expanded="true">
              Actions
            </button>
            <div class="collapse show" id="actions-collapse">
              {% for routine in report.routines %}
              <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
                <li><a href="#{{ routine }}" class="link-dark d-inline-flex text-decoration-none rounded">{{
                    report.get_routine_name(routine) }}</a></li>
                {% for header, method in routine.plots %}
                <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
                  <li><a class="link-dark d-inline-flex text-decoration-none rounded"
                      href="#{{ routine }}-{{ method }}">- {{ header }}</a></li>
                {% endfor %}
                </ul>
              </ul>
              {% endfor %}
            </div>
          </li>
          <li class="border-top my-3"></li>
          {% endif %}
          {% if not is_static %}
          <li class="mb-1">
            <button class="btn btn-toggle d-inline-flex align-items-center rounded border-0 collapsed"
              data-bs-toggle="collapse" data-bs-target="#saved-reports" aria-expanded="true">
              Saved reports
            </button>
            <div class="collapse show list-group" id="saved-reports">
              <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
                {% for folder in folders %}
                <li><a id="reports" href="{{ url_for('page', path=folder) }}"
                    class="link-dark d-inline-flex text-decoration-none rounded list-group-item {{ 'active' if folder == report.path else '' }}">{{ folder }}</a></li>
                {% endfor %}
              </ul>
            </div>
          </li>
          {% endif %}
        </ul>

      </div>

    </nav>

    <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">

      {% if report.path %}
      <div
        class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
        <h1>{{ report.title }}</h1>
      </div>
      <p>
        Platform: {{ report.metadata.get('platform')}}<br>
        Run date: {{ report.metadata.get('date') }}<br>
        Start time (UTC): {{ report.metadata.get('start-time') }}<br>
        End time (UTC): {{ report.metadata.get('end-time') }}
      </p>
      <h3 id="summary" style="scroll-margin-top: 2.5em;"
        class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
        Summary</h3>

      <p>In the table below we show the libraries and respective versions used in {{ report.title }}.</p>

      <div class="table-responsive col-md-5 col-lg-3">
        <table class="table table-striped table-sm">
          <thead>
            <tr>
              <th scope="col">Library</th>
              <th scope="col">Version</th>
            </tr>
          </thead>
          <tbody>
            {% for library, version in report.metadata.get('versions').items() %}
            <tr>
              <td>{{ library }}</td>
              <td>{{ version }}</td>
            </tr>
            {% endfor %}
          </tbody>
        </table>
      </div>

      <h3 id="actions"
        class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
        Actions</h3>
      <p>Please find below data generated by actions:</p>

      {% for routine in report.routines %}
      <div id="{{ routine }}" style="scroll-margin-top: 4em;">
        <h4
          class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
          {{ report.get_routine_name(routine) }}</h4>
        {% if routine.plots %}
        {% for header, method in routine.plots %}
        <div id="{{ routine }}-{{ method }}" style="scroll-margin-top: 4em;">
          {% for qubit in report.qubits %}
          <div id="{{ routine }}-{{ method }}-{{ qubit }}" style="scroll-margin-top: 4em;">
          <h5
            class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
            {{ header }} - Qubit {{ qubit }}</h5>
          {% if is_static %}
            {{ report.get_figure(routine, method, qubit) }}
          {% else %}
          <!-- find a way to change height and width depending on screen type -->
          <iframe src="{{ report.get_live_figure(routine, method, qubit) }}"
            style="position: relative; height: 500px; width: 100%;"></iframe>
          {% endif %}
          </div>
          {% endfor %}
        </div>
        {% endfor %}
        {% else %}
        <p>No plots available for {{ routine.__name__ }}.</p>
        {% endif %}
      </div>
      {% endfor %}

      {% else %}
      <div
        class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
        <h2>Welcome to Qibocal live!</h2>
      </div>
      <p>
        Please select a report from the <mark>Saved reports</mark> in the sidebar .
      </p>
      {% endif %}
    </main>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
    crossorigin="anonymous"></script>
</body>

</html>

src/qibocal/web/static/styles.css METASEP
/*
 * Sidebar
 */

.sidebar {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
  /* Behind the navbar */
  padding: 48px 0 0;
  /* Height of navbar */
  box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}

.sidebar-sticky {
  height: calc(100vh - 48px);
  overflow-x: hidden;
  overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}

.list-group-item.active {
  background-color: #6600ff1c;
  border-color: #6600ff1c;
}

.list-group-item {
  background-color: inherit;
  border-width: 0px;
}

/*
 * Navbar
 */

.navbar {
  background-color: #6400FF;
}

.navbar-brand {
  padding-top: .75rem;
  padding-bottom: .75rem;
}

.navbar .navbar-toggler {
  right: 1rem;
}

/*
 * Side bar buttom menu
 */

.btn-toggle {
  padding: .25rem .5rem;
  font-weight: 600;
  color: rgba(0, 0, 0, .65);
  background-color: transparent;
}

.btn-toggle:hover,
.btn-toggle:focus {
  color: rgba(0, 0, 0, .85);
  background-color: #6600ff1c;
}

.btn-toggle::before {
  width: 1.25em;
  line-height: 0;
  content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
  transition: transform .35s ease;
  transform-origin: .5em 50%;
}

.btn-toggle[aria-expanded="true"] {
  color: rgba(0, 0, 0, .85);
}

.btn-toggle[aria-expanded="true"]::before {
  transform: rotate(90deg);
}

.btn-toggle-nav a {
  padding: .1875rem .5rem;
  margin-top: .125rem;
  margin-left: 1.25rem;
}

.btn-toggle-nav a:hover,
.btn-toggle-nav a:focus {
  background-color: #6600ff1c;
}

pyproject.toml METASEP
[tool.poetry]
name = "qibocal"
version = "0.0.1.dev0"
description = ""
authors = ["andrea-pasquale <andreapasquale97@gmail.com>"]
license = "Apache License 2.0"
readme = "README.md"
homepage = ""
repository = "https://github.com/qiboteam/qibocal/"
documentation = ""
keywords = []
classifiers=[
    "Programming Language :: Python :: 3",
    "Topic :: Scientific/Engineering :: Physics",
    ]

[tool.poetry.dependencies]
python = ">=3.8, <4"
qibo = {git = "https://github.com/qiboteam/qibo.git", rev = "master"}
qibolab = {git = "https://github.com/qiboteam/qibolab.git", rev = "main"}
pandas = "^1.4.3"
Pint-Pandas = {git = "https://github.com/hgrecco/pint-pandas.git", rev = "master"}
dash = "^2.6.0"
lmfit = "^1.0.3"
# docs dependencies (for readthedocs, https://github.com/readthedocs/readthedocs.org/issues/4912#issuecomment-664002569)
Sphinx = { version = "^4.3.2", optional = true }
sphinx-rtd-theme = { version = "^1.0.0", optional = true }
sphinxcontrib-bibtex = { version = "^2.4.1", optional = true }
recommonmark = { version = "^0.7.1", optional = true }
sphinx_markdown_tables = { version = "^0.0.17", optional = true }

[tool.poetry.dev-dependencies]
black = "^22.6.0"
pylint = "2.12.2"
pre-commit = "^2.20.0"
pytest = "^7.1.2"
pytest-cov = "^3.0.0"
poethepoet = "^0.16.0"
Sphinx = "^4.3.2"
sphinx-rtd-theme = "^1.0.0"
sphinxcontrib-bibtex = "^2.4.1"
recommonmark = "^0.7.1"
sphinx_markdown_tables = "^0.0.17"

[tool.poetry.extras]
docs = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-bibtex", "recommonmark", "sphinx_markdown_tables"]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
qq = "qibocal:command"
qq-live = "qibocal:live_plot"
qq-upload = "qibocal:upload"

[tool.poe.tasks]
test = "pytest src/qibocal/tests"
lint = "pylint src/**/*.py -E"
lint-warnings = "pylint src/**/*.py --exit-zero"
docs = "make -C doc html"
docs-clean = "make -C doc clean"

[tool.pytest.ini_options]
testpaths = ['src/qibocal/tests/']
addopts = [
    '--cov=qibocal',
    '--cov-report=xml'
]

[tool.pylint.master]
# extensions not to check
extension-pkg-whitelist = ["numpy"]
jobs = 1

[tool.pylint.messages_control]
disable = ["invalid-name", "fixme"]

[tool.pylint.reports]
# Available formats are:
# text, parseable, colorized, json and msvs (visual studio).
output-format = "colorized"

[tool.pylint.format]
# Maximum number of characters on a single line.
max-line-length = 100

[tool.pylint.design]
# Maximum number of arguments for function / method
max-args = 10

poetry.lock METASEP
[[package]]
name = "alabaster"
version = "0.7.12"
description = "A configurable sidebar-enabled Sphinx theme"
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "asteval"
version = "0.9.27"
description = "Safe, minimalistic evaluator of python expression using ast module"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
all = ["Sphinx", "build", "coverage", "pytest", "pytest-cov", "twine"]
dev = ["build", "twine"]
doc = ["Sphinx"]
test = ["coverage", "pytest", "pytest-cov"]

[[package]]
name = "astroid"
version = "2.9.3"
description = "An abstract syntax tree for Python with inference support."
category = "dev"
optional = false
python-versions = ">=3.6.2"

[package.dependencies]
lazy-object-proxy = ">=1.4.0"
setuptools = ">=20.0"
typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
wrapt = ">=1.11,<1.14"

[[package]]
name = "attrs"
version = "22.1.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]

[[package]]
name = "babel"
version = "2.11.0"
description = "Internationalization utilities"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
pytz = ">=2015.7"

[[package]]
name = "black"
version = "22.10.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}

[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]

[[package]]
name = "brotli"
version = "1.0.9"
description = "Python bindings for the Brotli compression library"
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "certifi"
version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"

[[package]]
name = "cfgv"
version = "3.3.1"
description = "Validate configuration and produce human readable error messages."
category = "dev"
optional = false
python-versions = ">=3.6.1"

[[package]]
name = "charset-normalizer"
version = "2.1.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.6.0"

[package.extras]
unicode-backport = ["unicodedata2"]

[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}

[[package]]
name = "cma"
version = "3.2.2"
description = "CMA-ES, Covariance Matrix Adaptation Evolution Strategy for non-linear numerical optimization in Python"
category = "main"
optional = false
python-versions = "*"

[package.dependencies]
numpy = "*"

[package.extras]
constrained-solution-tracking = ["moarchiving"]
plotting = ["matplotlib"]

[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"

[[package]]
name = "commonmark"
version = "0.9.1"
description = "Python parser for the CommonMark Markdown spec"
category = "main"
optional = false
python-versions = "*"

[package.extras]
test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]

[[package]]
name = "contourpy"
version = "1.0.6"
description = "Python library for calculating contours of 2D quadrilateral grids"
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
numpy = ">=1.16"

[package.extras]
bokeh = ["bokeh", "selenium"]
docs = ["docutils (<0.18)", "sphinx (<=5.2.0)", "sphinx-rtd-theme"]
test = ["Pillow", "flake8", "isort", "matplotlib", "pytest"]
test-minimal = ["pytest"]
test-no-codebase = ["Pillow", "matplotlib", "pytest"]

[[package]]
name = "coverage"
version = "6.5.0"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}

[package.extras]
toml = ["tomli"]

[[package]]
name = "cycler"
version = "0.11.0"
description = "Composable style cycles"
category = "main"
optional = false
python-versions = ">=3.6"

[[package]]
name = "dash"
version = "2.6.2"
description = "A Python framework for building reactive web-apps. Developed by Plotly."
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
dash-core-components = "2.0.0"
dash-html-components = "2.0.0"
dash-table = "5.0.0"
Flask = ">=1.0.4"
flask-compress = "*"
plotly = ">=5.0.0"

[package.extras]
celery = ["celery[redis] (>=5.1.2)", "redis (>=3.5.3)"]
ci = ["black (==21.6b0)", "black (==22.3.0)", "dash-dangerously-set-inner-html", "dash-flow-example (==0.0.5)", "flake8 (==3.9.2)", "flaky (==3.7.0)", "flask-talisman (==1.0.0)", "isort (==4.3.21)", "mimesis", "mock (==4.0.3)", "numpy", "openpyxl", "orjson (==3.5.4)", "orjson (==3.6.7)", "pandas (==1.1.5)", "pandas (>=1.4.0)", "preconditions", "pyarrow", "pyarrow (<3)", "pylint (==2.13.5)", "pytest-mock", "pytest-rerunfailures", "pytest-sugar (==0.9.4)", "xlrd (<2)", "xlrd (>=2.0.1)"]
dev = ["PyYAML (>=5.4.1)", "coloredlogs (>=15.0.1)", "fire (>=0.4.0)"]
diskcache = ["diskcache (>=5.2.1)", "multiprocess (>=0.70.12)", "psutil (>=5.8.0)"]
testing = ["beautifulsoup4 (>=4.8.2)", "cryptography (<3.4)", "lxml (>=4.6.2)", "multiprocess (>=0.70.12)", "percy (>=2.0.2)", "psutil (>=5.8.0)", "pytest (>=6.0.2)", "requests[security] (>=2.21.0)", "selenium (>=3.141.0,<=4.2.0)", "waitress (>=1.4.4)"]

[[package]]
name = "dash-core-components"
version = "2.0.0"
description = "Core component suite for Dash"
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "dash-html-components"
version = "2.0.0"
description = "Vanilla HTML components for Dash"
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "dash-table"
version = "5.0.0"
description = "Dash table"
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "distlib"
version = "0.3.6"
description = "Distribution utilities"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "docutils"
version = "0.17.1"
description = "Docutils -- Python Documentation Utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[[package]]
name = "exceptiongroup"
version = "1.0.0"
description = "Backport of PEP 654 (exception groups)"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
test = ["pytest (>=6)"]

[[package]]
name = "filelock"
version = "3.8.0"
description = "A platform independent file lock."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]

[[package]]
name = "flask"
version = "2.2.2"
description = "A simple framework for building complex web applications."
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
click = ">=8.0"
importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""}
itsdangerous = ">=2.0"
Jinja2 = ">=3.0"
Werkzeug = ">=2.2.2"

[package.extras]
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]

[[package]]
name = "flask-compress"
version = "1.13"
description = "Compress responses in your Flask app with gzip, deflate or brotli."
category = "main"
optional = false
python-versions = "*"

[package.dependencies]
brotli = "*"
flask = "*"

[[package]]
name = "fonttools"
version = "4.38.0"
description = "Tools to manipulate font files"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=14.0.0)", "xattr", "zopfli (>=0.1.4)"]
graphite = ["lz4 (>=1.7.4.2)"]
interpolatable = ["munkres", "scipy"]
lxml = ["lxml (>=4.0,<5)"]
pathops = ["skia-pathops (>=0.5.0)"]
plot = ["matplotlib"]
repacker = ["uharfbuzz (>=0.23.0)"]
symfont = ["sympy"]
type1 = ["xattr"]
ufo = ["fs (>=2.2.0,<3)"]
unicode = ["unicodedata2 (>=14.0.0)"]
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]

[[package]]
name = "future"
version = "0.18.2"
description = "Clean single-source support for Python 3 and 2"
category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"

[[package]]
name = "identify"
version = "2.5.8"
description = "File identification library for Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
license = ["ukkonen"]

[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"

[[package]]
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "importlib-metadata"
version = "5.0.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
zipp = ">=0.5"

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
perf = ["ipython"]
testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]

[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "isort"
version = "5.10.1"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6.1,<4.0"

[package.extras]
colors = ["colorama (>=0.4.3,<0.5.0)"]
pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]

[[package]]
name = "itsdangerous"
version = "2.1.2"
description = "Safely pass data to untrusted environments and back."
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
MarkupSafe = ">=2.0"

[package.extras]
i18n = ["Babel (>=2.7)"]

[[package]]
name = "joblib"
version = "1.2.0"
description = "Lightweight pipelining with Python functions"
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "kiwisolver"
version = "1.4.4"
description = "A fast implementation of the Cassowary constraint solver"
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "latexcodec"
version = "2.0.1"
description = "A lexer and codec to work with LaTeX code in Python."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.dependencies]
six = ">=1.4.1"

[[package]]
name = "lazy-object-proxy"
version = "1.8.0"
description = "A fast and thorough lazy object proxy."
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "lmfit"
version = "1.0.3"
description = "Least-Squares Minimization with Bounds and Constraints"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
asteval = ">=0.9.22"
numpy = ">=1.18"
scipy = ">=1.4"
uncertainties = ">=3.0.1"

[[package]]
name = "markdown"
version = "3.4.1"
description = "Python implementation of Markdown."
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}

[package.extras]
testing = ["coverage", "pyyaml"]

[[package]]
name = "markupsafe"
version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "matplotlib"
version = "3.6.1"
description = "Python plotting package"
category = "main"
optional = false
python-versions = ">=3.8"

[package.dependencies]
contourpy = ">=1.0.1"
cycler = ">=0.10"
fonttools = ">=4.22.0"
kiwisolver = ">=1.0.1"
numpy = ">=1.19"
packaging = ">=20.0"
pillow = ">=6.2.0"
pyparsing = ">=2.2.1"
python-dateutil = ">=2.7"
setuptools_scm = ">=7"

[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "mpmath"
version = "1.2.1"
description = "Python library for arbitrary-precision floating-point arithmetic"
category = "main"
optional = false
python-versions = "*"

[package.extras]
develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"]
tests = ["pytest (>=4.6)"]

[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "nodeenv"
version = "1.7.0"
description = "Node.js virtual environment builder"
category = "dev"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"

[package.dependencies]
setuptools = "*"

[[package]]
name = "numpy"
version = "1.23.4"
description = "NumPy is the fundamental package for array computing with Python."
category = "main"
optional = false
python-versions = ">=3.8"

[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"

[[package]]
name = "pandas"
version = "1.5.1"
description = "Powerful data structures for data analysis, time series, and statistics"
category = "main"
optional = false
python-versions = ">=3.8"

[package.dependencies]
numpy = [
    {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
    {version = ">=1.20.3", markers = "python_version < \"3.10\""},
]
python-dateutil = ">=2.8.1"
pytz = ">=2020.1"

[package.extras]
test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]

[[package]]
name = "pastel"
version = "0.2.1"
description = "Bring colors to your terminal."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "pathspec"
version = "0.10.1"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "pillow"
version = "9.3.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]

[[package]]
name = "pint"
version = "0.20.1"
description = "Physical quantities module"
category = "main"
optional = false
python-versions = ">=3.8"

[package.extras]
babel = ["babel (<=2.8)"]
dask = ["dask"]
numpy = ["numpy (>=1.19.5)"]
pandas = ["pint-pandas (>=0.3)"]
test = ["pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"]
uncertainties = ["uncertainties (>=3.1.6)"]
xarray = ["xarray"]

[[package]]
name = "Pint-Pandas"
version = "0.3.dev121+ge7584d5"
description = "Physical quantities module"
category = "main"
optional = false
python-versions = ">=3.8"
develop = false

[package.dependencies]
pandas = ">=1.5"
pint = ">=0.20.1"

[package.extras]
test = ["codecov", "coveralls", "nbval", "pyarrow", "pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"]

[package.source]
type = "git"
url = "https://github.com/hgrecco/pint-pandas.git"
reference = "master"
resolved_reference = "e7584d5bbdeb1e87a8b4c3b60386c95e9da38bc2"

[[package]]
name = "platformdirs"
version = "2.5.2"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]

[[package]]
name = "plotly"
version = "5.11.0"
description = "An open-source, interactive data visualization library for Python"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
tenacity = ">=6.2.0"

[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]

[[package]]
name = "poethepoet"
version = "0.16.4"
description = "A task runner that works well with poetry."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
pastel = ">=0.2.1,<0.3.0"
tomli = ">=1.2.2"

[package.extras]
poetry-plugin = ["poetry (>=1.0,<2.0)"]

[[package]]
name = "pre-commit"
version = "2.20.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
toml = "*"
virtualenv = ">=20.0.8"

[[package]]
name = "psutil"
version = "5.9.3"
description = "Cross-platform lib for process and system monitoring in Python."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.extras]
test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]

[[package]]
name = "pybtex"
version = "0.24.0"
description = "A BibTeX-compatible bibliography processor in Python"
category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*"

[package.dependencies]
latexcodec = ">=1.0.4"
PyYAML = ">=3.01"
six = "*"

[package.extras]
test = ["pytest"]

[[package]]
name = "pybtex-docutils"
version = "1.0.2"
description = "A docutils backend for pybtex."
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
docutils = ">=0.8"
pybtex = ">=0.16"

[[package]]
name = "pygments"
version = "2.13.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.6"

[package.extras]
plugins = ["importlib-metadata"]

[[package]]
name = "pylint"
version = "2.12.2"
description = "python code static checker"
category = "dev"
optional = false
python-versions = ">=3.6.2"

[package.dependencies]
astroid = ">=2.9.0,<2.10"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
isort = ">=4.2.5,<6"
mccabe = ">=0.6,<0.7"
platformdirs = ">=2.2.0"
toml = ">=0.9.2"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}

[[package]]
name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "main"
optional = false
python-versions = ">=3.6.8"

[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]

[[package]]
name = "pytest"
version = "7.2.0"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}

[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]

[[package]]
name = "pytest-cov"
version = "3.0.0"
description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"

[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]

[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"

[package.dependencies]
six = ">=1.5"

[[package]]
name = "pytz"
version = "2022.6"
description = "World timezone definitions, modern and historical"
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "pyyaml"
version = "6.0"
description = "YAML parser and emitter for Python"
category = "main"
optional = false
python-versions = ">=3.6"

[[package]]
name = "qibo"
version = "0.1.9.dev0"
description = "A framework for quantum computing with hardware acceleration."
category = "main"
optional = false
python-versions = ">=3.7.0"
develop = false

[package.dependencies]
cma = "*"
importlib_metadata = "*"
joblib = "*"
matplotlib = "*"
psutil = "*"
pyyaml = "*"
scipy = "*"
sympy = "*"
tabulate = "*"

[package.extras]
docs = ["IPython", "nbsphinx", "recommonmark", "sphinx", "sphinx_markdown_tables", "sphinx_rtd_theme", "sphinxcontrib-bibtex"]
tests = ["cirq", "dill", "ply", "pre-commit", "pytest", "sklearn"]

[package.source]
type = "git"
url = "https://github.com/qiboteam/qibo.git"
reference = "master"
resolved_reference = "833ab405986536e67f124cdbf3bb876e7203e501"

[[package]]
name = "qibolab"
version = "0.0.1.dev6"
description = "Quantum hardware module and drivers for Qibo"
category = "main"
optional = false
python-versions = ">=3.8.0"
develop = false

[package.dependencies]
qibo = ">=0.1.8"

[package.extras]
docs = ["IPython", "nbsphinx", "recommonmark", "sphinx", "sphinx_markdown_tables", "sphinx_rtd_theme", "sphinxcontrib-bibtex"]
tiiq = ["qblox-instruments (==0.6.1)", "qcodes"]

[package.source]
type = "git"
url = "https://github.com/qiboteam/qibolab.git"
reference = "main"
resolved_reference = "a2f2e2012bba450b4fb5c0dbbd8e2f3fba4279d0"

[[package]]
name = "recommonmark"
version = "0.7.1"
description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects."
category = "main"
optional = false
python-versions = "*"

[package.dependencies]
commonmark = ">=0.8.1"
docutils = ">=0.11"
sphinx = ">=1.3.1"

[[package]]
name = "requests"
version = "2.28.1"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7, <4"

[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<3"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"

[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]

[[package]]
name = "scipy"
version = "1.9.3"
description = "Fundamental algorithms for scientific computing in Python"
category = "main"
optional = false
python-versions = ">=3.8"

[package.dependencies]
numpy = ">=1.18.5,<1.26.0"

[package.extras]
dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"]
doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"]
test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]

[[package]]
name = "setuptools"
version = "65.5.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]

[[package]]
name = "setuptools-scm"
version = "7.0.5"
description = "the blessed package to manage your versions by scm tags"
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
packaging = ">=20.0"
setuptools = "*"
tomli = ">=1.0.0"
typing-extensions = "*"

[package.extras]
test = ["pytest (>=6.2)", "virtualenv (>20)"]
toml = ["setuptools (>=42)"]

[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"

[[package]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
category = "main"
optional = false
python-versions = "*"

[[package]]
name = "sphinx"
version = "4.5.0"
description = "Python documentation generator"
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
alabaster = ">=0.7,<0.8"
babel = ">=1.3"
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
docutils = ">=0.14,<0.18"
imagesize = "*"
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
Jinja2 = ">=2.3"
packaging = "*"
Pygments = ">=2.0"
requests = ">=2.5.0"
snowballstemmer = ">=1.1"
sphinxcontrib-applehelp = "*"
sphinxcontrib-devhelp = "*"
sphinxcontrib-htmlhelp = ">=2.0.0"
sphinxcontrib-jsmath = "*"
sphinxcontrib-qthelp = "*"
sphinxcontrib-serializinghtml = ">=1.1.5"

[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"]
test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"]

[[package]]
name = "sphinx-markdown-tables"
version = "0.0.17"
description = "A Sphinx extension for rendering tables written in markdown"
category = "main"
optional = false
python-versions = "*"

[package.dependencies]
markdown = ">=3.4"

[[package]]
name = "sphinx-rtd-theme"
version = "1.1.0"
description = "Read the Docs theme for Sphinx"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"

[package.dependencies]
docutils = "<0.18"
sphinx = ">=1.6,<6"

[package.extras]
dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"]

[[package]]
name = "sphinxcontrib-applehelp"
version = "1.0.2"
description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
category = "main"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]

[[package]]
name = "sphinxcontrib-bibtex"
version = "2.5.0"
description = "Sphinx extension for BibTeX style citations."
category = "main"
optional = false
python-versions = ">=3.6"

[package.dependencies]
docutils = ">=0.8"
importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
pybtex = ">=0.24"
pybtex-docutils = ">=1.0.0"
Sphinx = ">=2.1"

[[package]]
name = "sphinxcontrib-devhelp"
version = "1.0.2"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
category = "main"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]

[[package]]
name = "sphinxcontrib-htmlhelp"
version = "2.0.0"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
category = "main"
optional = false
python-versions = ">=3.6"

[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["html5lib", "pytest"]

[[package]]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
category = "main"
optional = false
python-versions = ">=3.5"

[package.extras]
test = ["flake8", "mypy", "pytest"]

[[package]]
name = "sphinxcontrib-qthelp"
version = "1.0.3"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
category = "main"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]

[[package]]
name = "sphinxcontrib-serializinghtml"
version = "1.1.5"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
category = "main"
optional = false
python-versions = ">=3.5"

[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]

[[package]]
name = "sympy"
version = "1.11.1"
description = "Computer algebra system (CAS) in Python"
category = "main"
optional = false
python-versions = ">=3.8"

[package.dependencies]
mpmath = ">=0.19"

[[package]]
name = "tabulate"
version = "0.9.0"
description = "Pretty-print tabular data"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
widechars = ["wcwidth"]

[[package]]
name = "tenacity"
version = "8.1.0"
description = "Retry code until it succeeds"
category = "main"
optional = false
python-versions = ">=3.6"

[package.extras]
doc = ["reno", "sphinx", "tornado (>=4.5)"]

[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"

[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "typing-extensions"
version = "4.4.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "uncertainties"
version = "3.1.7"
description = "Transparent calculations with uncertainties on the quantities involved (aka error propagation); fast calculation of derivatives"
category = "main"
optional = false
python-versions = "*"

[package.dependencies]
future = "*"

[package.extras]
all = ["nose", "numpy", "sphinx"]
docs = ["sphinx"]
optional = ["numpy"]
tests = ["nose", "numpy"]

[[package]]
name = "urllib3"
version = "1.26.12"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"

[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]

[[package]]
name = "virtualenv"
version = "20.16.6"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
distlib = ">=0.3.6,<1"
filelock = ">=3.4.1,<4"
platformdirs = ">=2.4,<3"

[package.extras]
docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"]
testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]

[[package]]
name = "werkzeug"
version = "2.2.2"
description = "The comprehensive WSGI web application library."
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
MarkupSafe = ">=2.1.1"

[package.extras]
watchdog = ["watchdog"]

[[package]]
name = "wrapt"
version = "1.13.3"
description = "Module for decorators, wrappers and monkey patching."
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"

[[package]]
name = "zipp"
version = "3.10.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]

[extras]
docs = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-bibtex", "recommonmark", "sphinx_markdown_tables"]

[metadata]
lock-version = "1.1"
python-versions = ">=3.8, <4"
content-hash = "340000a2abf2e0ac4f5215872243f1462229c79e6c7f7a368bed574ee35988e8"

[metadata.files]
alabaster = [
    {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
    {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
]
asteval = [
    {file = "asteval-0.9.27-py3-none-any.whl", hash = "sha256:c31678db0e1c758a47d776c693a63709eb898f8af8065eb6201c65d68c3f7a3f"},
    {file = "asteval-0.9.27.tar.gz", hash = "sha256:de838c33aed4c9bb25737eadbb7d1edaaf875e2bab505cc079f1a4b35de03e47"},
]
astroid = [
    {file = "astroid-2.9.3-py3-none-any.whl", hash = "sha256:506daabe5edffb7e696ad82483ad0228245a9742ed7d2d8c9cdb31537decf9f6"},
    {file = "astroid-2.9.3.tar.gz", hash = "sha256:1efdf4e867d4d8ba4a9f6cf9ce07cd182c4c41de77f23814feb27ca93ca9d877"},
]
attrs = [
    {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
    {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
babel = [
    {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"},
    {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"},
]
black = [
    {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
    {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
    {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
    {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"},
    {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"},
    {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
    {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"},
    {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"},
    {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"},
    {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"},
    {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"},
    {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"},
    {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
    {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"},
    {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"},
    {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"},
    {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"},
    {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"},
    {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"},
    {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
    {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
brotli = [
    {file = "Brotli-1.0.9-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70"},
    {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b"},
    {file = "Brotli-1.0.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6"},
    {file = "Brotli-1.0.9-cp27-cp27m-win32.whl", hash = "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa"},
    {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452"},
    {file = "Brotli-1.0.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7"},
    {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031"},
    {file = "Brotli-1.0.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43"},
    {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"},
    {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"},
    {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"},
    {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"},
    {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"},
    {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"},
    {file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"},
    {file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"},
    {file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"},
    {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"},
    {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"},
    {file = "Brotli-1.0.9-cp35-cp35m-win32.whl", hash = "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1"},
    {file = "Brotli-1.0.9-cp35-cp35m-win_amd64.whl", hash = "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea"},
    {file = "Brotli-1.0.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f"},
    {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"},
    {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"},
    {file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"},
    {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"},
    {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"},
    {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"},
    {file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"},
    {file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"},
    {file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"},
    {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"},
    {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"},
    {file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"},
    {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"},
    {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"},
    {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"},
    {file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"},
    {file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"},
    {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"},
    {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8"},
    {file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"},
    {file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"},
    {file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"},
    {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"},
    {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"},
    {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"},
    {file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"},
    {file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"},
    {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"},
    {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7"},
    {file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"},
    {file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"},
    {file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"},
    {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"},
    {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"},
    {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"},
    {file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"},
    {file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"},
    {file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"},
    {file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"},
    {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"},
]
certifi = [
    {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
    {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
cfgv = [
    {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
    {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
]
charset-normalizer = [
    {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
    {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
]
click = [
    {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
    {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
cma = [
    {file = "cma-3.2.2-py2.py3-none-any.whl", hash = "sha256:0e3994b1f93c065b3eb2e68dbc589d9e694e191698d0aa58cb70cbdb803ade4e"},
    {file = "cma-3.2.2.tar.gz", hash = "sha256:47ac71ddd2b9a922daa601516064a585a1a3dbbae7802bca935db25e525547eb"},
]
colorama = [
    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
commonmark = [
    {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"},
    {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
]
contourpy = [
    {file = "contourpy-1.0.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:613c665529899b5d9fade7e5d1760111a0b011231277a0d36c49f0d3d6914bd6"},
    {file = "contourpy-1.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:78ced51807ccb2f45d4ea73aca339756d75d021069604c2fccd05390dc3c28eb"},
    {file = "contourpy-1.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3b1bd7577c530eaf9d2bc52d1a93fef50ac516a8b1062c3d1b9bcec9ebe329b"},
    {file = "contourpy-1.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8834c14b8c3dd849005e06703469db9bf96ba2d66a3f88ecc539c9a8982e0ee"},
    {file = "contourpy-1.0.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4052a8a4926d4468416fc7d4b2a7b2a3e35f25b39f4061a7e2a3a2748c4fc48"},
    {file = "contourpy-1.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c0e1308307a75e07d1f1b5f0f56b5af84538a5e9027109a7bcf6cb47c434e72"},
    {file = "contourpy-1.0.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fc4e7973ed0e1fe689435842a6e6b330eb7ccc696080dda9a97b1a1b78e41db"},
    {file = "contourpy-1.0.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08e8d09d96219ace6cb596506fb9b64ea5f270b2fb9121158b976d88871fcfd1"},
    {file = "contourpy-1.0.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f33da6b5d19ad1bb5e7ad38bb8ba5c426d2178928bc2b2c44e8823ea0ecb6ff3"},
    {file = "contourpy-1.0.6-cp310-cp310-win32.whl", hash = "sha256:12a7dc8439544ed05c6553bf026d5e8fa7fad48d63958a95d61698df0e00092b"},
    {file = "contourpy-1.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:eadad75bf91897f922e0fb3dca1b322a58b1726a953f98c2e5f0606bd8408621"},
    {file = "contourpy-1.0.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:913bac9d064cff033cf3719e855d4f1db9f1c179e0ecf3ba9fdef21c21c6a16a"},
    {file = "contourpy-1.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46deb310a276cc5c1fd27958e358cce68b1e8a515fa5a574c670a504c3a3fe30"},
    {file = "contourpy-1.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b64f747e92af7da3b85631a55d68c45a2d728b4036b03cdaba4bd94bcc85bd6f"},
    {file = "contourpy-1.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50627bf76abb6ba291ad08db583161939c2c5fab38c38181b7833423ab9c7de3"},
    {file = "contourpy-1.0.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:358f6364e4873f4d73360b35da30066f40387dd3c427a3e5432c6b28dd24a8fa"},
    {file = "contourpy-1.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c78bfbc1a7bff053baf7e508449d2765964d67735c909b583204e3240a2aca45"},
    {file = "contourpy-1.0.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e43255a83835a129ef98f75d13d643844d8c646b258bebd11e4a0975203e018f"},
    {file = "contourpy-1.0.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:375d81366afd547b8558c4720337218345148bc2fcffa3a9870cab82b29667f2"},
    {file = "contourpy-1.0.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b98c820608e2dca6442e786817f646d11057c09a23b68d2b3737e6dcb6e4a49b"},
    {file = "contourpy-1.0.6-cp311-cp311-win32.whl", hash = "sha256:0e4854cc02006ad6684ce092bdadab6f0912d131f91c2450ce6dbdea78ee3c0b"},
    {file = "contourpy-1.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:d2eff2af97ea0b61381828b1ad6cd249bbd41d280e53aea5cccd7b2b31b8225c"},
    {file = "contourpy-1.0.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5b117d29433fc8393b18a696d794961464e37afb34a6eeb8b2c37b5f4128a83e"},
    {file = "contourpy-1.0.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341330ed19074f956cb20877ad8d2ae50e458884bfa6a6df3ae28487cc76c768"},
    {file = "contourpy-1.0.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:371f6570a81dfdddbb837ba432293a63b4babb942a9eb7aaa699997adfb53278"},
    {file = "contourpy-1.0.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9447c45df407d3ecb717d837af3b70cfef432138530712263730783b3d016512"},
    {file = "contourpy-1.0.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:730c27978a0003b47b359935478b7d63fd8386dbb2dcd36c1e8de88cbfc1e9de"},
    {file = "contourpy-1.0.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:da1ef35fd79be2926ba80fbb36327463e3656c02526e9b5b4c2b366588b74d9a"},
    {file = "contourpy-1.0.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd2bc0c8f2e8de7dd89a7f1c10b8844e291bca17d359373203ef2e6100819edd"},
    {file = "contourpy-1.0.6-cp37-cp37m-win32.whl", hash = "sha256:3a1917d3941dd58732c449c810fa7ce46cc305ce9325a11261d740118b85e6f3"},
    {file = "contourpy-1.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:06ca79e1efbbe2df795822df2fa173d1a2b38b6e0f047a0ec7903fbca1d1847e"},
    {file = "contourpy-1.0.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e626cefff8491bce356221c22af5a3ea528b0b41fbabc719c00ae233819ea0bf"},
    {file = "contourpy-1.0.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbe6fe7a1166b1ddd7b6d887ea6fa8389d3f28b5ed3f73a8f40ece1fc5a3d340"},
    {file = "contourpy-1.0.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e13b31d1b4b68db60b3b29f8e337908f328c7f05b9add4b1b5c74e0691180109"},
    {file = "contourpy-1.0.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79d239fc22c3b8d9d3de492aa0c245533f4f4c7608e5749af866949c0f1b1b9"},
    {file = "contourpy-1.0.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e8e686a6db92a46111a1ee0ee6f7fbfae4048f0019de207149f43ac1812cf95"},
    {file = "contourpy-1.0.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2bd02f1a7adff3a1f33e431eb96ab6d7987b039d2946a9b39fe6fb16a1036"},
    {file = "contourpy-1.0.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:03d1b9c6b44a9e30d554654c72be89af94fab7510b4b9f62356c64c81cec8b7d"},
    {file = "contourpy-1.0.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b48d94386f1994db7c70c76b5808c12e23ed7a4ee13693c2fc5ab109d60243c0"},
    {file = "contourpy-1.0.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:208bc904889c910d95aafcf7be9e677726df9ef71e216780170dbb7e37d118fa"},
    {file = "contourpy-1.0.6-cp38-cp38-win32.whl", hash = "sha256:444fb776f58f4906d8d354eb6f6ce59d0a60f7b6a720da6c1ccb839db7c80eb9"},
    {file = "contourpy-1.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:9bc407a6af672da20da74823443707e38ece8b93a04009dca25856c2d9adadb1"},
    {file = "contourpy-1.0.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:aa4674cf3fa2bd9c322982644967f01eed0c91bb890f624e0e0daf7a5c3383e9"},
    {file = "contourpy-1.0.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f56515e7c6fae4529b731f6c117752247bef9cdad2b12fc5ddf8ca6a50965a5"},
    {file = "contourpy-1.0.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:344cb3badf6fc7316ad51835f56ac387bdf86c8e1b670904f18f437d70da4183"},
    {file = "contourpy-1.0.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b1e66346acfb17694d46175a0cea7d9036f12ed0c31dfe86f0f405eedde2bdd"},
    {file = "contourpy-1.0.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8468b40528fa1e15181cccec4198623b55dcd58306f8815a793803f51f6c474a"},
    {file = "contourpy-1.0.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dedf4c64185a216c35eb488e6f433297c660321275734401760dafaeb0ad5c2"},
    {file = "contourpy-1.0.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:494efed2c761f0f37262815f9e3c4bb9917c5c69806abdee1d1cb6611a7174a0"},
    {file = "contourpy-1.0.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:75a2e638042118118ab39d337da4c7908c1af74a8464cad59f19fbc5bbafec9b"},
    {file = "contourpy-1.0.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a628bba09ba72e472bf7b31018b6281fd4cc903f0888049a3724afba13b6e0b8"},
    {file = "contourpy-1.0.6-cp39-cp39-win32.whl", hash = "sha256:e1739496c2f0108013629aa095cc32a8c6363444361960c07493818d0dea2da4"},
    {file = "contourpy-1.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:a457ee72d9032e86730f62c5eeddf402e732fdf5ca8b13b41772aa8ae13a4563"},
    {file = "contourpy-1.0.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d912f0154a20a80ea449daada904a7eb6941c83281a9fab95de50529bfc3a1da"},
    {file = "contourpy-1.0.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4081918147fc4c29fad328d5066cfc751da100a1098398742f9f364be63803fc"},
    {file = "contourpy-1.0.6-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0537cc1195245bbe24f2913d1f9211b8f04eb203de9044630abd3664c6cc339c"},
    {file = "contourpy-1.0.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcd556c8fc37a342dd636d7eef150b1399f823a4462f8c968e11e1ebeabee769"},
    {file = "contourpy-1.0.6-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f6ca38dd8d988eca8f07305125dec6f54ac1c518f1aaddcc14d08c01aebb6efc"},
    {file = "contourpy-1.0.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c1baa49ab9fedbf19d40d93163b7d3e735d9cd8d5efe4cce9907902a6dad391f"},
    {file = "contourpy-1.0.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:211dfe2bd43bf5791d23afbe23a7952e8ac8b67591d24be3638cabb648b3a6eb"},
    {file = "contourpy-1.0.6-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38c6536c2d71ca2f7e418acaf5bca30a3af7f2a2fa106083c7d738337848dbe"},
    {file = "contourpy-1.0.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b1ee48a130da4dd0eb8055bbab34abf3f6262957832fd575e0cab4979a15a41"},
    {file = "contourpy-1.0.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5641927cc5ae66155d0c80195dc35726eae060e7defc18b7ab27600f39dd1fe7"},
    {file = "contourpy-1.0.6-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ee394502026d68652c2824348a40bf50f31351a668977b51437131a90d777ea"},
    {file = "contourpy-1.0.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b97454ed5b1368b66ed414c754cba15b9750ce69938fc6153679787402e4cdf"},
    {file = "contourpy-1.0.6-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0236875c5a0784215b49d00ebbe80c5b6b5d5244b3655a36dda88105334dea17"},
    {file = "contourpy-1.0.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c593aeff7a0171f639da92cb86d24954bbb61f8a1b530f74eb750a14685832"},
    {file = "contourpy-1.0.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9b0e7fe7f949fb719b206548e5cde2518ffb29936afa4303d8a1c4db43dcb675"},
    {file = "contourpy-1.0.6.tar.gz", hash = "sha256:6e459ebb8bb5ee4c22c19cc000174f8059981971a33ce11e17dddf6aca97a142"},
]
coverage = [
    {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
    {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"},
    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"},
    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"},
    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"},
    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"},
    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"},
    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"},
    {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"},
    {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"},
    {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"},
    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"},
    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"},
    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"},
    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"},
    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"},
    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"},
    {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"},
    {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"},
    {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"},
    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"},
    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"},
    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"},
    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"},
    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"},
    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"},
    {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"},
    {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"},
    {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"},
    {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"},
    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"},
    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"},
    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"},
    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"},
    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"},
    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"},
    {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"},
    {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"},
    {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"},
    {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"},
    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"},
    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"},
    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"},
    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"},
    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"},
    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"},
    {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"},
    {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"},
    {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"},
    {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"},
]
cycler = [
    {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"},
    {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"},
]
dash = [
    {file = "dash-2.6.2-py3-none-any.whl", hash = "sha256:dd04d0c072879e3c14541412e580b61cd87e9de817dba5c7dfda6d9629bc0757"},
    {file = "dash-2.6.2.tar.gz", hash = "sha256:00c7d1dddaf40154c0faa1f212faf8907907b8c2eb50b5d70ac8c72e51b5319d"},
]
dash-core-components = [
    {file = "dash_core_components-2.0.0-py3-none-any.whl", hash = "sha256:52b8e8cce13b18d0802ee3acbc5e888cb1248a04968f962d63d070400af2e346"},
    {file = "dash_core_components-2.0.0.tar.gz", hash = "sha256:c6733874af975e552f95a1398a16c2ee7df14ce43fa60bb3718a3c6e0b63ffee"},
]
dash-html-components = [
    {file = "dash_html_components-2.0.0-py3-none-any.whl", hash = "sha256:b42cc903713c9706af03b3f2548bda4be7307a7cf89b7d6eae3da872717d1b63"},
    {file = "dash_html_components-2.0.0.tar.gz", hash = "sha256:8703a601080f02619a6390998e0b3da4a5daabe97a1fd7a9cebc09d015f26e50"},
]
dash-table = [
    {file = "dash_table-5.0.0-py3-none-any.whl", hash = "sha256:19036fa352bb1c11baf38068ec62d172f0515f73ca3276c79dee49b95ddc16c9"},
    {file = "dash_table-5.0.0.tar.gz", hash = "sha256:18624d693d4c8ef2ddec99a6f167593437a7ea0bf153aa20f318c170c5bc7308"},
]
distlib = [
    {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
    {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
]
docutils = [
    {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
    {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
]
exceptiongroup = [
    {file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"},
    {file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"},
]
filelock = [
    {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
    {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
]
flask = [
    {file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"},
    {file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"},
]
flask-compress = [
    {file = "Flask-Compress-1.13.tar.gz", hash = "sha256:ee96f18bf9b00f2deb4e3406ca4a05093aa80e2ef0578525a3b4d32ecdff129d"},
    {file = "Flask_Compress-1.13-py3-none-any.whl", hash = "sha256:1128f71fbd788393ce26830c51f8b5a1a7a4d085e79a21a5cddf4c057dcd559b"},
]
fonttools = [
    {file = "fonttools-4.38.0-py3-none-any.whl", hash = "sha256:820466f43c8be8c3009aef8b87e785014133508f0de64ec469e4efb643ae54fb"},
    {file = "fonttools-4.38.0.zip", hash = "sha256:2bb244009f9bf3fa100fc3ead6aeb99febe5985fa20afbfbaa2f8946c2fbdaf1"},
]
future = [
    {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
]
identify = [
    {file = "identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"},
    {file = "identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"},
]
idna = [
    {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
    {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
imagesize = [
    {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
    {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
]
importlib-metadata = [
    {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
    {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
]
iniconfig = [
    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
isort = [
    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
itsdangerous = [
    {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
    {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
]
jinja2 = [
    {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
    {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
joblib = [
    {file = "joblib-1.2.0-py3-none-any.whl", hash = "sha256:091138ed78f800342968c523bdde947e7a305b8594b910a0fea2ab83c3c6d385"},
    {file = "joblib-1.2.0.tar.gz", hash = "sha256:e1cee4a79e4af22881164f218d4311f60074197fb707e082e803b61f6d137018"},
]
kiwisolver = [
    {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"},
    {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"},
    {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"},
    {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"},
    {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"},
    {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"},
    {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"},
    {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"},
    {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"},
    {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"},
    {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"},
    {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"},
    {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"},
    {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"},
    {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"},
    {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"},
    {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"},
    {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"},
    {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"},
    {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"},
    {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"},
    {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"},
    {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"},
    {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"},
    {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"},
    {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"},
    {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"},
    {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"},
    {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"},
    {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"},
    {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"},
    {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"},
    {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"},
    {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"},
    {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"},
    {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"},
    {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"},
    {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"},
    {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"},
    {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"},
    {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"},
    {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"},
    {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"},
    {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"},
    {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"},
    {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"},
    {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"},
    {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"},
    {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"},
    {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"},
    {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"},
    {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"},
    {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"},
    {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"},
    {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"},
    {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"},
    {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"},
    {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"},
    {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"},
    {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"},
    {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"},
]
latexcodec = [
    {file = "latexcodec-2.0.1-py2.py3-none-any.whl", hash = "sha256:c277a193638dc7683c4c30f6684e3db728a06efb0dc9cf346db8bd0aa6c5d271"},
    {file = "latexcodec-2.0.1.tar.gz", hash = "sha256:2aa2551c373261cefe2ad3a8953a6d6533e68238d180eb4bb91d7964adb3fe9a"},
]
lazy-object-proxy = [
    {file = "lazy-object-proxy-1.8.0.tar.gz", hash = "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156"},
    {file = "lazy_object_proxy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe"},
    {file = "lazy_object_proxy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25"},
    {file = "lazy_object_proxy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"},
    {file = "lazy_object_proxy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7"},
    {file = "lazy_object_proxy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e"},
    {file = "lazy_object_proxy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d"},
    {file = "lazy_object_proxy-1.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c"},
    {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win32.whl", hash = "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd"},
    {file = "lazy_object_proxy-1.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858"},
    {file = "lazy_object_proxy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada"},
    {file = "lazy_object_proxy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f"},
    {file = "lazy_object_proxy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c"},
    {file = "lazy_object_proxy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288"},
    {file = "lazy_object_proxy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f"},
    {file = "lazy_object_proxy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0"},
    {file = "lazy_object_proxy-1.8.0-pp37-pypy37_pp73-any.whl", hash = "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891"},
    {file = "lazy_object_proxy-1.8.0-pp38-pypy38_pp73-any.whl", hash = "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec"},
    {file = "lazy_object_proxy-1.8.0-pp39-pypy39_pp73-any.whl", hash = "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8"},
]
lmfit = [
    {file = "lmfit-1.0.3.tar.gz", hash = "sha256:d067c3ea501f035af5d3c079e6e6e35dc3cc1ac7d439429a425b0aeb5a7858a2"},
]
markdown = [
    {file = "Markdown-3.4.1-py3-none-any.whl", hash = "sha256:08fb8465cffd03d10b9dd34a5c3fea908e20391a2a90b88d66362cb05beed186"},
    {file = "Markdown-3.4.1.tar.gz", hash = "sha256:3b809086bb6efad416156e00a0da66fe47618a5d6918dd688f53f40c8e4cfeff"},
]
markupsafe = [
    {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
    {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
]
matplotlib = [
    {file = "matplotlib-3.6.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:7730e60e751cfcfe7fcb223cf03c0b979e9a064c239783ad37929d340a364cef"},
    {file = "matplotlib-3.6.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9dd40505ccc526acaf9a5db1b3029e237c64b58f1249983b28a291c2d6a1d0fa"},
    {file = "matplotlib-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85948b303534b69fd771126764cf883fde2af9b003eb5778cb60f3b46f93d3f6"},
    {file = "matplotlib-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71eced071825005011cdc64efbae2e2c76b8209c18aa487dedf69796fe4b1e40"},
    {file = "matplotlib-3.6.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220314c2d6b9ca11570d7cd4b841c9f3137546f188336003b9fb8def4dcb804d"},
    {file = "matplotlib-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cc5d726d4d42865f909c5208a7841109d76584950dd0587b01a77cc279d4ab7"},
    {file = "matplotlib-3.6.1-cp310-cp310-win32.whl", hash = "sha256:183bf3ac6a6023ee590aa4b677f391ceed65ec0d6b930901a8483c267bd12995"},
    {file = "matplotlib-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:a68b91ac7e6bb26100a540a033f54c95fe06d9c0aa51312c2a52d07d1bde78f4"},
    {file = "matplotlib-3.6.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:4648f0d79a87bf50ee740058305c91091ee5e1fbb71a7d2f5fe6707bfe328d1c"},
    {file = "matplotlib-3.6.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9403764017d20ff570f7ce973a8b9637f08a6109118f4e0ce6c7493d8849a0d3"},
    {file = "matplotlib-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4c8b5a243dd29d50289d694e931bd6cb6ae0b5bd654d12c647543d63862540c"},
    {file = "matplotlib-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1effccef0cea2d4da9feeed22079adf6786f92c800a7d0d2ef2104318a1c66c"},
    {file = "matplotlib-3.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dc25473319afabe49150267e54648ac559c33b0fc2a80c8caecfbbc2948a820"},
    {file = "matplotlib-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47cb088bbce82ae9fc2edf3c25e56a5c6142ce2553fea2b781679f960a70c207"},
    {file = "matplotlib-3.6.1-cp311-cp311-win32.whl", hash = "sha256:4d3b0e0a4611bd22065bbf47e9b2f689ac9e575bcb850a9f0ae2bbed75cab956"},
    {file = "matplotlib-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:e3c116e779fbbf421a9e4d3060db259a9bb486d98f4e3c5a0877c599bd173582"},
    {file = "matplotlib-3.6.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:565f514dec81a41cbed10eb6011501879695087fc2787fb89423a466508abbbd"},
    {file = "matplotlib-3.6.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:05e86446562063d6186ff6d700118c0dbd5dccc403a6187351ee526c48878f10"},
    {file = "matplotlib-3.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8245e85fd793f58edf29b8a9e3be47e8ecf76ea1a1e8240545f2746181ca5787"},
    {file = "matplotlib-3.6.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1e2c75d5d1ff6b7ef9870360bfa23bea076b8dc0945a60d19453d7619ed9ea8f"},
    {file = "matplotlib-3.6.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9756a8e69f6e1f76d47eb42132175b6814da1fbeae0545304c6d0fc2aae252a"},
    {file = "matplotlib-3.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f5788168da2661b42f7468063b725cc73fdbeeb80f2704cb2d8c415e9a57c50"},
    {file = "matplotlib-3.6.1-cp38-cp38-win32.whl", hash = "sha256:0bab7564aafd5902128d54b68dca04f5755413fb6b502100bb0235a545882c48"},
    {file = "matplotlib-3.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3c53486278a0629fd892783271dc994b962fba8dfe207445d039e14f1928ea46"},
    {file = "matplotlib-3.6.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:27337bcb38d5db7430c14f350924542d75416ec1546d5d9d9f39b362b71db3fb"},
    {file = "matplotlib-3.6.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fad858519bd6d52dbfeebdbe04d00dd8e932ed436f1c535e61bcc970a96c11e4"},
    {file = "matplotlib-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4a3d903588b519b38ed085d0ae762a1dcd4b70164617292175cfd91b90d6c415"},
    {file = "matplotlib-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bdbd37d0a41e025879863fe9b17bab15c0421313bc33e77e5e1aa54215c9c5"},
    {file = "matplotlib-3.6.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e632f66218811d4cf8b7a2a649e25ec15406c3c498f72d19e2bcf8377f38445d"},
    {file = "matplotlib-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ddd58324dc9a77e2e56d7b7aea7dbd0575b6f7cd1333c3ca9d388ac70978344"},
    {file = "matplotlib-3.6.1-cp39-cp39-win32.whl", hash = "sha256:12ab21d0cad122f5b23688d453a0280676e7c42f634f0dbd093d15d42d142b1f"},
    {file = "matplotlib-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:563896ba269324872ace436a57775dcc8322678a9496b28a8c25cdafa5ec2b92"},
    {file = "matplotlib-3.6.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:52935b7d4ccbf0dbc9cf454dbb10ca99c11cbe8da9467596b96e5e21fd4dfc5c"},
    {file = "matplotlib-3.6.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87027ff7b2edeb14476900261ef04d4beae949e1dfa0a3eb3ad6a6efbf9d0e1d"},
    {file = "matplotlib-3.6.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4de03085afb3b80fab341afaf8e60dfe06ce439b6dfed55d657cf34a7bc3c40"},
    {file = "matplotlib-3.6.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b53387d4e59432ff221540a4ffb5ee9669c69417805e4faf0148a00d701c61f9"},
    {file = "matplotlib-3.6.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:02561141c434154f7bae8e5449909d152367cb40aa57bfb2a27f2748b9c5f95f"},
    {file = "matplotlib-3.6.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0161ebf87518ecfe0980c942d5f0d5df0e080c1746ebaab2027a969967014b7"},
    {file = "matplotlib-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2469f57e4c5cc0e85eddc7b30995ea9c404a78c0b1856da75d1a5887156ca350"},
    {file = "matplotlib-3.6.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5f97141e05baf160c3ec125f06ceb2a44c9bb62f42fcb8ee1c05313c73e99432"},
    {file = "matplotlib-3.6.1.tar.gz", hash = "sha256:e2d1b7225666f7e1bcc94c0bc9c587a82e3e8691da4757e357e5c2515222ee37"},
]
mccabe = [
    {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
    {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
mpmath = [
    {file = "mpmath-1.2.1-py3-none-any.whl", hash = "sha256:604bc21bd22d2322a177c73bdb573994ef76e62edd595d17e00aff24b0667e5c"},
    {file = "mpmath-1.2.1.tar.gz", hash = "sha256:79ffb45cf9f4b101a807595bcb3e72e0396202e0b1d25d689134b48c4216a81a"},
]
mypy-extensions = [
    {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
    {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
nodeenv = [
    {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"},
    {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"},
]
numpy = [
    {file = "numpy-1.23.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2"},
    {file = "numpy-1.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f"},
    {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c237129f0e732885c9a6076a537e974160482eab8f10db6292e92154d4c67d71"},
    {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8365b942f9c1a7d0f0dc974747d99dd0a0cdfc5949a33119caf05cb314682d3"},
    {file = "numpy-1.23.4-cp310-cp310-win32.whl", hash = "sha256:2341f4ab6dba0834b685cce16dad5f9b6606ea8a00e6da154f5dbded70fdc4dd"},
    {file = "numpy-1.23.4-cp310-cp310-win_amd64.whl", hash = "sha256:d331afac87c92373826af83d2b2b435f57b17a5c74e6268b79355b970626e329"},
    {file = "numpy-1.23.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:488a66cb667359534bc70028d653ba1cf307bae88eab5929cd707c761ff037db"},
    {file = "numpy-1.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce03305dd694c4873b9429274fd41fc7eb4e0e4dea07e0af97a933b079a5814f"},
    {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8981d9b5619569899666170c7c9748920f4a5005bf79c72c07d08c8a035757b0"},
    {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a70a7d3ce4c0e9284e92285cba91a4a3f5214d87ee0e95928f3614a256a1488"},
    {file = "numpy-1.23.4-cp311-cp311-win32.whl", hash = "sha256:5e13030f8793e9ee42f9c7d5777465a560eb78fa7e11b1c053427f2ccab90c79"},
    {file = "numpy-1.23.4-cp311-cp311-win_amd64.whl", hash = "sha256:7607b598217745cc40f751da38ffd03512d33ec06f3523fb0b5f82e09f6f676d"},
    {file = "numpy-1.23.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ab46e4e7ec63c8a5e6dbf5c1b9e1c92ba23a7ebecc86c336cb7bf3bd2fb10e5"},
    {file = "numpy-1.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8aae2fb3180940011b4862b2dd3756616841c53db9734b27bb93813cd79fce6"},
    {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c053d7557a8f022ec823196d242464b6955a7e7e5015b719e76003f63f82d0f"},
    {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0882323e0ca4245eb0a3d0a74f88ce581cc33aedcfa396e415e5bba7bf05f68"},
    {file = "numpy-1.23.4-cp38-cp38-win32.whl", hash = "sha256:dada341ebb79619fe00a291185bba370c9803b1e1d7051610e01ed809ef3a4ba"},
    {file = "numpy-1.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:0fe563fc8ed9dc4474cbf70742673fc4391d70f4363f917599a7fa99f042d5a8"},
    {file = "numpy-1.23.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67b833dbccefe97cdd3f52798d430b9d3430396af7cdb2a0c32954c3ef73894"},
    {file = "numpy-1.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7"},
    {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12ac457b63ec8ded85d85c1e17d85efd3c2b0967ca39560b307a35a6703a4735"},
    {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95de7dc7dc47a312f6feddd3da2500826defdccbc41608d0031276a24181a2c0"},
    {file = "numpy-1.23.4-cp39-cp39-win32.whl", hash = "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef"},
    {file = "numpy-1.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:f260da502d7441a45695199b4e7fd8ca87db659ba1c78f2bbf31f934fe76ae0e"},
    {file = "numpy-1.23.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61be02e3bf810b60ab74e81d6d0d36246dbfb644a462458bb53b595791251911"},
    {file = "numpy-1.23.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296d17aed51161dbad3c67ed6d164e51fcd18dbcd5dd4f9d0a9c6055dce30810"},
    {file = "numpy-1.23.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4d52914c88b4930dafb6c48ba5115a96cbab40f45740239d9f4159c4ba779962"},
    {file = "numpy-1.23.4.tar.gz", hash = "sha256:ed2cc92af0efad20198638c69bb0fc2870a58dabfba6eb722c933b48556c686c"},
]
packaging = [
    {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
    {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pandas = [
    {file = "pandas-1.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a78e05ec09731c5b3bd7a9805927ea631fe6f6cb06f0e7c63191a9a778d52b4"},
    {file = "pandas-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5b0c970e2215572197b42f1cff58a908d734503ea54b326412c70d4692256391"},
    {file = "pandas-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f340331a3f411910adfb4bbe46c2ed5872d9e473a783d7f14ecf49bc0869c594"},
    {file = "pandas-1.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c709f4700573deb2036d240d140934df7e852520f4a584b2a8d5443b71f54d"},
    {file = "pandas-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32e3d9f65606b3f6e76555bfd1d0b68d94aff0929d82010b791b6254bf5a4b96"},
    {file = "pandas-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a52419d9ba5906db516109660b114faf791136c94c1a636ed6b29cbfff9187ee"},
    {file = "pandas-1.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:66a1ad667b56e679e06ba73bb88c7309b3f48a4c279bd3afea29f65a766e9036"},
    {file = "pandas-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:36aa1f8f680d7584e9b572c3203b20d22d697c31b71189322f16811d4ecfecd3"},
    {file = "pandas-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bcf1a82b770b8f8c1e495b19a20d8296f875a796c4fe6e91da5ef107f18c5ecb"},
    {file = "pandas-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c25e5c16ee5c0feb6cf9d982b869eec94a22ddfda9aa2fbed00842cbb697624"},
    {file = "pandas-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:932d2d7d3cab44cfa275601c982f30c2d874722ef6396bb539e41e4dc4618ed4"},
    {file = "pandas-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:eb7e8cf2cf11a2580088009b43de84cabbf6f5dae94ceb489f28dba01a17cb77"},
    {file = "pandas-1.5.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cb2a9cf1150302d69bb99861c5cddc9c25aceacb0a4ef5299785d0f5389a3209"},
    {file = "pandas-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:81f0674fa50b38b6793cd84fae5d67f58f74c2d974d2cb4e476d26eee33343d0"},
    {file = "pandas-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:17da7035d9e6f9ea9cdc3a513161f8739b8f8489d31dc932bc5a29a27243f93d"},
    {file = "pandas-1.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:669c8605dba6c798c1863157aefde959c1796671ffb342b80fcb80a4c0bc4c26"},
    {file = "pandas-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:683779e5728ac9138406c59a11e09cd98c7d2c12f0a5fc2b9c5eecdbb4a00075"},
    {file = "pandas-1.5.1-cp38-cp38-win32.whl", hash = "sha256:ddf46b940ef815af4e542697eaf071f0531449407a7607dd731bf23d156e20a7"},
    {file = "pandas-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:db45b94885000981522fb92349e6b76f5aee0924cc5315881239c7859883117d"},
    {file = "pandas-1.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:927e59c694e039c75d7023465d311277a1fc29ed7236b5746e9dddf180393113"},
    {file = "pandas-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e675f8fe9aa6c418dc8d3aac0087b5294c1a4527f1eacf9fe5ea671685285454"},
    {file = "pandas-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04e51b01d5192499390c0015630975f57836cc95c7411415b499b599b05c0c96"},
    {file = "pandas-1.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cee0c74e93ed4f9d39007e439debcaadc519d7ea5c0afc3d590a3a7b2edf060"},
    {file = "pandas-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b156a971bc451c68c9e1f97567c94fd44155f073e3bceb1b0d195fd98ed12048"},
    {file = "pandas-1.5.1-cp39-cp39-win32.whl", hash = "sha256:05c527c64ee02a47a24031c880ee0ded05af0623163494173204c5b72ddce658"},
    {file = "pandas-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:6bb391659a747cf4f181a227c3e64b6d197100d53da98dcd766cc158bdd9ec68"},
    {file = "pandas-1.5.1.tar.gz", hash = "sha256:249cec5f2a5b22096440bd85c33106b6102e0672204abd2d5c014106459804ee"},
]
pastel = [
    {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"},
    {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"},
]
pathspec = [
    {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"},
    {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"},
]
pillow = [
    {file = "Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"},
    {file = "Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"},
    {file = "Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"},
    {file = "Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"},
    {file = "Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"},
    {file = "Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"},
    {file = "Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"},
    {file = "Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"},
    {file = "Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"},
    {file = "Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"},
    {file = "Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"},
    {file = "Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"},
    {file = "Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"},
    {file = "Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"},
    {file = "Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"},
    {file = "Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"},
    {file = "Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"},
    {file = "Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"},
    {file = "Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"},
    {file = "Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"},
    {file = "Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"},
    {file = "Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"},
    {file = "Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"},
    {file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"},
]
pint = [
    {file = "Pint-0.20.1-py3-none-any.whl", hash = "sha256:68afe65665542ee3ec99f69f043b1d39bfe7c6d61b786940157138fd08b838fb"},
    {file = "Pint-0.20.1.tar.gz", hash = "sha256:387cf04078dc7dfe4a708033baad54ab61d82ab06c4ee3d4922b1e45d5626067"},
]
Pint-Pandas = []
platformdirs = [
    {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
    {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
]
plotly = [
    {file = "plotly-5.11.0-py2.py3-none-any.whl", hash = "sha256:52fd74b08aa4fd5a55b9d3034a30dbb746e572d7ed84897422f927fdf687ea5f"},
    {file = "plotly-5.11.0.tar.gz", hash = "sha256:4efef479c2ec1d86dcdac8405b6ca70ca65649a77408e39a7e84a1ea2db6c787"},
]
pluggy = [
    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
poethepoet = [
    {file = "poethepoet-0.16.4-py3-none-any.whl", hash = "sha256:1f05dce92ca6457d018696b614ba2149261380f30ceb21c196daf19c0c2e1fcd"},
    {file = "poethepoet-0.16.4.tar.gz", hash = "sha256:a80f6bba64812515c406ffc218aff833951b17854eb111f724b48c44f9759af5"},
]
pre-commit = [
    {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"},
    {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"},
]
psutil = [
    {file = "psutil-5.9.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b4a247cd3feaae39bb6085fcebf35b3b8ecd9b022db796d89c8f05067ca28e71"},
    {file = "psutil-5.9.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5fa88e3d5d0b480602553d362c4b33a63e0c40bfea7312a7bf78799e01e0810b"},
    {file = "psutil-5.9.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:767ef4fa33acda16703725c0473a91e1832d296c37c63896c7153ba81698f1ab"},
    {file = "psutil-5.9.3-cp27-cp27m-win32.whl", hash = "sha256:9a4af6ed1094f867834f5f07acd1250605a0874169a5fcadbcec864aec2496a6"},
    {file = "psutil-5.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:fa5e32c7d9b60b2528108ade2929b115167fe98d59f89555574715054f50fa31"},
    {file = "psutil-5.9.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:fe79b4ad4836e3da6c4650cb85a663b3a51aef22e1a829c384e18fae87e5e727"},
    {file = "psutil-5.9.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:db8e62016add2235cc87fb7ea000ede9e4ca0aa1f221b40cef049d02d5d2593d"},
    {file = "psutil-5.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:941a6c2c591da455d760121b44097781bc970be40e0e43081b9139da485ad5b7"},
    {file = "psutil-5.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71b1206e7909792d16933a0d2c1c7f04ae196186c51ba8567abae1d041f06dcb"},
    {file = "psutil-5.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f57d63a2b5beaf797b87024d018772439f9d3103a395627b77d17a8d72009543"},
    {file = "psutil-5.9.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7507f6c7b0262d3e7b0eeda15045bf5881f4ada70473b87bc7b7c93b992a7d7"},
    {file = "psutil-5.9.3-cp310-cp310-win32.whl", hash = "sha256:1b540599481c73408f6b392cdffef5b01e8ff7a2ac8caae0a91b8222e88e8f1e"},
    {file = "psutil-5.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:547ebb02031fdada635452250ff39942db8310b5c4a8102dfe9384ee5791e650"},
    {file = "psutil-5.9.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d8c3cc6bb76492133474e130a12351a325336c01c96a24aae731abf5a47fe088"},
    {file = "psutil-5.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d880053c6461c9b89cd5d4808f3b8336665fa3acdefd6777662c5ed73a851a"},
    {file = "psutil-5.9.3-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e8b50241dd3c2ed498507f87a6602825073c07f3b7e9560c58411c14fe1e1c9"},
    {file = "psutil-5.9.3-cp36-cp36m-win32.whl", hash = "sha256:828c9dc9478b34ab96be75c81942d8df0c2bb49edbb481f597314d92b6441d89"},
    {file = "psutil-5.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:ed15edb14f52925869250b1375f0ff58ca5c4fa8adefe4883cfb0737d32f5c02"},
    {file = "psutil-5.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d266cd05bd4a95ca1c2b9b5aac50d249cf7c94a542f47e0b22928ddf8b80d1ef"},
    {file = "psutil-5.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e4939ff75149b67aef77980409f156f0082fa36accc475d45c705bb00c6c16a"},
    {file = "psutil-5.9.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68fa227c32240c52982cb931801c5707a7f96dd8927f9102d6c7771ea1ff5698"},
    {file = "psutil-5.9.3-cp37-cp37m-win32.whl", hash = "sha256:beb57d8a1ca0ae0eb3d08ccaceb77e1a6d93606f0e1754f0d60a6ebd5c288837"},
    {file = "psutil-5.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:12500d761ac091f2426567f19f95fd3f15a197d96befb44a5c1e3cbe6db5752c"},
    {file = "psutil-5.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba38cf9984d5462b506e239cf4bc24e84ead4b1d71a3be35e66dad0d13ded7c1"},
    {file = "psutil-5.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46907fa62acaac364fff0b8a9da7b360265d217e4fdeaca0a2397a6883dffba2"},
    {file = "psutil-5.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a04a1836894c8279e5e0a0127c0db8e198ca133d28be8a2a72b4db16f6cf99c1"},
    {file = "psutil-5.9.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a4e07611997acf178ad13b842377e3d8e9d0a5bac43ece9bfc22a96735d9a4f"},
    {file = "psutil-5.9.3-cp38-cp38-win32.whl", hash = "sha256:6ced1ad823ecfa7d3ce26fe8aa4996e2e53fb49b7fed8ad81c80958501ec0619"},
    {file = "psutil-5.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35feafe232d1aaf35d51bd42790cbccb882456f9f18cdc411532902370d660df"},
    {file = "psutil-5.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:538fcf6ae856b5e12d13d7da25ad67f02113c96f5989e6ad44422cb5994ca7fc"},
    {file = "psutil-5.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a3d81165b8474087bb90ec4f333a638ccfd1d69d34a9b4a1a7eaac06648f9fbe"},
    {file = "psutil-5.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a7826e68b0cf4ce2c1ee385d64eab7d70e3133171376cac53d7c1790357ec8f"},
    {file = "psutil-5.9.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ec296f565191f89c48f33d9544d8d82b0d2af7dd7d2d4e6319f27a818f8d1cc"},
    {file = "psutil-5.9.3-cp39-cp39-win32.whl", hash = "sha256:9ec95df684583b5596c82bb380c53a603bb051cf019d5c849c47e117c5064395"},
    {file = "psutil-5.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4bd4854f0c83aa84a5a40d3b5d0eb1f3c128f4146371e03baed4589fe4f3c931"},
    {file = "psutil-5.9.3.tar.gz", hash = "sha256:7ccfcdfea4fc4b0a02ca2c31de7fcd186beb9cff8207800e14ab66f79c773af6"},
]
pybtex = [
    {file = "pybtex-0.24.0-py2.py3-none-any.whl", hash = "sha256:e1e0c8c69998452fea90e9179aa2a98ab103f3eed894405b7264e517cc2fcc0f"},
    {file = "pybtex-0.24.0.tar.gz", hash = "sha256:818eae35b61733e5c007c3fcd2cfb75ed1bc8b4173c1f70b56cc4c0802d34755"},
]
pybtex-docutils = [
    {file = "pybtex-docutils-1.0.2.tar.gz", hash = "sha256:43aa353b6d498fd5ac30f0073a98e332d061d34fe619d3d50d1761f8fd4aa016"},
    {file = "pybtex_docutils-1.0.2-py3-none-any.whl", hash = "sha256:6f9e3c25a37bcaac8c4f69513272706ec6253bb708a93d8b4b173f43915ba239"},
]
pygments = [
    {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
    {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"},
]
pylint = [
    {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"},
    {file = "pylint-2.12.2.tar.gz", hash = "sha256:9d945a73640e1fec07ee34b42f5669b770c759acd536ec7b16d7e4b87a9c9ff9"},
]
pyparsing = [
    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
]
pytest = [
    {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
    {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
]
pytest-cov = [
    {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
    {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
]
python-dateutil = [
    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
pytz = [
    {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"},
    {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"},
]
pyyaml = [
    {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
    {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
    {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
    {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
    {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
    {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
    {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
    {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
    {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
    {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
    {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
    {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
    {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
    {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
    {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
    {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
    {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
    {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
    {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
    {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
    {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
    {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
]
qibo = []
qibolab = []
recommonmark = [
    {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"},
    {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"},
]
requests = [
    {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
    {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
scipy = [
    {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"},
    {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"},
    {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"},
    {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"},
    {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"},
    {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"},
    {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"},
    {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"},
    {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"},
    {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"},
    {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"},
    {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"},
    {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"},
    {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"},
    {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"},
    {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"},
    {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"},
    {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"},
    {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"},
    {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"},
    {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"},
]
setuptools = [
    {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"},
    {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"},
]
setuptools-scm = [
    {file = "setuptools_scm-7.0.5-py3-none-any.whl", hash = "sha256:7930f720905e03ccd1e1d821db521bff7ec2ac9cf0ceb6552dd73d24a45d3b02"},
    {file = "setuptools_scm-7.0.5.tar.gz", hash = "sha256:031e13af771d6f892b941adb6ea04545bbf91ebc5ce68c78aaf3fff6e1fb4844"},
]
six = [
    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
snowballstemmer = [
    {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
    {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
]
sphinx = [
    {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"},
    {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"},
]
sphinx-markdown-tables = [
    {file = "sphinx-markdown-tables-0.0.17.tar.gz", hash = "sha256:6bc6d3d400eaccfeebd288446bc08dd83083367c58b85d40fe6c12d77ef592f1"},
    {file = "sphinx_markdown_tables-0.0.17-py3-none-any.whl", hash = "sha256:2bd0c30779653e4dd120300cbd9ca412c480738cc2241f6dea477a883f299e04"},
]
sphinx-rtd-theme = [
    {file = "sphinx_rtd_theme-1.1.0-py2.py3-none-any.whl", hash = "sha256:36da4267c804b98197419df8aa415d245449b8945301fce8c961038e0ba79ec5"},
    {file = "sphinx_rtd_theme-1.1.0.tar.gz", hash = "sha256:6e20f00f62b2c05434a33c5116bc3348a41ca94af03d3d7d1714c63457073bb3"},
]
sphinxcontrib-applehelp = [
    {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
    {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
]
sphinxcontrib-bibtex = [
    {file = "sphinxcontrib-bibtex-2.5.0.tar.gz", hash = "sha256:71b42e5db0e2e284f243875326bf9936aa9a763282277d75048826fef5b00eaa"},
    {file = "sphinxcontrib_bibtex-2.5.0-py3-none-any.whl", hash = "sha256:748f726eaca6efff7731012103417ef130ecdcc09501b4d0c54283bf5f059f76"},
]
sphinxcontrib-devhelp = [
    {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
    {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
]
sphinxcontrib-htmlhelp = [
    {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
    {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
]
sphinxcontrib-jsmath = [
    {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
    {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
]
sphinxcontrib-qthelp = [
    {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
    {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
]
sphinxcontrib-serializinghtml = [
    {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
    {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
]
sympy = [
    {file = "sympy-1.11.1-py3-none-any.whl", hash = "sha256:938f984ee2b1e8eae8a07b884c8b7a1146010040fccddc6539c54f401c8f6fcf"},
    {file = "sympy-1.11.1.tar.gz", hash = "sha256:e32380dce63cb7c0108ed525570092fd45168bdae2faa17e528221ef72e88658"},
]
tabulate = [
    {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"},
    {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"},
]
tenacity = [
    {file = "tenacity-8.1.0-py3-none-any.whl", hash = "sha256:35525cd47f82830069f0d6b73f7eb83bc5b73ee2fff0437952cedf98b27653ac"},
    {file = "tenacity-8.1.0.tar.gz", hash = "sha256:e48c437fdf9340f5666b92cd7990e96bc5fc955e1298baf4a907e3972067a445"},
]
toml = [
    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
tomli = [
    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
typing-extensions = [
    {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
    {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
]
uncertainties = [
    {file = "uncertainties-3.1.7-py2.py3-none-any.whl", hash = "sha256:4040ec64d298215531922a68fa1506dc6b1cb86cd7cca8eca848fcfe0f987151"},
    {file = "uncertainties-3.1.7.tar.gz", hash = "sha256:80111e0839f239c5b233cb4772017b483a0b7a1573a581b92ab7746a35e6faab"},
]
urllib3 = [
    {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
    {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
]
virtualenv = [
    {file = "virtualenv-20.16.6-py3-none-any.whl", hash = "sha256:186ca84254abcbde98180fd17092f9628c5fe742273c02724972a1d8a2035108"},
    {file = "virtualenv-20.16.6.tar.gz", hash = "sha256:530b850b523c6449406dfba859d6345e48ef19b8439606c5d74d7d3c9e14d76e"},
]
werkzeug = [
    {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"},
    {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"},
]
wrapt = [
    {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"},
    {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"},
    {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"},
    {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"},
    {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"},
    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"},
    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"},
    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"},
    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"},
    {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"},
    {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"},
    {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"},
    {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"},
    {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"},
    {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"},
    {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"},
    {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"},
    {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"},
    {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"},
    {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"},
    {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"},
    {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"},
    {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"},
    {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"},
    {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"},
    {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"},
    {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"},
    {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"},
    {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"},
    {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"},
    {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"},
    {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"},
    {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"},
    {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"},
    {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"},
    {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"},
    {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"},
    {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"},
    {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"},
    {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"},
    {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"},
    {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"},
    {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"},
    {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"},
    {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"},
    {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"},
    {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"},
    {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"},
    {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"},
    {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"},
    {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"},
]
zipp = [
    {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"},
    {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"},
]

README.md METASEP
# Qibocal

This package provides Quantum Characterization Validation and Verification protocols using [Qibo](https://github.com/qiboteam/qibo) and [Qibolab](https://github.com/qiboteam/qibolab).

## Installation

The package can be installed by source:
```sh
git clone https://github.com/qiboteam/qibocal.git
cd qibocal
pip install .
```


### Developer instructions
For development make sure to install the package using [`poetry`](https://python-poetry.org/) and to install the pre-commit hooks:
```sh
git clone https://github.com/qiboteam/qibocal.git
cd qibocal
poetry install
pre-commit install
```

When installing qibocal poetry will also install [Qibolab](https://github.com/qiboteam/qibolab). Make sure to setup SSH authentication for your GitHub account
to avoid errors during installation. Here are the instructions on how to [generate](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) a new SSH key and to [add](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) it to your GitHub account.

If you are looking to test new features in Qibolab make sure to reinstall Qibolab in the same environment where qibocal is installed.

```sh
git clone git@github.com:qiboteam/qibolab.git
cd qibolab
git checkout <your_branch>
pip install -e .[tiiq]
```



## Minimal working example
The command for executing calibration routines is the following:
```sh
qq <runcard>
```
where:
- `<runcard>`: yaml file containing the calibration routines to be performed. For more information see the documentation or the runcard examples in the `runcards` folder.

### Uploading reports to server

In order to upload the report to a centralized server, send to the server administrators your public ssh key (from the machine(s) you are planning to upload the report) and then use the `qq-upload <output_folder>` command. This program will upload your report to the server and generate an unique URL.

src/qibocal/web/server.py METASEP
import os
import pathlib

import yaml
from flask import Flask, render_template

from qibocal import __version__
from qibocal.cli.builders import ReportBuilder

server = Flask(__name__)


@server.route("/")
@server.route("/data/<path>")
def page(path=None):
    folders = [
        folder
        for folder in reversed(sorted(os.listdir(os.getcwd())))
        if os.path.isdir(folder) and "meta.yml" in os.listdir(folder)
    ]

    report = None
    if path is not None:
        try:
            report = ReportBuilder(path)
        except (FileNotFoundError, TypeError):
            pass

    return render_template(
        "template.html",
        version=__version__,
        folders=folders,
        report=report,
    )

src/qibocal/web/report.py METASEP
import os
import pathlib

from jinja2 import Environment, FileSystemLoader

from qibocal import __version__
from qibocal.cli.builders import ReportBuilder


def create_report(path):
    """Creates an HTML report for the data in the given path."""
    filepath = pathlib.Path(__file__)

    with open(os.path.join(filepath.with_name("static"), "styles.css")) as file:
        css_styles = f"<style>\n{file.read()}\n</style>"

    report = ReportBuilder(path)
    env = Environment(loader=FileSystemLoader(filepath.with_name("templates")))
    template = env.get_template("template.html")

    html = template.render(
        is_static=True,
        css_styles=css_styles,
        version=__version__,
        report=report,
    )

    with open(os.path.join(path, "index.html"), "w") as file:
        file.write(html)

src/qibocal/web/app.py METASEP
import os

import pandas as pd
import yaml
from dash import Dash, Input, Output, dcc, html

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.web.server import server

DataUnits()  # dummy dataset call to suppress ``pint[V]`` error

app = Dash(
    server=server,
    suppress_callback_exceptions=True,
)

app.layout = html.Div(
    [
        dcc.Location(id="url", refresh=False),
        dcc.Graph(id="graph", figure={}),
        dcc.Interval(
            id="interval",
            # TODO: Perhaps the user should be allowed to change the refresh rate
            interval=1000,
            n_intervals=0,
            disabled=False,
        ),
    ]
)


@app.callback(
    Output("graph", "figure"),
    Input("interval", "n_intervals"),
    Input("graph", "figure"),
    Input("url", "pathname"),
)
def get_graph(n, current_figure, url):
    method, folder, routine, qubit, format = url.split(os.sep)[2:]
    try:
        # data = DataUnits.load_data(folder, routine, format, "precision_sweep")
        # with open(f"{folder}/platform.yml", "r") as f:
        #     nqubits = yaml.safe_load(f)["nqubits"]
        # if len(data) > 2:
        #     params, fit = resonator_spectroscopy_fit(folder, format, nqubits)
        # else:
        #     params, fit = None, None
        # return getattr(plots.resonator_spectroscopy, method)(data, params, fit)

        # # FIXME: Temporarily hardcode the plotting method to test
        # # multiple routines with different names in one folder
        # # should be changed to:
        # # return getattr(getattr(plots, routine), method)(data)

        return getattr(plots, method)(folder, routine, qubit, format)
    except (FileNotFoundError, pd.errors.EmptyDataError):
        return current_figure

src/qibocal/web/__init__.py METASEP

src/qibocal/tests/test_data.py METASEP
"""Some tests for the Data and DataUnits class"""
import numpy as np
import pytest
from pint import DimensionalityError, UndefinedUnitError

from qibocal.data import Data, DataUnits


def random_data_units(length, options=None):
    data = DataUnits(options=options)
    for l in range(length):
        msr, i, q, phase = np.random.rand(4)
        pulse_sequence_result = {
            "MSR[V]": msr,
            "i[V]": i,
            "q[V]": q,
            "phase[deg]": phase,
        }
        add_options = {}
        if options is not None:
            for option in options:
                add_options[option] = str(l)
        data.add({**pulse_sequence_result, **add_options})

    return data


def random_data(length):
    data = Data()
    for i in range(length):
        data.add({"int": int(i), "float": float(i), "string": str(i), "bool": bool(i)})
    return data


def test_data_initialization():
    """Test DataUnits constructor"""
    data = DataUnits()
    assert len(data.df.columns) == 4
    assert list(data.df.columns) == [  # pylint: disable=E1101
        "MSR",
        "i",
        "q",
        "phase",
    ]

    data1 = DataUnits(quantities={"attenuation": "dB"})
    assert len(data1.df.columns) == 5
    assert list(data1.df.columns) == [  # pylint: disable=E1101
        "attenuation",
        "MSR",
        "i",
        "q",
        "phase",
    ]

    data2 = DataUnits(quantities={"attenuation": "dB"}, options=["option1"])
    assert len(data2.df.columns) == 6
    assert list(data2.df.columns) == [  # pylint: disable=E1101
        "option1",
        "attenuation",
        "MSR",
        "i",
        "q",
        "phase",
    ]


def test_data_units_units():
    """Test units of measure in DataUnits"""
    data_units = DataUnits()
    assert data_units.df.MSR.values.units == "volt"

    data_units1 = DataUnits(quantities={"frequency": "Hz"})
    assert data_units1.df.frequency.values.units == "hertz"

    with pytest.raises(UndefinedUnitError):
        data_units2 = DataUnits(quantities={"fake_unit": "fake"})


def test_data_units_add():
    """Test add method of DataUnits"""
    data_units = random_data_units(5)
    assert len(data_units) == 5

    data_units1 = DataUnits(quantities={"attenuation": "dB"})
    msr, i, q, phase, att = np.random.rand(len(data_units1.df.columns))
    data_units1.add(
        {
            "MSR[V]": msr,
            "i[V]": i,
            "q[V]": q,
            "phase[deg]": phase,
            "attenuation[dB]": att,
        }
    )
    assert len(data_units1) == 1

    data_units1.add(
        {
            "MSR[V]": 0,
            "i[V]": 0.0,
            "q[V]": 0.0,
            "phase[deg]": 0,
            "attenuation[dB]": 1,
        }
    )
    assert len(data_units1) == 2

    data_units2 = DataUnits()
    msr, i, q, phase = np.random.rand(len(data_units2.df.columns))
    with pytest.raises(DimensionalityError):
        data_units2.add({"MSR[dB]": msr, "i[V]": i, "q[V]": q, "phase[deg]": phase})

    with pytest.raises(UndefinedUnitError):
        data_units2.add({"MSR[test]": msr, "i[V]": i, "q[V]": q, "phase[deg]": phase})

    data_units3 = random_data_units(10, options=["test"])
    assert len(data_units3) == 10


def test_data_add():
    """Test add method of Data class"""
    data = random_data(5)
    assert len(data) == 5
    data.add({"int": 123, "float": 123.456, "string": "123", "bool": True})
    assert len(data) == 6


def test_data_units_load_data_from_dict():
    """Test set method of DataUnits class"""
    data_units = DataUnits()
    test = {
        "MSR[V]": [1, 2, 3],
        "i[V]": [3.0, 4.0, 5.0],
        "q[V]": np.array([3, 4, 5]),
        "phase[deg]": [6.0, 7.0, 8.0],
    }
    data_units.load_data_from_dict(test)
    assert len(data_units) == 3
    assert (data_units.get_values("MSR", "V") == [1, 2, 3]).all()
    assert (data_units.get_values("i", "V") == [3.0, 4.0, 5.0]).all()
    assert (data_units.get_values("q", "V") == [3, 4, 5]).all()
    assert (data_units.get_values("phase", "deg") == [6.0, 7.0, 8.0]).all()

    data_units1 = DataUnits(options=["option1", "option2"])
    test = {"option1": ["one", "two", "three"], "option2": [1, 2, 3]}
    data_units1.load_data_from_dict(test)
    assert len(data_units1) == 3
    assert (data_units1.get_values("option1") == ["one", "two", "three"]).all()
    assert (data_units1.get_values("option2") == [1, 2, 3]).all()


def test_data_load_data_from_dict():
    """Test set method of Data class"""
    data = random_data(5)
    test = {
        "int": [1, 2, 3],
        "float": [3.0, 4.0, 5.0],
        "string": ["one", "two", "three"],
        "bool": [True, False, True],
    }
    data.load_data_from_dict(test)
    assert len(data) == 3
    assert (data.get_values("int") == [1, 2, 3]).all()
    assert (data.get_values("float") == [3.0, 4.0, 5.0]).all()
    assert (data.get_values("string") == ["one", "two", "three"]).all()
    assert (data.get_values("bool") == [True, False, True]).all()


def test_get_values_data_units():
    """Test get_values method of DataUnits class"""
    data_units = random_data_units(5, options=["option"])

    assert (data_units.get_values("option") == data_units.df["option"]).all()
    assert (
        data_units.get_values("MSR", "uV")
        == data_units.df["MSR"].pint.to("uV").pint.magnitude
    ).all()


def test_get_values_data():
    """Test get_values method of Data class"""
    data = random_data(5)
    assert (data.get_values("int") == data.df["int"]).all()

src/qibocal/plots/t1.py METASEP
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import Data, DataUnits
from qibocal.fitting.utils import exp


# T1
def t1_time_msr_phase(folder, routine, qubit, format):
    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(quantities={"Time": "ns"})

    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = DataUnits()

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("Time", "ns"),
            y=data.get_values("MSR", "uV"),
            name="T1",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data.get_values("Time", "ns"),
            y=data.get_values("phase", "rad"),
            name="T1",
        ),
        row=1,
        col=2,
    )

    # add fitting trace
    if len(data) > 0 and len(data_fit) > 0:
        timerange = np.linspace(
            min(data.get_values("Time", "ns")),
            max(data.get_values("Time", "ns")),
            2 * len(data),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=timerange,
                y=exp(
                    timerange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.20,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.1f} ns.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    # last part
    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Time (ns)",
        yaxis_title="MSR (uV)",
        xaxis2_title="Time (ns)",
        yaxis2_title="Phase (rad)",
    )
    return fig

src/qibocal/plots/spectroscopies.py METASEP
import os

import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import Data, DataUnits
from qibocal.fitting.utils import lorenzian


def frequency_msr_phase__fast_precision(folder, routine, qubit, format):
    try:
        data_fast = DataUnits.load_data(folder, routine, format, f"fast_sweep_q{qubit}")
    except:
        data_fast = DataUnits(quantities={"frequency": "Hz"})
    try:
        data_precision = DataUnits.load_data(
            folder, routine, format, f"precision_sweep_q{qubit}"
        )
    except:
        data_precision = DataUnits(quantities={"frequency": "Hz"})
    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = Data(
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                "label1",
                "label2",
            ]
        )

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data_fast.get_values("frequency", "GHz"),
            y=data_fast.get_values("MSR", "uV"),
            name="Fast",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data_fast.get_values("frequency", "GHz"),
            y=data_fast.get_values("phase", "rad"),
            name="Fast",
        ),
        row=1,
        col=2,
    )
    fig.add_trace(
        go.Scatter(
            x=data_precision.get_values("frequency", "GHz"),
            y=data_precision.get_values("MSR", "uV"),
            name="Precision",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data_precision.get_values("frequency", "GHz"),
            y=data_precision.get_values("phase", "rad"),
            name="Precision",
        ),
        row=1,
        col=2,
    )
    if len(data_fast) > 0 and len(data_fit) > 0:
        freqrange = np.linspace(
            min(data_fast.get_values("frequency", "GHz")),
            max(data_fast.get_values("frequency", "GHz")),
            2 * len(data_fast),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=freqrange,
                y=lorenzian(
                    freqrange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )
        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.25,
                showarrow=False,
                text=f"The estimated {params[0]} is {data_fit.df[params[0]][0]:.1f} Hz.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )
        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.30,
                showarrow=False,
                text=f"The estimated {params[1]} is {data_fit.df[params[1]][0]:.3f} uV.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )
    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Frequency (GHz)",
        yaxis_title="MSR (uV)",
        xaxis2_title="Frequency (GHz)",
        yaxis2_title="Phase (rad)",
    )
    return fig


def frequency_attenuation_msr_phase__cut(folder, routine, qubit, format):
    data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    plot1d_attenuation = 30  # attenuation value to use for 1D frequency vs MSR plot

    fig = go.Figure()
    # index data on a specific attenuation value
    smalldf = data.df[data.get_values("attenuation", "dB") == plot1d_attenuation].copy()
    # split multiple software averages to different datasets
    datasets = []
    while len(smalldf):
        datasets.append(smalldf.drop_duplicates("frequency"))
        smalldf.drop(datasets[-1].index, inplace=True)
        fig.add_trace(
            go.Scatter(
                x=datasets[-1]["frequency"].pint.to("GHz").pint.magnitude,
                y=datasets[-1]["MSR"].pint.to("V").pint.magnitude,
            ),
        )

    fig.update_layout(
        showlegend=False,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting,
        xaxis_title="Frequency (GHz)",
        yaxis_title="MSR (V)",
    )
    return fig


def frequency_flux_msr_phase(folder, routine, qubit, format):
    data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Heatmap(
            x=data.get_values("frequency", "GHz"),
            y=data.get_values("current", "A"),
            z=data.get_values("MSR", "V"),
            colorbar_x=0.45,
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Heatmap(
            x=data.get_values("frequency", "GHz"),
            y=data.get_values("current", "A"),
            z=data.get_values("phase", "rad"),
            colorbar_x=1.0,
        ),
        row=1,
        col=2,
    )
    fig.update_layout(
        showlegend=False,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Frequency (GHz)",
        yaxis_title="Current (A)",
        xaxis2_title="Frequency (GHz)",
        yaxis2_title="Current (A)",
    )
    return fig


def frequency_flux_msr_phase__matrix(folder, routine, qubit, format):
    fluxes = []
    for i in range(25):  # FIXME: 25 is hardcoded
        file = f"{folder}/data/{routine}/data_q{qubit}_f{i}.csv"
        if os.path.exists(file):
            fluxes += [i]

    if len(fluxes) < 1:
        nb = 1
    else:
        nb = len(fluxes)
    fig = make_subplots(
        rows=2,
        cols=nb,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        x_title="Frequency (Hz)",
        y_title="Current (A)",
        shared_xaxes=True,
        shared_yaxes=True,
    )

    for j in fluxes:
        if j == fluxes[-1]:
            showscale = True
        else:
            showscale = False
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}_f{j}")
        fig.add_trace(
            go.Heatmap(
                x=data.get_values("frequency", "GHz"),
                y=data.get_values("current", "A"),
                z=data.get_values("MSR", "V"),
                showscale=showscale,
            ),
            row=1,
            col=j,
        )
        fig.add_trace(
            go.Heatmap(
                x=data.get_values("frequency", "GHz"),
                y=data.get_values("current", "A"),
                z=data.get_values("phase", "rad"),
                showscale=showscale,
            ),
            row=2,
            col=j,
        )
    fig.update_layout(
        showlegend=False,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
    )
    return fig


def frequency_attenuation_msr_phase(folder, routine, qubit, format):
    data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Heatmap(
            x=data.get_values("frequency", "GHz"),
            y=data.get_values("attenuation", "dB"),
            z=data.get_values("MSR", "V"),
            colorbar_x=0.45,
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Heatmap(
            x=data.get_values("frequency", "GHz"),
            y=data.get_values("attenuation", "dB"),
            z=data.get_values("phase", "rad"),
            colorbar_x=1.0,
        ),
        row=1,
        col=2,
    )
    fig.update_layout(
        showlegend=False,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Frequency (GHz)",
        yaxis_title="Attenuation (dB)",
        xaxis2_title="Frequency (GHz)",
        yaxis2_title="Attenuation (dB)",
    )
    return fig


def dispersive_frequency_msr_phase(folder, routine, qubit, formato):

    try:
        data_spec = DataUnits.load_data(folder, routine, formato, f"data_q{qubit}")
    except:
        data_spec = DataUnits(name=f"data_q{qubit}", quantities={"frequency": "Hz"})

    try:
        data_shifted = DataUnits.load_data(
            folder, routine, formato, f"data_shifted_q{qubit}"
        )
    except:
        data_shifted = DataUnits(
            name=f"data_shifted_q{qubit}", quantities={"frequency": "Hz"}
        )

    try:
        data_fit = Data.load_data(folder, routine, formato, f"fit_q{qubit}")
    except:
        data_fit = Data(
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                "label1",
                "label2",
            ]
        )

    try:
        data_fit_shifted = Data.load_data(
            folder, routine, formato, f"fit_shifted_q{qubit}"
        )
    except:
        data_fit_shifted = Data(
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                "label1",
                "label2",
            ]
        )

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data_spec.get_values("frequency", "GHz"),
            y=data_spec.get_values("MSR", "uV"),
            name="Spectroscopy",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data_spec.get_values("frequency", "GHz"),
            y=data_spec.get_values("phase", "rad"),
            name="Spectroscopy",
        ),
        row=1,
        col=2,
    )

    fig.add_trace(
        go.Scatter(
            x=data_shifted.get_values("frequency", "GHz"),
            y=data_shifted.get_values("MSR", "uV"),
            name="Shifted Spectroscopy",
        ),
        row=1,
        col=1,
    )

    fig.add_trace(
        go.Scatter(
            x=data_shifted.get_values("frequency", "GHz"),
            y=data_shifted.get_values("phase", "rad"),
            name="Shifted Spectroscopy",
        ),
        row=1,
        col=2,
    )

    # fitting traces
    if len(data_spec) > 0 and len(data_fit) > 0:
        freqrange = np.linspace(
            min(data_spec.get_values("frequency", "GHz")),
            max(data_spec.get_values("frequency", "GHz")),
            2 * len(data_spec),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=freqrange,
                y=lorenzian(
                    freqrange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                ),
                name="Fit spectroscopy",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )
        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.25,
                showarrow=False,
                text=f"The estimated {params[0]} is {data_fit.df[params[0]][0]:.1f} Hz.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    # fitting shifted  traces
    if len(data_shifted) > 0 and len(data_fit_shifted) > 0:
        freqrange = np.linspace(
            min(data_shifted.get_values("frequency", "GHz")),
            max(data_shifted.get_values("frequency", "GHz")),
            2 * len(data_shifted),
        )
        params = [i for i in list(data_fit_shifted.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=freqrange,
                y=lorenzian(
                    freqrange,
                    data_fit_shifted.get_values("popt0"),
                    data_fit_shifted.get_values("popt1"),
                    data_fit_shifted.get_values("popt2"),
                    data_fit_shifted.get_values("popt3"),
                ),
                name="Fit shifted spectroscopy",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )
        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.30,
                showarrow=False,
                text=f"The estimated shifted {params[0]} is {data_fit_shifted.df[params[0]][0]:.1f} Hz.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Frequency (GHz)",
        yaxis_title="MSR (uV)",
        xaxis2_title="Frequency (GHz)",
        yaxis2_title="Phase (rad)",
    )
    return fig

src/qibocal/plots/ramsey.py METASEP
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import Data, DataUnits
from qibocal.fitting.utils import ramsey


# For Ramsey oscillations
def time_msr(folder, routine, qubit, format):
    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(
            name=f"data_q{qubit}", quantities={"wait": "ns", "t_max": "ns"}
        )
    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = DataUnits()

    fig = make_subplots(
        rows=1,
        cols=1,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=("MSR (V)",),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("wait", "ns"),
            y=data.get_values("MSR", "uV"),
            name="Ramsey",
        ),
        row=1,
        col=1,
    )

    # add fitting trace
    if len(data) > 0 and len(data_fit) > 0:
        timerange = np.linspace(
            min(data.get_values("wait", "ns")),
            max(data.get_values("wait", "ns")),
            2 * len(data),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=timerange,
                y=ramsey(
                    timerange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                    data_fit.get_values("popt4"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.30,
                showarrow=False,
                text=f"Estimated {params[1]} is {data_fit.df[params[1]][0]:.3f} Hz.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.20,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.1f} ns",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.25,
                showarrow=False,
                text=f"Estimated {params[2]} is {data_fit.df[params[2]][0]:.3f} Hz",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Time (ns)",
        yaxis_title="MSR (uV)",
    )
    return fig

src/qibocal/plots/rabi.py METASEP
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import Data, DataUnits
from qibocal.fitting.utils import rabi


# For Rabi oscillations
def time_msr_phase(folder, routine, qubit, format):
    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(quantities={"Time": "ns"})

    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = Data(
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                "popt4",
                "label1",
                "label2",
            ]
        )

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("Time", "ns"),
            y=data.get_values("MSR", "uV"),
            name="Rabi Oscillations",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data.get_values("Time", "ns"),
            y=data.get_values("phase", "rad"),
            name="Rabi Oscillations",
        ),
        row=1,
        col=2,
    )

    # add fitting trace
    if len(data) > 0 and len(data_fit) > 0:
        timerange = np.linspace(
            min(data.get_values("Time", "ns")),
            max(data.get_values("Time", "ns")),
            2 * len(data),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=timerange,
                y=rabi(
                    timerange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                    data_fit.get_values("popt4"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.20,
                showarrow=False,
                text=f"Estimated {params[1]} is {data_fit.df[params[1]][0]:.3f} ns.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.30,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.1f} uV.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    # last part
    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Time (ns)",
        yaxis_title="MSR (uV)",
        xaxis2_title="Time (ns)",
        yaxis2_title="Phase (rad)",
    )
    return fig


def gain_msr_phase(folder, routine, qubit, format):

    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(quantities={"gain", "dimensionless"})

    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = Data(
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                "popt4",
                "label1",
                "label2",
            ]
        )

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("gain", "dimensionless"),
            y=data.get_values("MSR", "uV"),
            name="Rabi Oscillations",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data.get_values("gain", "dimensionless"),
            y=data.get_values("phase", "rad"),
            name="Rabi Oscillations",
        ),
        row=1,
        col=2,
    )

    # add fitting trace
    if len(data) > 0 and len(data_fit) > 0:
        gainrange = np.linspace(
            min(data.get_values("gain", "dimensionless")),
            max(data.get_values("gain", "dimensionless")),
            2 * len(data),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=gainrange,
                y=rabi(
                    gainrange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                    data_fit.get_values("popt4"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.20,
                showarrow=False,
                text=f"Estimated {params[1]} is {data_fit.df[params[1]][0]:.3f}",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.25,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.4f} uV",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Gain (dimensionless)",
        yaxis_title="MSR (uV)",
    )
    return fig


def amplitude_msr_phase(folder, routine, qubit, format):

    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(quantities={"amplitude", "dimensionless"})
    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = DataUnits()

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("amplitude", "dimensionless"),
            y=data.get_values("MSR", "uV"),
            name="Rabi Oscillations",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data.get_values("amplitude", "dimensionless"),
            y=data.get_values("phase", "rad"),
            name="Rabi Oscillations",
        ),
        row=1,
        col=2,
    )

    # add fitting trace
    if len(data) > 0 and len(data_fit) > 0:
        amplituderange = np.linspace(
            min(data.get_values("amplitude", "dimensionless")),
            max(data.get_values("amplitude", "dimensionless")),
            2 * len(data),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=amplituderange,
                y=rabi(
                    amplituderange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                    data_fit.get_values("popt4"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.30,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.3f} uV.",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.25,
                showarrow=False,
                text=f"Estimated {params[1]} is {data_fit.df[params[1]][0]:.4f}",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Amplitude (dimensionless)",
        yaxis_title="MSR (uV)",
    )
    return fig


def duration_gain_msr_phase(folder, routine, qubit, format):
    data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Heatmap(
            x=data.get_values("duration", "ns"),
            y=data.get_values("gain", "dimensionless"),
            z=data.get_values("MSR", "V"),
            colorbar_x=0.45,
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Heatmap(
            x=data.get_values("duration", "ns"),
            y=data.get_values("gain", "dimensionless"),
            z=data.get_values("phase", "rad"),
            colorbar_x=1.0,
        ),
        row=1,
        col=2,
    )
    fig.update_layout(
        showlegend=False,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="duration (ns)",
        yaxis_title="gain (dimensionless)",
        xaxis2_title="duration (ns)",
        yaxis2_title="gain (dimensionless)",
    )
    return fig


def duration_amplitude_msr_phase(folder, routine, qubit, format):
    data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Heatmap(
            x=data.get_values("duration", "ns"),
            y=data.get_values("amplitude", "dimensionless"),
            z=data.get_values("MSR", "V"),
            colorbar_x=0.45,
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Heatmap(
            x=data.get_values("duration", "ns"),
            y=data.get_values("amplitude", "dimensionless"),
            z=data.get_values("phase", "rad"),
            colorbar_x=1.0,
        ),
        row=1,
        col=2,
    )
    fig.update_layout(
        showlegend=False,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="duration (ns)",
        yaxis_title="amplitude (dimensionless)",
        xaxis2_title="duration (ns)",
        yaxis2_title="amplitude (dimensionless)",
    )
    return fig

src/qibocal/plots/flipping.py METASEP
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import Data, DataUnits
from qibocal.fitting.utils import flipping


# Flipping
def flips_msr_phase(folder, routine, qubit, format):
    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(quantities={"flips": "dimensionless"})

    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = DataUnits()

    fig = make_subplots(
        rows=1,
        cols=2,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(
            "MSR (V)",
            "phase (rad)",
        ),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("flips", "dimensionless"),
            y=data.get_values("MSR", "uV"),
            name="Flipping MSR",
        ),
        row=1,
        col=1,
    )
    fig.add_trace(
        go.Scatter(
            x=data.get_values("flips", "dimensionless"),
            y=data.get_values("phase", "rad"),
            name="Flipping Phase",
        ),
        row=1,
        col=2,
    )

    # add fitting trace
    if len(data) > 0 and len(data_fit) > 0:
        flipsrange = np.linspace(
            min(data.get_values("flips", "dimensionless")),
            max(data.get_values("flips", "dimensionless")),
            2 * len(data),
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=flipsrange,
                y=flipping(
                    flipsrange,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.25,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.4f}",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )
        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.30,
                showarrow=False,
                text=f"Estimated {params[1]} is {data_fit.df[params[1]][0]:.3f}",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    # last part
    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Flips (dimensionless)",
        yaxis_title="MSR (uV)",
        xaxis2_title="Flips (dimensionless)",
        yaxis2_title="Phase (rad)",
    )
    return fig

src/qibocal/plots/calibrate_qubit_states.py METASEP
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import DataUnits


# For calibrate qubit states
def exc_gnd(folder, routine, qubit, format):

    try:
        data_exc = DataUnits.load_data(folder, routine, format, f"data_exc_q{qubit}")
    except:
        data_exc = DataUnits(quantities={"iteration": "dimensionless"})

    fig = make_subplots(
        rows=1,
        cols=1,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=("Calibrate qubit states",),
    )

    fig.add_trace(
        go.Scatter(
            x=data_exc.get_values("i", "V"),
            y=data_exc.get_values("q", "V"),
            name="exc_state",
            mode="markers",
            marker=dict(size=3, color="lightcoral"),
        ),
        row=1,
        col=1,
    )

    try:
        data_gnd = DataUnits.load_data(folder, routine, format, f"data_gnd_q{qubit}")
    except:
        data_gnd = DataUnits(quantities={"iteration": "dimensionless"})

    fig.add_trace(
        go.Scatter(
            x=data_gnd.get_values("i", "V"),
            y=data_gnd.get_values("q", "V"),
            name="gnd state",
            mode="markers",
            marker=dict(size=3, color="skyblue"),
        ),
        row=1,
        col=1,
    )

    i_exc = data_exc.get_values("i", "V")
    q_exc = data_exc.get_values("q", "V")

    i_mean_exc = i_exc.mean()
    q_mean_exc = q_exc.mean()
    iq_mean_exc = complex(i_mean_exc, q_mean_exc)
    mod_iq_exc = abs(iq_mean_exc) * 1e6

    fig.add_trace(
        go.Scatter(
            x=[i_mean_exc],
            y=[q_mean_exc],
            name=f" state1_voltage: {mod_iq_exc} <br> mean_state1: {iq_mean_exc}",
            mode="markers",
            marker=dict(size=10, color="red"),
        ),
        row=1,
        col=1,
    )

    i_gnd = data_gnd.get_values("i", "V")
    q_gnd = data_gnd.get_values("q", "V")

    i_mean_gnd = i_gnd.mean()
    q_mean_gnd = q_gnd.mean()
    iq_mean_gnd = complex(i_mean_gnd, q_mean_gnd)
    mod_iq_gnd = abs(iq_mean_gnd) * 1e6

    fig.add_trace(
        go.Scatter(
            x=[i_mean_gnd],
            y=[q_mean_gnd],
            name=f" state0_voltage: {mod_iq_gnd} <br> mean_state0: {iq_mean_gnd}",
            mode="markers",
            marker=dict(size=10, color="blue"),
        ),
        row=1,
        col=1,
    )

    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="i (V)",
        yaxis_title="q (V)",
        width=1000,
    )

    return fig

src/qibocal/plots/allXY.py METASEP
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from qibocal.data import Data, DataUnits
from qibocal.fitting.utils import cos


# allXY
def prob_gate(folder, routine, qubit, format):

    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(
            quantities={"probability": "dimensionless", "gateNumber": "dimensionless"}
        )

    fig = make_subplots(
        rows=1,
        cols=1,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(f"allXY",),
    )

    fig.add_trace(
        go.Scatter(
            x=data.get_values("gateNumber", "dimensionless"),
            y=data.get_values("probability", "dimensionless"),
            mode="markers",
            name="Probabilities",
        ),
        row=1,
        col=1,
    )
    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Gate sequence number",
        yaxis_title="Z projection probability of qubit state |o>",
    )
    return fig


# allXY
def prob_gate_iteration(folder, routine, qubit, format):

    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(
            quantities={
                "probability": "dimensionless",
                "gateNumber": "dimensionless",
                "beta_param": "dimensionless",
            }
        )

    data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    fig = make_subplots(
        rows=1,
        cols=1,
        horizontal_spacing=0.1,
        vertical_spacing=0.1,
        subplot_titles=(f"allXY",),
    )

    gates = len(data.get_values("gateNumber", "dimensionless"))
    # print(gates)
    import numpy as np

    for n in range(gates // 21):
        data_start = n * 21
        data_end = data_start + 21
        beta_param = np.array(data.get_values("beta_param", "dimensionless"))[
            data_start
        ]
        gates = np.array(data.get_values("gateNumber", "dimensionless"))[
            data_start:data_end
        ]
        probabilities = np.array(data.get_values("probability", "dimensionless"))[
            data_start:data_end
        ]
        c = "#" + "{:06x}".format(n * 99999)
        fig.add_trace(
            go.Scatter(
                x=gates,
                y=probabilities,
                mode="markers+lines",
                line=dict(color=c),
                name=f"beta_parameter = {beta_param}",
                marker_size=16,
            ),
            row=1,
            col=1,
        )
    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Gate sequence number",
        yaxis_title="Z projection probability of qubit state |o>",
    )
    return fig


# beta param tuning
def msr_beta(folder, routine, qubit, format):

    try:
        data = DataUnits.load_data(folder, routine, format, f"data_q{qubit}")
    except:
        data = DataUnits(
            name=f"data_q{qubit}", quantities={"beta_param": "dimensionless"}
        )
    try:
        data_fit = Data.load_data(folder, routine, format, f"fit_q{qubit}")
    except:
        data_fit = DataUnits()

    fig = make_subplots(
        rows=1,
        cols=1,
        horizontal_spacing=0.01,
        vertical_spacing=0.01,
        subplot_titles=(f"beta_param_tuning",),
    )

    c = "#6597aa"
    fig.add_trace(
        go.Scatter(
            x=data.get_values("beta_param", "dimensionless"),
            y=data.get_values("MSR", "uV"),
            line=dict(color=c),
            mode="markers",
            name="[Rx(pi/2) - Ry(pi)] - [Ry(pi/2) - Rx(pi)]",
        ),
        row=1,
        col=1,
    )
    # add fitting traces
    if len(data) > 0 and len(data_fit) > 0:
        beta_param = np.linspace(
            min(data.get_values("beta_param", "dimensionless")),
            max(data.get_values("beta_param", "dimensionless")),
            20,
        )
        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        fig.add_trace(
            go.Scatter(
                x=beta_param,
                y=cos(
                    beta_param,
                    data_fit.get_values("popt0"),
                    data_fit.get_values("popt1"),
                    data_fit.get_values("popt2"),
                    data_fit.get_values("popt3"),
                ),
                name="Fit",
                line=go.scatter.Line(dash="dot"),
            ),
            row=1,
            col=1,
        )

        fig.add_annotation(
            dict(
                font=dict(color="black", size=12),
                x=0,
                y=-0.20,
                showarrow=False,
                text=f"Estimated {params[0]} is {data_fit.df[params[0]][0]:.4f}",
                textangle=0,
                xanchor="left",
                xref="paper",
                yref="paper",
            )
        )

    fig.update_layout(
        showlegend=True,
        uirevision="0",  # ``uirevision`` allows zooming while live plotting
        xaxis_title="Beta parameter",
        yaxis_title="MSR[uV]",
    )
    return fig

src/qibocal/plots/__init__.py METASEP
from qibocal.plots.allXY import *
from qibocal.plots.calibrate_qubit_states import *
from qibocal.plots.flipping import *
from qibocal.plots.rabi import *
from qibocal.plots.ramsey import *
from qibocal.plots.spectroscopies import *
from qibocal.plots.t1 import *

src/qibocal/fitting/utils.py METASEP
import re

import numpy as np


def lorenzian(frequency, amplitude, center, sigma, offset):
    # http://openafox.com/science/peak-function-derivations.html
    return (amplitude / np.pi) * (
        sigma / ((frequency - center) ** 2 + sigma**2)
    ) + offset


def rabi(x, p0, p1, p2, p3, p4):
    # A fit to Superconducting Qubit Rabi Oscillation
    #   Offset                       : p[0]
    #   Oscillation amplitude        : p[1]
    #   Period    T                  : 1/p[2]
    #   Phase                        : p[3]
    #   Arbitrary parameter T_2      : 1/p[4]
    return p0 + p1 * np.sin(2 * np.pi * x * p2 + p3) * np.exp(-x * p4)


def ramsey(x, p0, p1, p2, p3, p4):
    # A fit to Superconducting Qubit Rabi Oscillation
    #   Offset                       : p[0]
    #   Oscillation amplitude        : p[1]
    #   DeltaFreq                    : p[2]
    #   Phase                        : p[3]
    #   Arbitrary parameter T_2      : 1/p[4]
    return p0 + p1 * np.sin(2 * np.pi * x * p2 + p3) * np.exp(-x * p4)


def exp(x, *p):
    return p[0] - p[1] * np.exp(-1 * x * p[2])


def flipping(x, p0, p1, p2, p3):
    # A fit to Flipping Qubit oscillation
    # Epsilon?? shoule be Amplitude : p[0]
    # Offset                        : p[1]
    # Period of oscillation         : p[2]
    # phase for the first point corresponding to pi/2 rotation   : p[3]
    return np.sin(x * 2 * np.pi / p2 + p3) * p0 + p1


def cos(x, p0, p1, p2, p3):
    # Offset                  : p[0]
    # Amplitude               : p[1]
    # Period                  : p[2]
    # Phase                   : p[3]
    return p0 + p1 * np.cos(2 * np.pi * x / p2 + p3)


def parse(key):
    name = key.split("[")[0]
    unit = re.search(r"\[([A-Za-z0-9_]+)\]", key).group(1)
    return name, unit

src/qibocal/fitting/methods.py METASEP
"""Routine-specific method for post-processing data acquired."""
import lmfit
import numpy as np
from scipy.optimize import curve_fit

from qibocal.config import log
from qibocal.data import Data
from qibocal.fitting.utils import cos, exp, flipping, lorenzian, parse, rabi, ramsey


def lorentzian_fit(data, x, y, qubit, nqubits, labels, fit_file_name=None):
    """Fitting routine for resonator spectroscopy"""
    if fit_file_name == None:
        data_fit = Data(
            name=f"fit_q{qubit}",
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                labels[1],
                labels[0],
            ],
        )
    else:
        data_fit = Data(
            name=fit_file_name + f"_q{qubit}",
            quantities=[
                "popt0",
                "popt1",
                "popt2",
                "popt3",
                labels[1],
                labels[0],
            ],
        )

    frequencies = data.get_values(*parse(x))
    voltages = data.get_values(*parse(y))

    # Create a lmfit model for fitting equation defined in resonator_peak
    model_Q = lmfit.Model(lorenzian)

    # Guess parameters for Lorentzian max or min
    if (nqubits == 1 and labels[0] == "resonator_freq") or (
        nqubits != 1 and labels[0] == "qubit_freq"
    ):
        guess_center = frequencies[
            np.argmax(voltages)
        ]  # Argmax = Returns the indices of the maximum values along an axis.
        guess_offset = np.mean(
            voltages[np.abs(voltages - np.mean(voltages) < np.std(voltages))]
        )
        guess_sigma = abs(frequencies[np.argmin(voltages)] - guess_center)
        guess_amp = (np.max(voltages) - guess_offset) * guess_sigma * np.pi

    else:
        guess_center = frequencies[
            np.argmin(voltages)
        ]  # Argmin = Returns the indices of the minimum values along an axis.
        guess_offset = np.mean(
            voltages[np.abs(voltages - np.mean(voltages) < np.std(voltages))]
        )
        guess_sigma = abs(frequencies[np.argmax(voltages)] - guess_center)
        guess_amp = (np.min(voltages) - guess_offset) * guess_sigma * np.pi

    # Add guessed parameters to the model
    model_Q.set_param_hint("center", value=guess_center, vary=True)
    model_Q.set_param_hint("sigma", value=guess_sigma, vary=True)
    model_Q.set_param_hint("amplitude", value=guess_amp, vary=True)
    model_Q.set_param_hint("offset", value=guess_offset, vary=True)
    guess_parameters = model_Q.make_params()

    # fit the model with the data and guessed parameters
    try:
        fit_res = model_Q.fit(
            data=voltages, frequency=frequencies, params=guess_parameters
        )
    except:
        log.warning("The fitting was not successful")
        return data_fit

    # get the values for postprocessing and for legend.
    f0 = fit_res.best_values["center"]
    BW = fit_res.best_values["sigma"] * 2
    Q = abs(f0 / BW)
    peak_voltage = (
        fit_res.best_values["amplitude"] / (fit_res.best_values["sigma"] * np.pi)
        + fit_res.best_values["offset"]
    )

    freq = f0 * 1e9

    data_fit.add(
        {
            labels[1]: peak_voltage,
            labels[0]: freq,
            "popt0": fit_res.best_values["amplitude"],
            "popt1": fit_res.best_values["center"],
            "popt2": fit_res.best_values["sigma"],
            "popt3": fit_res.best_values["offset"],
        }
    )
    return data_fit


def rabi_fit(data, x, y, qubit, nqubits, labels):
    data_fit = Data(
        name=f"fit_q{qubit}",
        quantities=[
            "popt0",
            "popt1",
            "popt2",
            "popt3",
            "popt4",
            labels[0],
            labels[1],
        ],
    )

    time = data.get_values(*parse(x))
    voltages = data.get_values(*parse(y))

    if nqubits == 1:
        pguess = [
            np.mean(voltages.values),
            np.max(voltages.values) - np.min(voltages.values),
            0.5 / time.values[np.argmin(voltages.values)],
            np.pi / 2,
            0.1e-6,
        ]
    else:
        pguess = [
            np.mean(voltages.values),
            np.max(voltages.values) - np.min(voltages.values),
            0.5 / time.values[np.argmax(voltages.values)],
            np.pi / 2,
            0.1e-6,
        ]
    try:
        popt, pcov = curve_fit(
            rabi, time.values, voltages.values, p0=pguess, maxfev=10000
        )
        smooth_dataset = rabi(time.values, *popt)
        pi_pulse_duration = np.abs((1.0 / popt[2]) / 2)
        pi_pulse_max_voltage = smooth_dataset.max()
        t2 = 1.0 / popt[4]  # double check T1
    except:
        log.warning("The fitting was not succesful")
        return data_fit

    data_fit.add(
        {
            "popt0": popt[0],
            "popt1": popt[1],
            "popt2": popt[2],
            "popt3": popt[3],
            "popt4": popt[4],
            labels[0]: pi_pulse_duration,
            labels[1]: pi_pulse_max_voltage,
        }
    )
    return data_fit


def ramsey_fit(data, x, y, qubit, qubit_freq, sampling_rate, offset_freq, labels):

    data_fit = Data(
        name=f"fit_q{qubit}",
        quantities=[
            "popt0",
            "popt1",
            "popt2",
            "popt3",
            "popt4",
            labels[0],
            labels[1],
            labels[2],
        ],
    )

    time = data.get_values(*parse(x))
    voltages = data.get_values(*parse(y))

    pguess = [
        np.mean(voltages.values),
        np.max(voltages.values) - np.min(voltages.values),
        0.5 / time.values[np.argmin(voltages.values)],
        np.pi / 2,
        500e-9,
    ]

    try:
        popt, pcov = curve_fit(
            ramsey, time.values, voltages.values, p0=pguess, maxfev=2000000
        )
        delta_fitting = popt[2]
        delta_phys = int((delta_fitting * sampling_rate) - offset_freq)
        corrected_qubit_frequency = int(qubit_freq + delta_phys)
        t2 = 1.0 / popt[4]
    except:
        log.warning("The fitting was not succesful")
        return data_fit

    data_fit.add(
        {
            "popt0": popt[0],
            "popt1": popt[1],
            "popt2": popt[2],
            "popt3": popt[3],
            "popt4": popt[4],
            labels[0]: delta_phys,
            labels[1]: corrected_qubit_frequency,
            labels[2]: t2,
        }
    )
    return data_fit


def t1_fit(data, x, y, qubit, nqubits, labels):

    data_fit = Data(
        name=f"fit_q{qubit}",
        quantities=[
            "popt0",
            "popt1",
            "popt2",
            labels[0],
        ],
    )

    time = data.get_values(*parse(x))
    voltages = data.get_values(*parse(y))

    if nqubits == 1:
        pguess = [
            max(voltages.values),
            (max(voltages.values) - min(voltages.values)),
            1 / 250,
        ]
    else:
        pguess = [
            min(voltages.values),
            (max(voltages.values) - min(voltages.values)),
            1 / 250,
        ]

    try:
        popt, pcov = curve_fit(
            exp, time.values, voltages.values, p0=pguess, maxfev=2000000
        )
        t1 = abs(1 / popt[2])

    except:
        log.warning("The fitting was not succesful")
        return data_fit

    data_fit.add(
        {
            "popt0": popt[0],
            "popt1": popt[1],
            "popt2": popt[2],
            labels[0]: t1,
        }
    )
    return data_fit


def flipping_fit(data, x, y, qubit, nqubits, niter, pi_pulse_amplitude, labels):

    data_fit = Data(
        name=f"fit_q{qubit}",
        quantities=[
            "popt0",
            "popt1",
            "popt2",
            "popt3",
            labels[0],
            labels[1],
        ],
    )

    flips = data.get_values(*parse(x))  # Check X data stores. N flips or i?
    voltages = data.get_values(*parse(y))

    if nqubits == 1:
        pguess = [0.0003, np.mean(voltages), -18, 0]  # epsilon guess parameter
    else:
        pguess = [0.0003, np.mean(voltages), 18, 0]  # epsilon guess parameter

    try:
        popt, pcov = curve_fit(flipping, flips, voltages, p0=pguess, maxfev=2000000)
        epsilon = -np.pi / popt[2]
        amplitude_delta = np.pi / (np.pi + epsilon)
        corrected_amplitude = amplitude_delta * pi_pulse_amplitude
        # angle = (niter * 2 * np.pi / popt[2] + popt[3]) / (1 + 4 * niter)
        # amplitude_delta = angle * 2 / np.pi * pi_pulse_amplitude
    except:
        log.warning("The fitting was not succesful")
        return data_fit

    data_fit.add(
        {
            "popt0": popt[0],
            "popt1": popt[1],
            "popt2": popt[2],
            "popt3": popt[3],
            labels[0]: amplitude_delta,
            labels[1]: corrected_amplitude,
        }
    )
    return data_fit


def drag_tunning_fit(data, x, y, qubit, nqubits, labels):

    data_fit = Data(
        name=f"fit_q{qubit}",
        quantities=[
            "popt0",
            "popt1",
            "popt2",
            "popt3",
            labels[0],
        ],
    )

    beta_params = data.get_values(*parse(x))
    voltages = data.get_values(*parse(y))

    pguess = [
        0,  # Offset:    p[0]
        beta_params.values[np.argmax(voltages)]
        - beta_params.values[np.argmin(voltages)],  # Amplitude: p[1]
        4,  # Period:    p[2]
        0.3,  # Phase:     p[3]
    ]

    try:
        popt, pcov = curve_fit(cos, beta_params.values, voltages.values)
        smooth_dataset = cos(beta_params.values, popt[0], popt[1], popt[2], popt[3])
        beta_optimal = beta_params.values[np.argmin(smooth_dataset)]

    except:
        log.warning("The fitting was not succesful")
        return data_fit

    data_fit.add(
        {
            "popt0": popt[0],
            "popt1": popt[1],
            "popt2": popt[2],
            "popt3": popt[3],
            labels[0]: beta_optimal,
        }
    )
    return data_fit

src/qibocal/fitting/__init__.py METASEP

src/qibocal/cli/builders.py METASEP
import datetime
import inspect
import os
import shutil

import yaml

from qibocal import calibrations
from qibocal.config import log, raise_error
from qibocal.data import Data


def load_yaml(path):
    """Load yaml file from disk."""
    with open(path) as file:
        data = yaml.safe_load(file)
    return data


class ActionBuilder:
    """Class for parsing and executing runcards.
    Args:
        runcard (path): path containing the runcard.
        folder (path): path for the output folder.
        force (bool): option to overwrite the output folder if it exists already.
    """

    def __init__(self, runcard, folder=None, force=False):
        path, self.folder = self._generate_output_folder(folder, force)
        self.runcard = load_yaml(runcard)
        # Qibolab default backend if not provided in runcard.
        backend_name = self.runcard.get("backend", "qibolab")
        platform_name = self.runcard.get("platform", "dummy")
        self.backend, self.platform = self._allocate_backend(
            backend_name, platform_name, path
        )
        self.qubits = self.runcard["qubits"]
        self.format = self.runcard["format"]

        # Saving runcard
        shutil.copy(runcard, f"{path}/runcard.yml")
        self.save_meta(path, self.folder)

    @staticmethod
    def _generate_output_folder(folder, force):
        """Static method for generating the output folder.
        Args:
            folder (path): path for the output folder. If None it will be created a folder automatically
            force (bool): option to overwrite the output folder if it exists already.
        """
        if folder is None:
            import getpass

            e = datetime.datetime.now()
            user = getpass.getuser().replace(".", "-")
            date = e.strftime("%Y-%m-%d")
            folder = f"{date}-{'000'}-{user}"
            num = 0
            while os.path.exists(folder):
                log.info(f"Directory {folder} already exists.")
                num += 1
                folder = f"{date}-{str(num).rjust(3, '0')}-{user}"
                log.info(f"Trying to create directory {folder}")
        elif os.path.exists(folder) and not force:
            raise_error(RuntimeError, f"Directory {folder} already exists.")
        elif os.path.exists(folder) and force:
            log.warning(f"Deleting previous directory {folder}.")
            shutil.rmtree(os.path.join(os.getcwd(), folder))

        path = os.path.join(os.getcwd(), folder)
        log.info(f"Creating directory {folder}.")
        os.makedirs(path)
        return path, folder

    def _allocate_backend(self, backend_name, platform_name, path):
        """Allocate the platform using Qibolab."""
        from qibo.backends import GlobalBackend, set_backend

        if backend_name == "qibolab":
            from qibolab.paths import qibolab_folder

            original_runcard = qibolab_folder / "runcards" / f"{platform_name}.yml"
            # copy of the original runcard that will stay unmodified
            shutil.copy(original_runcard, f"{path}/platform.yml")
            # copy of the original runcard that will be modified during calibration
            updated_runcard = f"{self.folder}/new_platform.yml"
            shutil.copy(original_runcard, updated_runcard)
            # allocate backend with updated_runcard
            set_backend(
                backend=backend_name, platform=platform_name, runcard=updated_runcard
            )
            backend = GlobalBackend()
            return backend, backend.platform
        else:
            set_backend(backend=backend_name, platform=platform_name)
            backend = GlobalBackend()
            return backend, None

    def save_meta(self, path, folder):
        import qibocal

        e = datetime.datetime.now(datetime.timezone.utc)
        meta = {}
        meta["title"] = folder
        meta["backend"] = str(self.backend)
        meta["platform"] = str(self.backend.platform)
        meta["date"] = e.strftime("%Y-%m-%d")
        meta["start-time"] = e.strftime("%H:%M:%S")
        meta["end-time"] = e.strftime("%H:%M:%S")
        meta["versions"] = self.backend.versions  # pylint: disable=E1101
        meta["versions"]["qibocal"] = qibocal.__version__

        with open(f"{path}/meta.yml", "w") as file:
            yaml.dump(meta, file)

    def _build_single_action(self, name):
        """Helper method to parse the actions in the runcard."""
        f = getattr(calibrations, name)
        path = os.path.join(self.folder, f"data/{name}/")
        os.makedirs(path)
        sig = inspect.signature(f)
        params = self.runcard["actions"][name]
        for param in list(sig.parameters)[2:-1]:
            if param not in params:
                raise_error(AttributeError, f"Missing parameter {param} in runcard.")
        if f.__annotations__["qubit"] == int:
            single_qubit_action = True
        else:
            single_qubit_action = False

        return f, params, path, single_qubit_action

    def execute(self):
        """Method to execute sequentially all the actions in the runcard."""
        if self.platform is not None:
            self.platform.connect()
            self.platform.setup()
            self.platform.start()

        for action in self.runcard["actions"]:
            routine, args, path, single_qubit_action = self._build_single_action(action)
            self._execute_single_action(routine, args, path, single_qubit_action)

        if self.platform is not None:
            self.platform.stop()
            self.platform.disconnect()

    def _execute_single_action(self, routine, arguments, path, single_qubit):
        """Method to execute a single action and retrieving the results."""
        if self.format is None:
            raise_error(ValueError, f"Cannot store data using {self.format} format.")
        if single_qubit:
            for qubit in self.qubits:
                results = routine(self.platform, qubit, **arguments)

                for data in results:
                    getattr(data, f"to_{self.format}")(path)

                if self.platform is not None:
                    self.update_platform_runcard(qubit, routine.__name__)
        else:
            results = routine(self.platform, self.qubits, **arguments)

            for data in results:
                getattr(data, f"to_{self.format}")(path)

            if self.platform is not None:
                self.update_platform_runcard(qubit, routine.__name__)

    def update_platform_runcard(self, qubit, routine):

        try:
            data_fit = Data.load_data(
                self.folder, routine, self.format, f"fit_q{qubit}"
            )
        except:
            data_fit = Data()

        params = [i for i in list(data_fit.df.keys()) if "popt" not in i]
        settings = load_yaml(f"{self.folder}/new_platform.yml")

        for param in params:
            settings["characterization"]["single_qubit"][qubit][param] = int(
                data_fit.get_values(param)
            )

        with open(f"{self.folder}/new_platform.yml", "w") as file:
            yaml.dump(
                settings, file, sort_keys=False, indent=4, default_flow_style=None
            )

    def dump_report(self):
        from qibocal.web.report import create_report

        # update end time
        meta = load_yaml(f"{self.folder}/meta.yml")
        e = datetime.datetime.now(datetime.timezone.utc)
        meta["end-time"] = e.strftime("%H:%M:%S")
        with open(f"{self.folder}/meta.yml", "w") as file:
            yaml.dump(meta, file)

        create_report(self.folder)


class ReportBuilder:
    """Parses routines and plots to report and live plotting page.

    Args:
        path (str): Path to the data folder to generate report for.
    """

    def __init__(self, path):
        self.path = path
        self.metadata = load_yaml(os.path.join(path, "meta.yml"))

        # find proper path title
        base, self.title = os.path.join(os.getcwd(), path), ""
        while self.title in ("", "."):
            base, self.title = os.path.split(base)

        self.runcard = load_yaml(os.path.join(path, "runcard.yml"))
        self.format = self.runcard.get("format")
        self.qubits = self.runcard.get("qubits")

        # create calibration routine objects
        # (could be incorporated to :meth:`qibocal.cli.builders.ActionBuilder._build_single_action`)
        self.routines = []
        for action in self.runcard.get("actions"):
            if hasattr(calibrations, action):
                routine = getattr(calibrations, action)
            else:
                raise_error(ValueError, f"Undefined action {action} in report.")

            if not hasattr(routine, "plots"):
                routine.plots = []
            self.routines.append(routine)

    def get_routine_name(self, routine):
        """Prettify routine's name for report headers."""
        return routine.__name__.replace("_", " ").title()

    def get_figure(self, routine, method, qubit):
        """Get html figure for report.

        Args:
            routine (Callable): Calibration method.
            method (Callable): Plot method.
            qubit (int): Qubit id.
        """
        import tempfile

        figure = method(self.path, routine.__name__, qubit, self.format)
        with tempfile.NamedTemporaryFile() as temp:
            figure.write_html(temp.name, include_plotlyjs=False, full_html=False)
            fightml = temp.read().decode("utf-8")
        return fightml

    def get_live_figure(self, routine, method, qubit):
        """Get url to dash page for live plotting.

        This url is used by :meth:`qibocal.web.app.get_graph`.

        Args:
            routine (Callable): Calibration method.
            method (Callable): Plot method.
            qubit (int): Qubit id.
        """
        return os.path.join(
            method.__name__,
            self.path,
            routine.__name__,
            str(qubit),
            self.format,
        )

src/qibocal/cli/_base.py METASEP
"""Adds global CLI options."""
import base64
import pathlib
import shutil
import socket
import subprocess
import uuid
from urllib.parse import urljoin

import click
from qibo.config import log, raise_error

from qibocal.cli.builders import ActionBuilder

CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])

# options for report upload
UPLOAD_HOST = (
    "qibocal@localhost"
    if socket.gethostname() == "saadiyat"
    else "qibocal@login.qrccluster.com"
)
TARGET_DIR = "qibocal-reports/"
ROOT_URL = "http://login.qrccluster.com:9000/"


@click.command(context_settings=CONTEXT_SETTINGS)
@click.argument("runcard", metavar="RUNCARD", type=click.Path(exists=True))
@click.option(
    "folder",
    "-o",
    type=click.Path(),
    help="Output folder. If not provided a standard name will generated.",
)
@click.option(
    "force",
    "-f",
    is_flag=True,
    help="Use --force option to overwrite the output folder.",
)
def command(runcard, folder, force=None):

    """qibocal: Quantum Calibration Verification and Validation using Qibo.

    Arguments:

     - RUNCARD: runcard with declarative inputs.
    """

    builder = ActionBuilder(runcard, folder, force)
    builder.execute()
    builder.dump_report()


@click.command(context_settings=CONTEXT_SETTINGS)
@click.option(
    "port",
    "-p",
    "--port",
    default=8050,
    type=int,
    help="Localhost port to launch dash server.",
)
@click.option(
    "debug",
    "-d",
    "--debug",
    is_flag=True,
    help="Launch server in debugging mode.",
)
def live_plot(port, debug):
    """Real time plotting of calibration data on a dash server."""
    import socket

    from qibocal.web.app import app

    # change port if it is already used
    while True:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            if s.connect_ex(("localhost", port)) != 0:
                break
        port += 1

    app.run_server(debug=debug, port=port)


@click.command(context_settings=CONTEXT_SETTINGS)
@click.argument("output_folder", metavar="FOLDER", type=click.Path(exists=True))
def upload(output_folder):
    """Uploads output folder to server"""

    output_path = pathlib.Path(output_folder)

    # check the rsync command exists.
    if not shutil.which("rsync"):
        raise_error(
            RuntimeError,
            "Could not find the rsync command. Please make sure it is installed.",
        )

    # check that we can authentica with a certificate
    ssh_command_line = (
        "ssh",
        "-o",
        "PreferredAuthentications=publickey",
        "-q",
        UPLOAD_HOST,
        "exit",
    )

    str_line = " ".join(repr(ele) for ele in ssh_command_line)

    log.info(f"Checking SSH connection to {UPLOAD_HOST}.")

    try:
        subprocess.run(ssh_command_line, check=True)
    except subprocess.CalledProcessError as e:
        raise RuntimeError(
            (
                "Could not validate the SSH key. "
                "The command\n%s\nreturned a non zero exit status. "
                "Please make sure that your public SSH key is on the server."
            )
            % str_line
        ) from e
    except OSError as e:
        raise RuntimeError(
            "Could not run the command\n{}\n: {}".format(str_line, e)
        ) from e

    log.info("Connection seems OK.")

    # upload output
    randname = base64.urlsafe_b64encode(uuid.uuid4().bytes).decode()
    newdir = TARGET_DIR + randname

    rsync_command = (
        "rsync",
        "-aLz",
        "--chmod=ug=rwx,o=rx",
        f"{output_path}/",
        f"{UPLOAD_HOST}:{newdir}",
    )

    log.info(f"Uploading output ({output_path}) to {UPLOAD_HOST}")
    try:
        subprocess.run(rsync_command, check=True)
    except subprocess.CalledProcessError as e:
        msg = f"Failed to upload output: {e}"
        raise RuntimeError(msg) from e

    url = urljoin(ROOT_URL, randname)
    log.info(f"Upload completed. The result is available at:\n{url}")

src/qibocal/cli/__init__.py METASEP
"""CLI entry point."""
from ._base import command, live_plot, upload

src/qibocal/calibrations/characterization/utils.py METASEP
import numpy as np


def variable_resolution_scanrange(
    lowres_width, lowres_step, highres_width, highres_step
):
    """Helper function for sweeps."""
    return np.concatenate(
        (
            np.arange(-lowres_width, -highres_width, lowres_step),
            np.arange(-highres_width, highres_width, highres_step),
            np.arange(highres_width, lowres_width, lowres_step),
        )
    )

src/qibocal/calibrations/characterization/t1.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import t1_fit


@plot("MSR vs Time", plots.t1_time_msr_phase)
def t1(
    platform: AbstractPlatform,
    qubit: int,
    delay_before_readout_start,
    delay_before_readout_end,
    delay_before_readout_step,
    software_averages,
    points=10,
):
    sequence = PulseSequence()
    qd_pulse = platform.create_RX_pulse(qubit, start=0)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=qd_pulse.duration)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    ro_wait_range = np.arange(
        delay_before_readout_start, delay_before_readout_end, delay_before_readout_step
    )

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse.frequency
    )

    data = DataUnits(name=f"data_q{qubit}", quantities={"Time": "ns"})

    count = 0
    for _ in range(software_averages):
        for wait in ro_wait_range:
            if count % points == 0 and count > 0:
                yield data
                yield t1_fit(
                    data,
                    x="Time[ns]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["t1"],
                )
            ro_pulse.start = qd_pulse.duration + wait
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "Time[ns]": wait,
            }
            data.add(results)
            count += 1
    yield data

src/qibocal/calibrations/characterization/resonator_spectroscopy.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.calibrations.characterization.utils import variable_resolution_scanrange
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import lorentzian_fit


@plot("MSR and Phase vs Frequency", plots.frequency_msr_phase__fast_precision)
def resonator_spectroscopy(
    platform: AbstractPlatform,
    qubit: int,
    lowres_width,
    lowres_step,
    highres_width,
    highres_step,
    precision_width,
    precision_step,
    software_averages,
    points=10,
):

    platform.reload_settings()
    sequence = PulseSequence()
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0)
    sequence.add(ro_pulse)

    resonator_frequency = platform.characterization["single_qubit"][qubit][
        "resonator_freq"
    ]

    frequency_range = (
        variable_resolution_scanrange(
            lowres_width, lowres_step, highres_width, highres_step
        )
        + resonator_frequency
    )
    fast_sweep_data = DataUnits(
        name=f"fast_sweep_q{qubit}", quantities={"frequency": "Hz"}
    )
    count = 0
    for _ in range(software_averages):
        for freq in frequency_range:
            if count % points == 0 and count > 0:
                yield fast_sweep_data
                yield lorentzian_fit(
                    fast_sweep_data,
                    x="frequency[GHz]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["resonator_freq", "peak_voltage"],
                )

            platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "frequency[Hz]": freq,
            }
            fast_sweep_data.add(results)
            count += 1
    yield fast_sweep_data

    if platform.resonator_type == "3D":
        resonator_frequency = fast_sweep_data.get_values("frequency", "Hz")[
            np.argmax(fast_sweep_data.get_values("MSR", "V"))
        ]
        avg_voltage = (
            np.mean(
                fast_sweep_data.get_values("MSR", "V")[: (lowres_width // lowres_step)]
            )
            * 1e6
        )
    else:
        resonator_frequency = fast_sweep_data.get_values("frequency", "Hz")[
            np.argmin(fast_sweep_data.get_values("MSR", "V"))
        ]
        avg_voltage = (
            np.mean(
                fast_sweep_data.get_values("MSR", "V")[: (lowres_width // lowres_step)]
            )
            * 1e6
        )

    precision_sweep__data = DataUnits(
        name=f"precision_sweep_q{qubit}", quantities={"frequency": "Hz"}
    )
    freqrange = (
        np.arange(-precision_width, precision_width, precision_step)
        + resonator_frequency
    )

    count = 0
    for _ in range(software_averages):
        for freq in freqrange:
            if count % points == 0 and count > 0:
                yield precision_sweep__data
                yield lorentzian_fit(
                    fast_sweep_data + precision_sweep__data,
                    x="frequency[GHz]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["resonator_freq", "peak_voltage"],
                )

            platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "frequency[Hz]": freq,
            }
            precision_sweep__data.add(results)
            count += 1
    yield precision_sweep__data


@plot("Frequency vs Attenuation", plots.frequency_attenuation_msr_phase)
@plot("MSR vs Frequency", plots.frequency_attenuation_msr_phase__cut)
def resonator_punchout(
    platform: AbstractPlatform,
    qubit: int,
    freq_width,
    freq_step,
    min_att,
    max_att,
    step_att,
    software_averages,
    points=10,
):
    platform.reload_settings()

    data = DataUnits(
        name=f"data_q{qubit}", quantities={"frequency": "Hz", "attenuation": "dB"}
    )
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0)
    sequence = PulseSequence()
    sequence.add(ro_pulse)

    # TODO: move this explicit instruction to the platform
    resonator_frequency = platform.characterization["single_qubit"][qubit][
        "resonator_freq"
    ]
    frequency_range = (
        np.arange(-freq_width, freq_width, freq_step)
        + resonator_frequency
        - (freq_width / 4)
    )
    attenuation_range = np.flip(np.arange(min_att, max_att, step_att))
    count = 0
    for _ in range(software_averages):
        for att in attenuation_range:
            for freq in frequency_range:
                if count % points == 0:
                    yield data
                # TODO: move these explicit instructions to the platform
                platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
                platform.ro_port[qubit].attenuation = att
                msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                    ro_pulse.serial
                ]
                results = {
                    "MSR[V]": msr * (np.exp(att / 10)),
                    "i[V]": i,
                    "q[V]": q,
                    "phase[rad]": phase,
                    "frequency[Hz]": freq,
                    "attenuation[dB]": att,
                }
                # TODO: implement normalization
                data.add(results)
                count += 1

    yield data


@plot("MSR and Phase vs Flux Current", plots.frequency_flux_msr_phase)
def resonator_spectroscopy_flux(
    platform: AbstractPlatform,
    qubit: int,
    freq_width,
    freq_step,
    current_max,
    current_min,
    current_step,
    software_averages,
    fluxline=0,
    points=10,
):
    platform.reload_settings()

    if fluxline == "qubit":
        fluxline = qubit

    sequence = PulseSequence()
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0)
    sequence.add(ro_pulse)

    data = DataUnits(
        name=f"data_q{qubit}", quantities={"frequency": "Hz", "current": "A"}
    )

    resonator_frequency = platform.characterization["single_qubit"][qubit][
        "resonator_freq"
    ]
    qubit_biasing_current = platform.characterization["single_qubit"][qubit][
        "sweetspot"
    ]
    frequency_range = (
        np.arange(-freq_width, freq_width, freq_step) + resonator_frequency
    )
    current_range = (
        np.arange(current_min, current_max, current_step) + qubit_biasing_current
    )

    count = 0
    for _ in range(software_averages):
        for curr in current_range:
            for freq in frequency_range:
                if count % points == 0:
                    yield data
                platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
                platform.qf_port[fluxline].current = curr
                msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                    ro_pulse.serial
                ]
                results = {
                    "MSR[V]": msr,
                    "i[V]": i,
                    "q[V]": q,
                    "phase[rad]": phase,
                    "frequency[Hz]": freq,
                    "current[A]": curr,
                }
                # TODO: implement normalization
                data.add(results)
                count += 1

    yield data
    # TODO: automatically extract the sweet spot current
    # TODO: add a method to generate the matrix


@plot("MSR row 1 and Phase row 2", plots.frequency_flux_msr_phase__matrix)
def resonator_spectroscopy_flux_matrix(
    platform: AbstractPlatform,
    qubit: int,
    freq_width,
    freq_step,
    current_min,
    current_max,
    current_step,
    fluxlines,
    software_averages,
    points=10,
):
    platform.reload_settings()

    sequence = PulseSequence()
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0)
    sequence.add(ro_pulse)

    resonator_frequency = platform.characterization["single_qubit"][qubit][
        "resonator_freq"
    ]

    frequency_range = (
        np.arange(-freq_width, freq_width, freq_step) + resonator_frequency
    )
    current_range = np.arange(current_min, current_max, current_step)

    count = 0
    for fluxline in fluxlines:
        fluxline = int(fluxline)
        print(fluxline)
        data = DataUnits(
            name=f"data_q{qubit}_f{fluxline}",
            quantities={"frequency": "Hz", "current": "A"},
        )
        for _ in range(software_averages):
            for curr in current_range:
                for freq in frequency_range:
                    if count % points == 0:
                        yield data
                    platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
                    platform.qf_port[fluxline].current = curr
                    msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                        ro_pulse.serial
                    ]
                    results = {
                        "MSR[V]": msr,
                        "i[V]": i,
                        "q[V]": q,
                        "phase[rad]": phase,
                        "frequency[Hz]": freq,
                        "current[A]": curr,
                    }
                    # TODO: implement normalization
                    data.add(results)
                    count += 1

    yield data


@plot("MSR and Phase vs Frequency", plots.dispersive_frequency_msr_phase)
def dispersive_shift(
    platform: AbstractPlatform,
    qubit: int,
    freq_width,
    freq_step,
    software_averages,
    points=10,
):

    platform.reload_settings()

    sequence = PulseSequence()
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0)
    sequence.add(ro_pulse)

    resonator_frequency = platform.characterization["single_qubit"][qubit][
        "resonator_freq"
    ]

    frequency_range = (
        np.arange(-freq_width, freq_width, freq_step) + resonator_frequency
    )

    data_spec = DataUnits(name=f"data_q{qubit}", quantities={"frequency": "Hz"})
    count = 0
    for _ in range(software_averages):
        for freq in frequency_range:
            if count % points == 0 and count > 0:
                yield data_spec
                yield lorentzian_fit(
                    data_spec,
                    x="frequency[GHz]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["resonator_freq", "peak_voltage"],
                )
            platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "frequency[Hz]": freq,
            }
            data_spec.add(results)
            count += 1
    yield data_spec

    # Shifted Spectroscopy
    sequence = PulseSequence()
    RX_pulse = platform.create_RX_pulse(qubit, start=0)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=RX_pulse.finish)
    sequence.add(RX_pulse)
    sequence.add(ro_pulse)

    data_shifted = DataUnits(
        name=f"data_shifted_q{qubit}", quantities={"frequency": "Hz"}
    )
    count = 0
    for _ in range(software_averages):
        for freq in frequency_range:
            if count % points == 0 and count > 0:
                yield data_shifted
                yield lorentzian_fit(
                    data_shifted,
                    x="frequency[GHz]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["resonator_freq", "peak_voltage"],
                    fit_file_name="fit_shifted",
                )
            platform.ro_port[qubit].lo_frequency = freq - ro_pulse.frequency
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "frequency[Hz]": freq,
            }
            data_shifted.add(results)
            count += 1
    yield data_shifted

src/qibocal/calibrations/characterization/ramsey.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import ramsey_fit


@plot("MSR vs Time", plots.time_msr)
def ramsey_frequency_detuned(
    platform: AbstractPlatform,
    qubit: int,
    t_start,
    t_end,
    t_step,
    n_osc,
    points=10,
):
    platform.reload_settings()
    sampling_rate = platform.sampling_rate

    data = DataUnits(name=f"data_q{qubit}", quantities={"wait": "ns", "t_max": "ns"})

    RX90_pulse1 = platform.create_RX90_pulse(qubit, start=0)
    RX90_pulse2 = platform.create_RX90_pulse(qubit, start=RX90_pulse1.finish)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=RX90_pulse2.finish)

    sequence = PulseSequence()
    sequence.add(RX90_pulse1)
    sequence.add(RX90_pulse2)
    sequence.add(ro_pulse)

    runcard_qubit_freq = platform.characterization["single_qubit"][qubit]["qubit_freq"]
    runcard_T2 = platform.characterization["single_qubit"][qubit]["T2"]
    intermediate_freq = platform.settings["native_gates"]["single_qubit"][qubit]["RX"][
        "frequency"
    ]

    current_qubit_freq = runcard_qubit_freq
    current_T2 = runcard_T2

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - RX90_pulse1.frequency
    )

    t_end = np.array(t_end)
    for t_max in t_end:
        count = 0
        platform.qd_port[qubit].lo_frequency = current_qubit_freq - intermediate_freq
        offset_freq = n_osc / t_max * sampling_rate  # Hz
        t_range = np.arange(t_start, t_max, t_step)
        for wait in t_range:
            if count % points == 0 and count > 0:
                yield data
                yield ramsey_fit(
                    data,
                    x="wait[ns]",
                    y="MSR[uV]",
                    qubit=qubit,
                    qubit_freq=current_qubit_freq,
                    sampling_rate=sampling_rate,
                    offset_freq=offset_freq,
                    labels=[
                        "delta_frequency",
                        "corrected_qubit_frequency",
                        "t2",
                    ],
                )
            RX90_pulse2.start = RX90_pulse1.finish + wait
            RX90_pulse2.relative_phase = (
                (RX90_pulse2.start / sampling_rate) * (2 * np.pi) * (-offset_freq)
            )
            ro_pulse.start = RX90_pulse2.finish

            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "wait[ns]": wait,
                "t_max[ns]": t_max,
            }
            data.add(results)
            count += 1

        # # Fitting
        data_fit = ramsey_fit(
            data,
            x="wait[ns]",
            y="MSR[uV]",
            qubit=qubit,
            qubit_freq=current_qubit_freq,
            sampling_rate=sampling_rate,
            offset_freq=offset_freq,
            labels=[
                "delta_frequency",
                "corrected_qubit_frequency",
                "t2",
            ],
        )

        new_t2 = data_fit.get_values("t2")
        corrected_qubit_freq = data_fit.get_values("corrected_qubit_frequency")

        # if ((new_t2 * 3.5) > t_max):
        if (new_t2 > current_T2).bool() and len(t_end) > 1:
            current_qubit_freq = int(corrected_qubit_freq)
            current_T2 = new_t2
            data = DataUnits(
                name=f"data_q{qubit}", quantities={"wait": "ns", "t_max": "ns"}
            )
        else:
            corrected_qubit_freq = int(current_qubit_freq)
            new_t2 = current_T2
            break

    yield data


@plot("MSR vs Time", plots.time_msr)
def ramsey(
    platform: AbstractPlatform,
    qubit: int,
    delay_between_pulses_start,
    delay_between_pulses_end,
    delay_between_pulses_step,
    software_averages,
    points=10,
):
    platform.reload_settings()
    sampling_rate = platform.sampling_rate
    qubit_freq = platform.characterization["single_qubit"][qubit]["qubit_freq"]

    RX90_pulse1 = platform.create_RX90_pulse(qubit, start=0)
    RX90_pulse2 = platform.create_RX90_pulse(qubit, start=RX90_pulse1.finish)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=RX90_pulse2.finish)

    sequence = PulseSequence()
    sequence.add(RX90_pulse1)
    sequence.add(RX90_pulse2)
    sequence.add(ro_pulse)

    waits = np.arange(
        delay_between_pulses_start,
        delay_between_pulses_end,
        delay_between_pulses_step,
    )

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - RX90_pulse1.frequency
    )

    data = DataUnits(name=f"data_q{qubit}", quantities={"wait": "ns", "t_max": "ns"})
    count = 0
    for _ in range(software_averages):
        for wait in waits:
            if count % points == 0 and count > 0:
                yield data
                yield ramsey_fit(
                    data,
                    x="wait[ns]",
                    y="MSR[uV]",
                    qubit=qubit,
                    qubit_freq=qubit_freq,
                    sampling_rate=sampling_rate,
                    offset_freq=0,
                    labels=[
                        "delta_frequency",
                        "corrected_qubit_frequency",
                        "t2",
                    ],
                )
            RX90_pulse2.start = RX90_pulse1.finish + wait
            ro_pulse.start = RX90_pulse2.finish

            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[deg]": phase,
                "wait[ns]": wait,
                "t_max[ns]": delay_between_pulses_end,
            }
            data.add(results)
            count += 1
    yield data

src/qibocal/calibrations/characterization/rabi_oscillations.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import rabi_fit


@plot("MSR vs Time", plots.time_msr_phase)
def rabi_pulse_length(
    platform: AbstractPlatform,
    qubit: int,
    pulse_duration_start,
    pulse_duration_end,
    pulse_duration_step,
    software_averages,
    points=10,
):
    platform.reload_settings()

    data = DataUnits(name=f"data_q{qubit}", quantities={"Time": "ns"})

    sequence = PulseSequence()
    qd_pulse = platform.create_qubit_drive_pulse(qubit, start=0, duration=4)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=4)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    qd_pulse_duration_range = np.arange(
        pulse_duration_start, pulse_duration_end, pulse_duration_step
    )

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse.frequency
    )

    count = 0
    for _ in range(software_averages):
        for duration in qd_pulse_duration_range:
            qd_pulse.duration = duration
            ro_pulse.start = duration
            if count % points == 0 and count > 0:
                yield data
                yield rabi_fit(
                    data,
                    x="Time[ns]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=[
                        "pi_pulse_duration",
                        "pi_pulse_max_voltage",
                    ],
                )
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "Time[ns]": duration,
            }
            data.add(results)
            count += 1
    yield data


@plot("MSR vs Gain", plots.gain_msr_phase)
def rabi_pulse_gain(
    platform: AbstractPlatform,
    qubit: int,
    pulse_gain_start,
    pulse_gain_end,
    pulse_gain_step,
    software_averages,
    points=10,
):
    platform.reload_settings()

    data = DataUnits(name=f"data_q{qubit}", quantities={"gain": "dimensionless"})

    sequence = PulseSequence()
    qd_pulse = platform.create_RX_pulse(qubit, start=0)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=qd_pulse.finish)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    qd_pulse_gain_range = np.arange(pulse_gain_start, pulse_gain_end, pulse_gain_step)

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse.frequency
    )

    count = 0
    for _ in range(software_averages):
        for gain in qd_pulse_gain_range:
            platform.qd_port[qubit].gain = gain
            if count % points == 0 and count > 0:
                yield data
                yield rabi_fit(
                    data,
                    x="gain[dimensionless]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=[
                        "pi_pulse_gain",
                        "pi_pulse_max_voltage",
                    ],
                )
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "gain[dimensionless]": gain,
            }
            data.add(results)
            count += 1
    yield data


@plot("MSR vs Amplitude", plots.amplitude_msr_phase)
def rabi_pulse_amplitude(
    platform,
    qubit: int,
    pulse_amplitude_start,
    pulse_amplitude_end,
    pulse_amplitude_step,
    software_averages,
    points=10,
):
    platform.reload_settings()

    data = DataUnits(name=f"data_q{qubit}", quantities={"amplitude": "dimensionless"})

    sequence = PulseSequence()
    qd_pulse = platform.create_RX_pulse(qubit, start=0)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=qd_pulse.duration)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    qd_pulse_amplitude_range = np.arange(
        pulse_amplitude_start, pulse_amplitude_end, pulse_amplitude_step
    )

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse.frequency
    )

    count = 0
    for _ in range(software_averages):
        for amplitude in qd_pulse_amplitude_range:
            qd_pulse.amplitude = amplitude
            if count % points == 0 and count > 0:
                yield data
                yield rabi_fit(
                    data,
                    x="amplitude[dimensionless]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=[
                        "pi_pulse_amplitude",
                        "pi_pulse_max_voltage",
                    ],
                )
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "amplitude[dimensionless]": amplitude,
            }
            data.add(results)
            count += 1
    yield data


@plot("MSR vs length and gain", plots.duration_gain_msr_phase)
def rabi_pulse_length_and_gain(
    platform: AbstractPlatform,
    qubit: int,
    pulse_duration_start,
    pulse_duration_end,
    pulse_duration_step,
    pulse_gain_start,
    pulse_gain_end,
    pulse_gain_step,
    software_averages,
    points=10,
):
    platform.reload_settings()

    data = DataUnits(
        name=f"data_q{qubit}", quantities={"duration": "ns", "gain": "dimensionless"}
    )

    sequence = PulseSequence()
    qd_pulse = platform.create_qubit_drive_pulse(qubit, start=0, duration=4)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=4)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    qd_pulse_duration_range = np.arange(
        pulse_duration_start, pulse_duration_end, pulse_duration_step
    )
    qd_pulse_gain_range = np.arange(pulse_gain_start, pulse_gain_end, pulse_gain_step)

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse.frequency
    )

    count = 0
    for _ in range(software_averages):
        for duration in qd_pulse_duration_range:
            qd_pulse.duration = duration
            ro_pulse.start = duration
            for gain in qd_pulse_gain_range:
                platform.qd_port[qubit].gain = gain
                if count % points == 0 and count > 0:
                    yield data
                msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                    ro_pulse.serial
                ]
                results = {
                    "MSR[V]": msr,
                    "i[V]": i,
                    "q[V]": q,
                    "phase[rad]": phase,
                    "duration[ns]": duration,
                    "gain[dimensionless]": gain,
                }
                data.add(results)
                count += 1

    yield data


@plot("MSR vs length and amplitude", plots.duration_amplitude_msr_phase)
def rabi_pulse_length_and_amplitude(
    platform,
    qubit: int,
    pulse_duration_start,
    pulse_duration_end,
    pulse_duration_step,
    pulse_amplitude_start,
    pulse_amplitude_end,
    pulse_amplitude_step,
    software_averages,
    points=10,
):
    platform.reload_settings()

    data = DataUnits(
        name=f"data_q{qubit}",
        quantities={"duration": "ns", "amplitude": "dimensionless"},
    )

    sequence = PulseSequence()
    qd_pulse = platform.create_qubit_drive_pulse(qubit, start=0, duration=4)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=4)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    qd_pulse_duration_range = np.arange(
        pulse_duration_start, pulse_duration_end, pulse_duration_step
    )
    qd_pulse_amplitude_range = np.arange(
        pulse_amplitude_start, pulse_amplitude_end, pulse_amplitude_step
    )

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse.frequency
    )

    count = 0
    for _ in range(software_averages):
        for duration in qd_pulse_duration_range:
            qd_pulse.duration = duration
            ro_pulse.start = duration
            for amplitude in qd_pulse_amplitude_range:
                qd_pulse.amplitude = amplitude
                if count % points == 0:
                    yield data
                msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                    ro_pulse.serial
                ]
                results = {
                    "MSR[V]": msr,
                    "i[V]": i,
                    "q[V]": q,
                    "phase[rad]": phase,
                    "duration[ns]": duration,
                    "amplitude[dimensionless]": amplitude,
                }
                data.add(results)
                count += 1

    yield data

src/qibocal/calibrations/characterization/qubit_spectroscopy.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import lorentzian_fit


@plot("MSR and Phase vs Frequency", plots.frequency_msr_phase__fast_precision)
def qubit_spectroscopy(
    platform: AbstractPlatform,
    qubit: int,
    fast_start,
    fast_end,
    fast_step,
    precision_start,
    precision_end,
    precision_step,
    software_averages,
    points=10,
):

    platform.reload_settings()

    sequence = PulseSequence()
    qd_pulse = platform.create_qubit_drive_pulse(qubit, start=0, duration=5000)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=5000)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    qubit_frequency = platform.characterization["single_qubit"][qubit]["qubit_freq"]

    freqrange = np.arange(fast_start, fast_end, fast_step) + qubit_frequency

    data = DataUnits(quantities={"frequency": "Hz", "attenuation": "dB"})

    # FIXME: Waiting for Qblox platform to take care of that
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )

    data = DataUnits(name=f"fast_sweep_q{qubit}", quantities={"frequency": "Hz"})
    count = 0
    for _ in range(software_averages):
        for freq in freqrange:
            if count % points == 0 and count > 0:
                yield data
                yield lorentzian_fit(
                    data,
                    x="frequency[GHz]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["qubit_freq", "peak_voltage"],
                )

            platform.qd_port[qubit].lo_frequency = freq - qd_pulse.frequency
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "frequency[Hz]": freq,
            }
            data.add(results)
            count += 1
    yield data

    if platform.resonator_type == "3D":
        qubit_frequency = data.get_values("frequency", "Hz")[
            np.argmin(data.get_values("MSR", "V"))
        ]
        avg_voltage = (
            np.mean(
                data.get_values("MSR", "V")[: ((fast_end - fast_start) // fast_step)]
            )
            * 1e6
        )
    else:
        qubit_frequency = data.get_values("frequency", "Hz")[
            np.argmax(data.get_values("MSR", "V"))
        ]
        avg_voltage = (
            np.mean(
                data.get_values("MSR", "V")[: ((fast_end - fast_start) // fast_step)]
            )
            * 1e6
        )

    prec_data = DataUnits(
        name=f"precision_sweep_q{qubit}", quantities={"frequency": "Hz"}
    )
    freqrange = (
        np.arange(precision_start, precision_end, precision_step) + qubit_frequency
    )
    count = 0
    for _ in range(software_averages):
        for freq in freqrange:
            if count % points == 0 and count > 0:
                yield prec_data
                yield lorentzian_fit(
                    data + prec_data,
                    x="frequency[GHz]",
                    y="MSR[uV]",
                    qubit=qubit,
                    nqubits=platform.settings["nqubits"],
                    labels=["qubit_freq", "peak_voltage"],
                )
            platform.qd_port[qubit].lo_frequency = freq - qd_pulse.frequency
            msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                ro_pulse.serial
            ]
            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "frequency[Hz]": freq,
            }
            prec_data.add(results)
            count += 1
    yield prec_data
    # TODO: Estimate avg_voltage correctly


@plot("MSR and Phase vs Frequency", plots.frequency_flux_msr_phase)
def qubit_spectroscopy_flux(
    platform: AbstractPlatform,
    qubit: int,
    freq_width,
    freq_step,
    current_max,
    current_min,
    current_step,
    software_averages,
    fluxline,
    points=10,
):
    platform.reload_settings()

    if fluxline == "qubit":
        fluxline = qubit

    sequence = PulseSequence()
    qd_pulse = platform.create_qubit_drive_pulse(qubit, start=0, duration=5000)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=5000)
    sequence.add(qd_pulse)
    sequence.add(ro_pulse)

    data = DataUnits(
        name=f"data_q{qubit}", quantities={"frequency": "Hz", "current": "A"}
    )

    qubit_frequency = platform.characterization["single_qubit"][qubit]["qubit_freq"]
    qubit_biasing_current = platform.characterization["single_qubit"][qubit][
        "sweetspot"
    ]
    frequency_range = np.arange(-freq_width, freq_width, freq_step) + qubit_frequency
    current_range = (
        np.arange(current_min, current_max, current_step) + qubit_biasing_current
    )

    count = 0
    for _ in range(software_averages):
        for curr in current_range:
            for freq in frequency_range:
                if count % points == 0:
                    yield data
                platform.qd_port[qubit].lo_frequency = freq - qd_pulse.frequency
                platform.qf_port[fluxline].current = curr
                msr, phase, i, q = platform.execute_pulse_sequence(sequence)[
                    ro_pulse.serial
                ]
                results = {
                    "MSR[V]": msr,
                    "i[V]": i,
                    "q[V]": q,
                    "phase[rad]": phase,
                    "frequency[Hz]": freq,
                    "current[A]": curr,
                }
                # TODO: implement normalization
                data.add(results)
                count += 1

    yield data

src/qibocal/calibrations/characterization/flipping.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import flipping_fit


@plot("MSR vs Flips", plots.flips_msr_phase)
def flipping(
    platform: AbstractPlatform,
    qubit: int,
    niter,
    step,
    points=10,
):
    platform.reload_settings()
    pi_pulse_amplitude = platform.settings["native_gates"]["single_qubit"][qubit]["RX"][
        "amplitude"
    ]

    data = DataUnits(name=f"data_q{qubit}", quantities={"flips": "dimensionless"})

    sequence = PulseSequence()
    RX90_pulse = platform.create_RX90_pulse(qubit, start=0)

    count = 0
    # repeat N iter times
    for n in range(0, niter, step):
        if count % points == 0 and count > 0:
            yield data
            yield flipping_fit(
                data,
                x="flips[dimensionless]",
                y="MSR[uV]",
                qubit=qubit,
                nqubits=platform.settings["nqubits"],
                niter=niter,
                pi_pulse_amplitude=pi_pulse_amplitude,
                labels=["amplitude_delta", "corrected_amplitude"],
            )
        sequence.add(RX90_pulse)
        # execute sequence RX(pi/2) - [RX(pi) - RX(pi)] from 0...n times - RO
        start1 = RX90_pulse.duration
        for j in range(n):
            RX_pulse1 = platform.create_RX_pulse(qubit, start=start1)
            start2 = start1 + RX_pulse1.duration
            RX_pulse2 = platform.create_RX_pulse(qubit, start=start2)
            sequence.add(RX_pulse1)
            sequence.add(RX_pulse2)
            start1 = start2 + RX_pulse2.duration

        # add ro pulse at the end of the sequence
        ro_pulse = platform.create_qubit_readout_pulse(qubit, start=start1)
        sequence.add(ro_pulse)

        msr, phase, i, q = platform.execute_pulse_sequence(sequence)[ro_pulse.serial]
        results = {
            "MSR[V]": msr,
            "i[V]": i,
            "q[V]": q,
            "phase[rad]": phase,
            "flips[dimensionless]": n,
        }
        data.add(results)
        count += 1
        sequence = PulseSequence()

    yield data

src/qibocal/calibrations/characterization/calibrate_qubit_states.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot


@plot("exc vs gnd", plots.exc_gnd)
def calibrate_qubit_states(
    platform: AbstractPlatform,
    qubit: int,
    niter,
    points=10,
):

    # create exc sequence
    exc_sequence = PulseSequence()
    RX_pulse = platform.create_RX_pulse(qubit, start=0)
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=RX_pulse.duration)
    exc_sequence.add(RX_pulse)
    exc_sequence.add(ro_pulse)

    # FIXME: Waiting to be able to pass qpucard to qibolab
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse.frequency
    )
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - RX_pulse.frequency
    )

    data_exc = DataUnits(name=f"data_exc_q{qubit}", quantities={"iteration": "s"})

    count = 0
    for n in np.arange(niter):
        if count % points == 0:
            yield data_exc
        msr, phase, i, q = platform.execute_pulse_sequence(exc_sequence, nshots=1)[
            ro_pulse.serial
        ]
        results = {
            "MSR[V]": msr,
            "i[V]": i,
            "q[V]": q,
            "phase[deg]": phase,
            "iteration[s]": n,
        }
        data_exc.add(results)
        count += 1
    yield data_exc

    gnd_sequence = PulseSequence()
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0)
    gnd_sequence.add(ro_pulse)

    data_gnd = DataUnits(name=f"data_gnd_q{qubit}", quantities={"iteration": "s"})
    count = 0
    for n in np.arange(niter):
        if count % points == 0:
            yield data_gnd

        msr, phase, i, q = platform.execute_pulse_sequence(gnd_sequence, nshots=1)[
            ro_pulse.serial
        ]
        results = {
            "MSR[V]": msr,
            "i[V]": i,
            "q[V]": q,
            "phase[deg]": phase,
            "iteration[s]": n,
        }
        data_gnd.add(results)
        count += 1
    yield data_gnd

src/qibocal/calibrations/characterization/allXY.py METASEP
import numpy as np
from qibolab.platforms.abstract import AbstractPlatform
from qibolab.pulses import PulseSequence

from qibocal import plots
from qibocal.data import DataUnits
from qibocal.decorators import plot
from qibocal.fitting.methods import drag_tunning_fit

# allXY rotations
gatelist = [
    ["I", "I"],
    ["RX(pi)", "RX(pi)"],
    ["RY(pi)", "RY(pi)"],
    ["RX(pi)", "RY(pi)"],
    ["RY(pi)", "RX(pi)"],
    ["RX(pi/2)", "I"],
    ["RY(pi/2)", "I"],
    ["RX(pi/2)", "RY(pi/2)"],
    ["RY(pi/2)", "RX(pi/2)"],
    ["RX(pi/2)", "RY(pi)"],
    ["RY(pi/2)", "RX(pi)"],
    ["RX(pi)", "RY(pi/2)"],
    ["RY(pi)", "RX(pi/2)"],
    ["RX(pi/2)", "RX(pi)"],
    ["RX(pi)", "RX(pi/2)"],
    ["RY(pi/2)", "RY(pi)"],
    ["RY(pi)", "RY(pi/2)"],
    ["RX(pi)", "I"],
    ["RY(pi)", "I"],
    ["RX(pi/2)", "RX(pi/2)"],
    ["RY(pi/2)", "RY(pi/2)"],
]


@plot("Prob vs gate sequence", plots.prob_gate)
def allXY(
    platform: AbstractPlatform,
    qubit: int,
    beta_param=None,
    software_averages=1,
    points=10,
):

    state0_voltage = complex(
        platform.characterization["single_qubit"][qubit]["state0_voltage"]
    )
    state1_voltage = complex(
        platform.characterization["single_qubit"][qubit]["state1_voltage"]
    )

    data = DataUnits(
        name=f"data_q{qubit}",
        quantities={"probability": "dimensionless", "gateNumber": "dimensionless"},
    )

    # FIXME: Waiting to be able to pass qpucard to qibolab
    ro_pulse_test = platform.create_qubit_readout_pulse(qubit, start=4)
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse_test.frequency
    )

    qd_pulse_test = platform.create_qubit_drive_pulse(qubit, start=0, duration=4)
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse_test.frequency
    )

    count = 0
    for _ in range(software_averages):
        gateNumber = 1
        for gates in gatelist:
            if count % points == 0 and count > 0:
                yield data
            seq, ro_pulse = _get_sequence_from_gate_pair(
                platform, gates, qubit, beta_param
            )
            seq.add(ro_pulse)
            msr, phase, i, q = platform.execute_pulse_sequence(seq, nshots=2048)[
                ro_pulse.serial
            ]

            prob = np.abs(msr * 1e6 - state1_voltage) / np.abs(
                state1_voltage - state0_voltage
            )
            prob = (2 * prob) - 1

            results = {
                "MSR[V]": msr,
                "i[V]": i,
                "q[V]": q,
                "phase[rad]": phase,
                "probability[dimensionless]": prob,
                "gateNumber[dimensionless]": gateNumber,
            }
            data.add(results)
            count += 1
            gateNumber += 1
    yield data


@plot("Prob vs gate sequence", plots.prob_gate_iteration)
def allXY_iteration(
    platform: AbstractPlatform,
    qubit: int,
    beta_start,
    beta_end,
    beta_step,
    software_averages=1,
    points=10,
):

    # FIXME: Waiting to be able to pass qpucard to qibolab
    ro_pulse_test = platform.create_qubit_readout_pulse(qubit, start=4)
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse_test.frequency
    )

    qd_pulse_test = platform.create_qubit_drive_pulse(qubit, start=0, duration=4)
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse_test.frequency
    )

    state0_voltage = complex(
        platform.characterization["single_qubit"][qubit]["state0_voltage"]
    )
    state1_voltage = complex(
        platform.characterization["single_qubit"][qubit]["state1_voltage"]
    )

    data = DataUnits(
        name=f"data_q{qubit}",
        quantities={
            "probability": "dimensionless",
            "gateNumber": "dimensionless",
            "beta_param": "dimensionless",
        },
    )

    count = 0
    for _ in range(software_averages):
        for beta_param in np.arange(beta_start, beta_end, beta_step).round(4):
            gateNumber = 1
            for gates in gatelist:
                if count % points == 0 and count > 0:
                    yield data
                seq, ro_pulse = _get_sequence_from_gate_pair(
                    platform, gates, qubit, beta_param
                )
                seq.add(ro_pulse)
                msr, phase, i, q = platform.execute_pulse_sequence(seq, nshots=1024)[
                    ro_pulse.serial
                ]

                prob = np.abs(msr * 1e6 - state1_voltage) / np.abs(
                    state1_voltage - state0_voltage
                )
                prob = (2 * prob) - 1

                results = {
                    "MSR[V]": msr,
                    "i[V]": i,
                    "q[V]": q,
                    "phase[rad]": phase,
                    "probability[dimensionless]": prob,
                    "gateNumber[dimensionless]": gateNumber,
                    "beta_param[dimensionless]": beta_param,
                }
                data.add(results)
                count += 1
                gateNumber += 1
    yield data


@plot("MSR vs beta parameter", plots.msr_beta)
def drag_pulse_tunning(
    platform: AbstractPlatform,
    qubit: int,
    beta_start,
    beta_end,
    beta_step,
    points=10,
):

    # platform.reload_settings()

    # FIXME: Waiting to be able to pass qpucard to qibolab
    ro_pulse_test = platform.create_qubit_readout_pulse(qubit, start=4)
    platform.ro_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["resonator_freq"]
        - ro_pulse_test.frequency
    )

    qd_pulse_test = platform.create_qubit_drive_pulse(qubit, start=0, duration=4)
    platform.qd_port[qubit].lo_frequency = (
        platform.characterization["single_qubit"][qubit]["qubit_freq"]
        - qd_pulse_test.frequency
    )

    data = DataUnits(name=f"data_q{qubit}", quantities={"beta_param": "dimensionless"})

    count = 0
    for beta_param in np.arange(beta_start, beta_end, beta_step).round(4):
        if count % points == 0 and count > 0:
            yield data
            yield drag_tunning_fit(
                data,
                x="beta_param[dimensionless]",
                y="MSR[uV]",
                qubit=qubit,
                nqubits=platform.settings["nqubits"],
                labels=[
                    "optimal_beta_param",
                ],
            )
        # drag pulse RX(pi/2)
        RX90_drag_pulse = platform.create_RX90_drag_pulse(
            qubit, start=0, beta=beta_param
        )
        # drag pulse RY(pi)
        RY_drag_pulse = platform.create_RX_drag_pulse(
            qubit,
            start=RX90_drag_pulse.finish,
            relative_phase=+np.pi / 2,
            beta=beta_param,
        )
        # RO pulse
        ro_pulse = platform.create_qubit_readout_pulse(
            qubit, start=RY_drag_pulse.finish
        )

        # Rx(pi/2) - Ry(pi) - Ro
        seq1 = PulseSequence()
        seq1.add(RX90_drag_pulse)
        seq1.add(RY_drag_pulse)
        seq1.add(ro_pulse)
        msr1, i1, q1, phase1 = platform.execute_pulse_sequence(seq1)[ro_pulse.serial]

        # drag pulse RY(pi/2)
        RY90_drag_pulse = platform.create_RX90_drag_pulse(
            qubit, start=0, relative_phase=np.pi / 2, beta=beta_param
        )
        # drag pulse RX(pi)
        RX_drag_pulse = platform.create_RX_drag_pulse(
            qubit, start=RY90_drag_pulse.finish, beta=beta_param
        )

        # Ry(pi/2) - Rx(pi) - Ro
        seq2 = PulseSequence()
        seq2.add(RY90_drag_pulse)
        seq2.add(RX_drag_pulse)
        seq2.add(ro_pulse)
        msr2, phase2, i2, q2 = platform.execute_pulse_sequence(seq2)[ro_pulse.serial]
        results = {
            "MSR[V]": msr1 - msr2,
            "i[V]": i1 - i2,
            "q[V]": q1 - q2,
            "phase[deg]": phase1 - phase2,
            "beta_param[dimensionless]": beta_param,
        }
        data.add(results)
        count += 1

    yield data


def _get_sequence_from_gate_pair(platform: AbstractPlatform, gates, qubit, beta_param):

    pulse_duration = platform.settings["native_gates"]["single_qubit"][qubit]["RX"][
        "duration"
    ]
    # All gates have equal pulse duration

    sequence = PulseSequence()

    sequenceDuration = 0
    pulse_start = 0

    for gate in gates:
        if gate == "I":
            # print("Transforming to sequence I gate")
            pass

        if gate == "RX(pi)":
            # print("Transforming to sequence RX(pi) gate")
            if beta_param == None:
                RX_pulse = platform.create_RX_pulse(
                    qubit,
                    start=pulse_start,
                )
            else:
                RX_pulse = platform.create_RX_drag_pulse(
                    qubit,
                    start=pulse_start,
                    beta=beta_param,
                )
            sequence.add(RX_pulse)

        if gate == "RX(pi/2)":
            # print("Transforming to sequence RX(pi/2) gate")
            if beta_param == None:
                RX90_pulse = platform.create_RX90_pulse(
                    qubit,
                    start=pulse_start,
                )
            else:
                RX90_pulse = platform.create_RX90_drag_pulse(
                    qubit,
                    start=pulse_start,
                    beta=beta_param,
                )
            sequence.add(RX90_pulse)

        if gate == "RY(pi)":
            # print("Transforming to sequence RY(pi) gate")
            if beta_param == None:
                RY_pulse = platform.create_RX_pulse(
                    qubit,
                    start=pulse_start,
                    relative_phase=np.pi / 2,
                )
            else:
                RY_pulse = platform.create_RX_drag_pulse(
                    qubit,
                    start=pulse_start,
                    relative_phase=np.pi / 2,
                    beta=beta_param,
                )
            sequence.add(RY_pulse)

        if gate == "RY(pi/2)":
            # print("Transforming to sequence RY(pi/2) gate")
            if beta_param == None:
                RY90_pulse = platform.create_RX90_pulse(
                    qubit,
                    start=pulse_start,
                    relative_phase=np.pi / 2,
                )
            else:
                RY90_pulse = platform.create_RX90_drag_pulse(
                    qubit,
                    start=pulse_start,
                    relative_phase=np.pi / 2,
                    beta=beta_param,
                )
            sequence.add(RY90_pulse)

        sequenceDuration = sequenceDuration + pulse_duration
        pulse_start = pulse_duration

    # RO pulse starting just after pair of gates
    ro_pulse = platform.create_qubit_readout_pulse(qubit, start=sequenceDuration + 4)

    return sequence, ro_pulse

src/qibocal/calibrations/characterization/__init__.py METASEP

src/qibocal/decorators.py METASEP
"""Decorators implementation."""
import os

from qibocal.config import raise_error


def plot(header, method):
    """Decorator for adding plots in the report and live plotting page.

    Args:
        header (str): Header of the plot to use in the report.
        method (Callable): Plotting method defined under ``qibocal.plots``.
    """

    def wrapped(f):
        if hasattr(f, "plots"):
            # insert in the beginning of the list to have
            # proper plot ordering in the report
            f.plots.insert(0, (header, method))
        else:
            f.plots = [(header, method)]
        return f

    return wrapped

src/qibocal/data.py METASEP
"""Implementation of DataUnits and Data class to store calibration routines outputs."""

import re
from abc import abstractmethod

import numpy as np
import pandas as pd
import pint_pandas

from qibocal.config import raise_error


class AbstractData:
    def __init__(self, name=None):

        if name is None:
            self.name = "data"
        else:
            self.name = name

        self.df = pd.DataFrame()
        self.quantities = None

    def __add__(self, data):
        self.df = pd.concat([self.df, data.df], ignore_index=True)
        return self

    @abstractmethod
    def add(self, data):
        raise_error(NotImplementedError)

    def __len__(self):
        """Computes the length of the data."""
        return len(self.df)

    @classmethod
    def load_data(cls, folder, routine, format, name):
        raise_error(NotImplementedError)

    @abstractmethod
    def to_csv(self, path):
        """Save data in csv file.

        Args:
            path (str): Path containing output folder."""
        if self.quantities == None:
            self.df.to_csv(f"{path}/{self.name}.csv")
        else:
            self.df.pint.dequantify().to_csv(f"{path}/{self.name}.csv")

    def to_pickle(self, path):
        """Save data in pickel file.

        Args:
            path (str): Path containing output folder."""
        self.df.to_pickle(f"{path}/{self.name}.pkl")


class DataUnits(AbstractData):
    """Class to store the data measured during the calibration routines.
    It is a wrapper to a pandas DataFrame with units of measure from the Pint
    library.

    Args:
        quantities (dict): dictionary containing additional quantities that the user
                        may save other than the pulse sequence output. The keys are the name of the
                        quantities and the corresponding values are the units of measure.
        options (list): list containing additional values to be saved.
    """

    def __init__(self, name=None, quantities=None, options=None):

        super().__init__(name=name)

        self._df = pd.DataFrame(
            {
                "MSR": pd.Series(dtype="pint[V]"),
                "i": pd.Series(dtype="pint[V]"),
                "q": pd.Series(dtype="pint[V]"),
                "phase": pd.Series(dtype="pint[deg]"),
            }
        )
        self.quantities = {"MSR": "V", "i": "V", "q": "V", "phase": "rad"}
        self.options = []

        if quantities is not None:
            self.quantities.update(quantities)
            for name, unit in quantities.items():
                self.df.insert(0, name, pd.Series(dtype=f"pint[{unit}]"))

        if options is not None:
            self.options = options
            for option in options:
                self.df.insert(  # pylint: disable=E1101
                    0, option, pd.Series(dtype=object)
                )

        from pint import UnitRegistry

        self.ureg = UnitRegistry()

    @property
    def df(self):
        return self._df

    @df.setter
    def df(self, df):
        """Set df attribute.

        Args:
            df (pd.DataFrame): pandas DataFrame. Every key should have the following form:
                               ``<name>[<unit>]``.
        """
        if isinstance(df, pd.DataFrame):
            self._df = df
        else:
            raise_error(TypeError, f"{df.type} is not a pd.DataFrame.")

    def load_data_from_dict(self, data: dict):
        """Set df attribute.

        Args:
            data (dict): dictionary containing the data to be added.
                        Every key should have the following form:
                        ``<name>[<unit>]``.
        """
        processed_data = {}
        for key, values in data.items():
            if "[" in key:
                name = key.split("[")[0]
                unit = re.search(r"\[([A-Za-z0-9_]+)\]", key).group(1)
                processed_data[name] = pd.Series(
                    data=(np.array(values) * self.ureg(unit)), dtype=f"pint[{unit}]"
                )
            else:
                processed_data[key] = pd.Series(data=(values), dtype=object)
        self._df = pd.DataFrame(processed_data)

    def add(self, data):
        """Add a row to `DataUnits`.

        Args:
            data (dict): dictionary containing the data to be added.
                        Every key should have the following form:
                        ``<name>[<unit>]``.
        """
        l = len(self)

        for key, value in data.items():
            if "[" in key:
                name = key.split("[")[0]
                unit = re.search(r"\[([A-Za-z0-9_]+)\]", key).group(1)
                # TODO: find a better way to do this
                self.df.loc[l, name] = np.array(value) * self.ureg(unit)
            else:
                self.df.loc[l, key] = value

    def get_values(self, key, unit=None):
        """Get values of a quantity in specified units.

        Args:
            quantity (str): Quantity to get the values of.
            unit (str): Unit of the returned values.

        Returns:
            ``pd.Series`` with the quantity values in the given units.
        """
        if unit is None:
            return self.df[key]
        else:
            return self.df[key].pint.to(unit).pint.magnitude

    @classmethod
    def load_data(cls, folder, routine, format, name):
        """Load data from specific format.

        Args:
            folder (path): path to the output folder from which the data will be loaded
            routine (str): calibration routine data to be loaded
            format (str): data format. Possible choices are 'csv' and 'pickle'.

        Returns:
            data (``DataUnits``): dataset object with the loaded data.
        """
        obj = cls()
        if format == "csv":
            file = f"{folder}/data/{routine}/{name}.csv"
            obj.df = pd.read_csv(file, header=[0, 1])
            obj.df.pop("Unnamed: 0_level_0")
            quantities_label = []
            obj.options = []
            for column in obj.df.columns:  # pylint: disable=E1101
                if "Unnamed" not in column[1]:
                    quantities_label.append(column[0])
                else:
                    obj.options.append(column[0])
            quantities_df = obj.df[quantities_label].pint.quantify()
            options_df = obj.df[obj.options]
            options_df.columns = options_df.columns.droplevel(1)
            obj.df = pd.concat([quantities_df, options_df], axis=1)
        elif format == "pickle":
            file = f"{folder}/data/{routine}/{name}.pkl"
            obj.df = pd.read_pickle(file)
        else:
            raise_error(ValueError, f"Cannot load data using {format} format.")

        return obj

    def to_csv(self, path):
        """Save data in csv file.

        Args:
            path (str): Path containing output folder."""
        data = self.df[list(self.quantities)].pint.dequantify()
        firsts = data.index.get_level_values(None)
        data[self.options] = self.df[self.options].loc[firsts].values
        data.to_csv(f"{path}/{self.name}.csv")


class Data(AbstractData):
    """Class to store the data obtained from calibration routines.
    It is a wrapper to a pandas DataFrame.

    Args:
        quantities (dict): dictionary quantities to be saved.
    """

    def __init__(self, name=None, quantities=None):

        super().__init__(name=name)

        if quantities is not None:
            self.quantities = quantities
            for name in quantities:
                self.df.insert(0, name, pd.Series(dtype=object))

    @property
    def df(self):
        return self._df

    @df.setter
    def df(self, data):
        """Set df attribute.

        Args:
            df (pd.DataFrame):
        """
        if isinstance(data, pd.DataFrame):
            self._df = data

    def load_data_from_dict(self, data: dict):
        """Set df attribute.

        Args:
            df (dict): dictionary containing the data to be added.
        """
        processed_data = {}
        for key, values in data.items():
            processed_data[key] = pd.Series(data=(values), dtype=object)
        self._df = pd.DataFrame(processed_data)

    def add(self, data):
        """Add a row to data.

        Args:
            data (dict): dictionary containing the data to be added.
                        Every key should have the following form:
                        ``<name>[<unit>]``.
        """
        l = len(self)
        for key, value in data.items():
            self.df.loc[l, key] = value

    def get_values(self, quantity):
        """Get values of a quantity in specified units.

        Args:
            quantity (str): Quantity to get the values of.

        Returns:
            ``pd.Series`` with the quantity values in the given units.
        """
        return self.df[quantity].values

    @classmethod
    def load_data(cls, folder, routine, format, name):
        """Load data from specific format.

        Args:
            folder (path): path to the output folder from which the data will be loaded
            routine (str): calibration routine data to be loaded
            format (str): data format. Possible choices are 'csv' and 'pickle'.

        Returns:
            data (``Data``): data object with the loaded data.
        """
        obj = cls()
        if format == "csv":
            file = f"{folder}/data/{routine}/{name}.csv"
            obj.df = pd.read_csv(file)
            obj.df.pop("Unnamed: 0")
        elif format == "pickle":
            file = f"{folder}/data/{routine}/{name}.pkl"
            obj.df = pd.read_pickle(file)
        else:
            raise_error(ValueError, f"Cannot load data using {format} format.")

        return obj

    def to_csv(self, path):
        """Save data in csv file.

        Args:
            path (str): Path containing output folder."""
        self.df.to_csv(f"{path}/{self.name}.csv")

    def to_pickle(self, path):
        """Save data in pickel file.

        Args:
            path (str): Path containing output folder."""
        self.df.to_pickle(f"{path}/{self.name}.pkl")

src/qibocal/config.py METASEP
"""Custom logger implemenation."""
import logging
import os

# Logging levels available here https://docs.python.org/3/library/logging.html#logging-levels
QIBOCAL_LOG_LEVEL = 30
if "QIBOCAL_LOG_LEVEL" in os.environ:  # pragma: no cover
    QIBOCAL_LOG_LEVEL = 10 * int(os.environ.get("QIBOCAL_LOG_LEVEL"))


def raise_error(exception, message=None, args=None):
    """Raise exception with logging error.

    Args:
        exception (Exception): python exception.
        message (str): the error message.
    """
    log.error(message)
    if args:
        raise exception(message, args)
    else:
        raise exception(message)


# Configuration for logging mechanism
class CustomHandler(logging.StreamHandler):
    """Custom handler for logging algorithm."""

    def format(self, record):
        """Format the record with specific format."""
        from qibocal import __version__

        fmt = f"[Qibocal {__version__}|%(levelname)s|%(asctime)s]: %(message)s"

        grey = "\x1b[38;20m"
        green = "\x1b[92m"
        yellow = "\x1b[33;20m"
        red = "\x1b[31;20m"
        bold_red = "\x1b[31;1m"
        reset = "\x1b[0m"

        self.FORMATS = {
            logging.DEBUG: green + fmt + reset,
            logging.INFO: grey + fmt + reset,
            logging.WARNING: yellow + fmt + reset,
            logging.ERROR: red + fmt + reset,
            logging.CRITICAL: bold_red + fmt + reset,
        }
        log_fmt = self.FORMATS.get(record.levelno)
        return logging.Formatter(log_fmt, datefmt="%Y-%m-%d %H:%M:%S").format(record)


# allocate logger object
log = logging.getLogger(__name__)
log.setLevel(QIBOCAL_LOG_LEVEL)
log.addHandler(CustomHandler())

src/qibocal/__init__.py METASEP
from .cli import command, live_plot, upload

"""qibocal: Quantum Calibration Verification and Validation using Qibo."""
import importlib.metadata as im

__version__ = im.version(__package__)

src/qibocal/calibrations/__init__.py METASEP
from qibocal.calibrations.characterization.allXY import *
from qibocal.calibrations.characterization.calibrate_qubit_states import *
from qibocal.calibrations.characterization.flipping import *
from qibocal.calibrations.characterization.qubit_spectroscopy import *
from qibocal.calibrations.characterization.rabi_oscillations import *
from qibocal.calibrations.characterization.ramsey import *
from qibocal.calibrations.characterization.resonator_spectroscopy import *
from qibocal.calibrations.characterization.t1 import *
from qibocal.calibrations.protocols.test import *

src/qibocal/calibrations/protocols/test.py METASEP
from qibo import gates, models

from qibocal.data import Data


def test(
    platform,
    qubit: list,
    nshots,
    points=1,
):
    data = Data("test", quantities=["nshots", "probabilities"])
    nqubits = len(qubit)
    circuit = models.Circuit(nqubits)
    circuit.add(gates.H(qubit[0]))
    circuit.add(gates.H(qubit[1]))
    # circuit.add(gates.H(1))
    circuit.add(gates.M(*qubit))
    execution = circuit(nshots=nshots)

    data.add({"nshots": nshots, "probabilities": execution.probabilities()})
    yield data

src/qibocal/calibrations/protocols/__init__.py METASEP

src/qibocal/calibrations/protocols/abstract.py METASEP

from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
        subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]
        fig = make_subplots(
            rows=l, cols=1 if len == 1 else 2,
            subplot_titles = subplot_titles)
        for count, fig_dict in enumerate(self.all_figures):
            plot_list = fig_dict['figs']
            for plot in plot_list:
                fig.add_trace(plot, row=count//2 + 1, col = count%2+1)
        fig.update_xaxes(title_font_size=18, tickfont_size=16)
        fig.update_yaxes(title_font_size=18, tickfont_size=16)
        fig.update_layout(
            font_family="Averta",
            hoverlabel_font_family="Averta",
            title_text="Report",
            hoverlabel_font_size=16,
            showlegend=True,
            height=500 * int(l/2),
            width=1000,
        )
        return fig


def embed_unitary_circuit(circuit: Circuit, nqubits: int, support: list) -> Circuit:
    """Takes a circuit and redistributes the gates to the support of
    a new circuit with ``nqubits`` qubits.

    Args:
        circuit (Circuit): The circuit with len(``support``) many qubits.
        nqubits (int): Qubits of new circuit.
        support (list): The qubits were the gates should be places.

    Returns:
        Circuit: Circuit with redistributed gates.
    """

    idxmap = np.vectorize(lambda idx: support[idx])
    newcircuit = Circuit(nqubits)
    for gate in circuit.queue:
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
infile
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
        subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]
        fig = make_subplots(
            rows=l, cols=1 if len == 1 else 2,
            subplot_titles = subplot_titles)
        for count, fig_dict in enumerate(self.all_figures):
            plot_list = fig_dict['figs']
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
        subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]
        fig = make_subplots(
            rows=l, cols=1 if len == 1 else 2,
            subplot_titles = subplot_titles)
        for count, fig_dict in enumerate(self.all_figures):
            plot_list = fig_dict['figs']
            for plot in plot_list:
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
        subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]
        fig = make_subplots(
            rows=l, cols=1 if len == 1 else 2,
            subplot_titles = subplot_titles)
        for count, fig_dict in enumerate(self.all_figures):
            plot_list = fig_dict['figs']
            for plot in plot_list:
                fig.add_trace(plot, row=count//2 + 1, col = count%2+1)
        fig.update_xaxes(title_font_size=18, tickfont_size=16)
        fig.update_yaxes(title_font_size=18, tickfont_size=16)
        fig.update_layout(
            font_family="Averta",
            hoverlabel_font_family="Averta",
            title_text="Report",
            hoverlabel_font_size=16,
            showlegend=True,
            height=500 * int(l/2),
            width=1000,
        )
        return fig


def embed_unitary_circuit(circuit: Circuit, nqubits: int, support: list) -> Circuit:
    """Takes a circuit and redistributes the gates to the support of
    a new circuit with ``nqubits`` qubits.

    Args:
        circuit (Circuit): The circuit with len(``support``) many qubits.
        nqubits (int): Qubits of new circuit.
        support (list): The qubits were the gates should be places.

    Returns:
        Circuit: Circuit with redistributed gates.
    """

    idxmap = np.vectorize(lambda idx: support[idx])
    newcircuit = Circuit(nqubits)
    for gate in circuit.queue:
        if not isinstance(gate, gates.measurements.M):
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
        subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]
        fig = make_subplots(
            rows=l, cols=1 if len == 1 else 2,
            subplot_titles = subplot_titles)
        for count, fig_dict in enumerate(self.all_figures):
            plot_list = fig_dict['figs']
            for plot in plot_list:
                fig.add_trace(plot, row=count//2 + 1, col = count%2+1)
        fig.update_xaxes(title_font_size=18, tickfont_size=16)
        fig.update_yaxes(title_font_size=18, tickfont_size=16)
        fig.update_layout(
            font_family="Averta",
            hoverlabel_font_family="Averta",
            title_text="Report",
            hoverlabel_font_size=16,
            showlegend=True,
            height=500 * int(l/2),
            width=1000,
        )
        return fig


def embed_unitary_circuit(circuit: Circuit, nqubits: int, support: list) -> Circuit:
    """Takes a circuit and redistributes the gates to the support of
    a new circuit with ``nqubits`` qubits.

    Args:
        circuit (Circuit): The circuit with len(``support``) many qubits.
        nqubits (int): Qubits of new circuit.
        support (list): The qubits were the gates should be places.

    Returns:
        Circuit: Circuit with redistributed gates.
    """

    idxmap = np.vectorize(lambda idx: support[idx])
    newcircuit = Circuit(nqubits)
    for gate in circuit.queue:
        if not isinstance(gate, gates.measurements.M):
            newcircuit.add(
                gate.__class__(gate.init_args[0], *idxmap(np.array(gate.init_args[1:])))
            )
        else:
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
inproject
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
common
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
common
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
commited
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
commited
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
commited
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """
non_informative
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
non_informative
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
non_informative
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
random
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
random
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=x_fit,
            y=y_fit,
            name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),
            line=go.scatter.Line(dash="dot"),
        )
        myfigs.append(fig)
        self.all_figures.append({'figs' : myfigs})

    def report(self):
        from plotly.subplots import make_subplots

        l = len(self.all_figures)
        subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]
        fig = make_subplots(
            rows=l, cols=1 if len == 1 else 2,
            subplot_titles = subplot_titles)
        for count, fig_dict in enumerate(self.all_figures):
random
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
        return circuit

    def clifford_unitary(
        self, theta: float = 0, nx: float = 0, ny: float = 0, nz: float = 0
    ) -> np.ndarray:
        """Four given parameters are used to build one Clifford unitary.

        Args:
            theta (float) : An angle
            nx (float) : prefactor
            ny (float) : prefactor
            nz (float) : prefactor

        Returns:
            ``qibo.gates.Unitary`` with the drawn matrix as unitary.
        """
        matrix = np.array(
            [
                [
                    np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),
                    -ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                ],
                [
                    ny * np.sin(theta / 2) - 1.0j * nx * np.sin(theta / 2),
                    np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),
                ],
            ]
        )
        return matrix

    def gates(self) -> list(gates.Unitary):
        """Draws the parameters and builds the unitary Clifford gates for
        a circuit layer.

        Returns:
            list filled with ``qibo.gates.Unitary``:
                the simulatanous Clifford gates.
        """
        # There are this many different Clifford matrices.
        amount = len(ONEQUBIT_CLIFFORD_PARAMS)
        gates_list = []
        # Choose as many random integers between 0 and 23 as there are used
        # qubits. Get the clifford parameters and build the unitares.
        for count, rint in enumerate(np.random.randint(0, amount, size=len(self.qubits))):
            # Build the random Clifford matrices append them
            gates_list.append(
                gates.Unitary(
                    self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count
                )
            )
        # Make a unitary gate out of 'unitary' for the qubits.
        return gates_list
    

class Experiment:
    """Experiment objects which holds an iterable circuit factory along with
    a simple data structure associated to each circuit.

    Args:
        circuitfactory (Iterable): Gives a certain amount of circuits when
            iterated over.
        data (list): If filled ``data`` can be used to specifying parameters
            while executing a circuit or deciding how to process results.
        nshots (int): For execution of circuit, indicates how many shots.
    """

    def __init__(
        self,
        circuitfactory: Iterable,
        nshots: int = None,
        data: list = None,
        noisemodel: NoiseModel = None,
    ) -> None:
        """ """
        self.circuitfactory = circuitfactory
        self.nshots = nshots
        self.data = data
        self.__noise_model = noisemodel

    @classmethod
    def load(cls, path: str) -> Experiment:
        """Creates an object with data and if possible with circuits.

        Args:
            path (str): The directory from where the object should be restored.

        Returns:
            Experiment: The object with data (and circuitfactory).
        """
        datapath = f"{path}data.pkl"
        circuitspath = f"{path}circuits.pkl"
        if isfile(datapath):
            with open(datapath, "rb") as f:
                data = pickle.load(f)
                if isinstance(data, pd.DataFrame):
                    data = data.to_dict("records")
            nshots = len(data[0]["samples"])
        else:
            data = None
        if isfile(circuitspath):
            with open(circuitspath, "rb") as f:
                circuitfactory = pickle.load(f)
        else:
            circuitfactory = None
        # Initiate an instance of the experiment class.
        obj = cls(circuitfactory, data=data, nshots=nshots)
        return obj

    def prebuild(self) -> None:
        """Converts the attribute ``circuitfactory`` which is in general
        an iterable into a list.
        """
        self.circuitfactory = list(self.circuitfactory)

    def execute(self) -> None:
        """Calls method ``single_task`` while iterating over attribute
        ``circuitfactory```.

        Collects data given the already set data and overwrites
        attribute ``data``.
        """
        if self.circuitfactory is None:
            raise NotImplementedError("There are no circuits to execute.")
        newdata = []
        for circuit in self.circuitfactory:
            try:
                datarow = next(self.data)
            except TypeError:
                datarow = {}
            newdata.append(self.single_task(deepcopy(circuit), datarow))
        self.data = newdata

    def single_task(self, circuit: Circuit, datarow: dict) -> None:
        """Executes a circuit, returns the single shot results.

        Args:
            circuit (Circuit): Will be executed, has to return samples.
            datarow (dict): Dictionary with parameters for execution and
                immediate postprocessing information.
        """
        if self.__noise_model is not None:
            circuit = self.__noise_model.apply(circuit)
        samples = circuit(nshots=self.nshots).samples()
        return {"samples": samples}

    def save(self) -> None:
        """Creates a path and pickles relevent data from ``self.data`` and
        if ``self.circuitfactory`` is a list that one too.
        """
        self.path = experiment_directory("standardrb")
        if isinstance(self.circuitfactory, list):
            with open(f"{self.path}circuits.pkl", "wb") as f:
                pickle.dump(self.circuitfactory, f)
        with open(f"{self.path}data.pkl", "wb") as f:
            pickle.dump(self.data, f)

    @property
    def dataframe(self) -> pd.DataFrame:
        return pd.DataFrame(self.data)

    def _append_data(self, name: str, datacolumn: list) -> None:
        """Adds data column to ``data`` attribute.

        Args:
            name (str): Name of data column.
            datacolumn (list): A list of the right shape
        """
        if len(datacolumn) != len(self.data):
            raise ValueError("Given data column doesn't have the right length.")
        df = self.dataframe
        df[name] = datacolumn
        self.data = df.to_dict("records")

    @property
    def samples(self) -> np.ndarray:
        """Returns the samples from ``self.data`` in a 2d array.

        Returns:
            np.ndarray: 2d array of samples.
        """

        try:
            return np.array(self.dataframe["samples"].tolist())
        except KeyError:
            print("No samples here. Execute experiment first.")
            return None

    @property
    def probabilities(self) -> np.ndarray:
        """Takes the stored samples and returns probabilities for each
        possible state to occure.

        Returns:
            np.ndarray: Probability array of 2 dimension.
        """

        allsamples = self.samples
        if allsamples is None:
            print("No probabilities either.")
            return None
        # Create all possible state vectors.
        allstates = list(product([0, 1], repeat=len(allsamples[0][0])))
        # Iterate over all the samples and count the different states.
        probs = [
            [np.sum(np.product(samples == state, axis=1)) for state in allstates]
            for samples in allsamples
        ]
        probs = np.array(probs) / (self.nshots)
        return probs

    def apply_task(self, gtask):
        self = gtask(self)


class Result:
    """Once initialized with the correct parameters an Result object can build
    reports to display results of an randomized benchmarking experiment.
    """

    def __init__(self, dataframe: pd.DataFrame) -> None:
        self.df = dataframe
        self.all_figures = []

    def extract(self, group_by: str, output: str, agg_type: str):
        """Aggregates the dataframe, extracts the data by which the frame was
        grouped, what was calculated given the ``agg_type`` parameters.

        Args:
            group_by (str): _description_
            output (str): _description_
            agg_type (str): _description_
        """
        grouped_df = self.df.groupby(group_by)[output].apply(agg_type)
        return np.array(grouped_df.index), np.array(grouped_df.values.tolist())
    
    def scatter_fit_fig(self, xdata_scatter, ydata_scatter, xdata, ydata):
        myfigs = []
        popt, pcov, x_fit, y_fit = self.fitting_func(xdata, ydata)
        fig = go.Scatter(
            x=xdata_scatter,
            y=ydata_scatter,
            line=dict(color="#6597aa"),
            mode="markers",
            marker={"opacity": 0.2, "symbol": "square"},
            name="runs",
        )
        myfigs.append(fig)
        fig = go.Scatter(
            x=xdata, y=ydata, line=dict(color="#aa6464"), mode="markers", name="average"
        )
random
from __future__ import annotations

import pickle
from collections.abc import Iterable
from copy import deepcopy
from itertools import product
from os.path import isfile
import plotly.graph_objects as go

import numpy as np
import pandas as pd
from qibo import gates
from qibo.models import Circuit
from qibo.noise import NoiseModel

from qibocal.calibrations.protocols.utils import (
    ONEQUBIT_CLIFFORD_PARAMS,
    experiment_directory,
)


class Circuitfactory:
    """TODO write documentation
    TODO make the embedding into lager qubit space possible"""

    def __init__(
        self, nqubits: int, depths: list, runs: int, qubits: list = None
    ) -> None:
        self.nqubits = nqubits if nqubits is not None else len(qubits)
        self.qubits = qubits if qubits is not None else [x for x in range(nqubits)]
        self.depths = depths
        self.runs = runs

    def __len__(self):
        return self.runs * len(self.depths)

    def __iter__(self) -> None:
        self.n = 0
        return self

    def __next__(self) -> None:
        if self.n >= self.runs * len(self.depths):
            raise StopIteration
        else:
            circuit = self.build_circuit(self.depths[self.n % len(self.depths)])
            self.n += 1
            # Distribute the circuit onto the given support.
            bigcircuit = Circuit(self.nqubits)
            bigcircuit.add(circuit.on_qubits(*self.qubits))
            return bigcircuit

    def build_circuit(self, depth: int):
        raise NotImplementedError


class SingleCliffordsFactory(Circuitfactory):
    def __init__(
        self, nqubits: list, depths: list, runs: int, qubits: list = None
    ) -> None:
        super().__init__(nqubits, depths, runs, qubits)

    def build_circuit(self, depth: int):
        circuit = Circuit(len(self.qubits))
        for _ in range(depth):
            circuit.add(self.gates())
        circuit.add(gates.M(*range(len(self.qubits))))
random
            newdata.append(self.single_task(deepcopy(circuit), datarow))         allsamples = self.samples             circuit = self.build_circuit(self.depths[self.n % len(self.depths)])             [np.sum(np.product(samples == state, axis=1)) for state in allstates]             for samples in allsamples         samples = circuit(nshots=self.nshots).samples()         return {"samples": samples}                 gates.Unitary(                     self.clifford_unitary(*ONEQUBIT_CLIFFORD_PARAMS[rint]), count         if not isinstance(gate, gates.measurements.M):             return np.array(self.dataframe["samples"].tolist())             circuit.add(self.gates())         circuit.add(gates.M(*range(len(self.qubits))))             name="A: {:.3f}, p: {:.3f}, B: {:.3f}".format(popt[0], popt[1], popt[2]),         grouped_df = self.df.groupby(group_by)[output].apply(agg_type)         df = self.dataframe         df[name] = datacolumn         self.data = df.to_dict("records")             bigcircuit.add(circuit.on_qubits(*self.qubits))             for plot in plot_list:                 fig.add_trace(plot, row=count//2 + 1, col = count%2+1)             newcircuit.add(                     np.cos(theta / 2) - 1.0j * nz * np.sin(theta / 2),             newcircuit.add(gates.M(*idxmap(np.array(gate.init_args[0:]))))                     np.cos(theta / 2) + 1.0j * nz * np.sin(theta / 2),         self.df = dataframe         subplot_titles = [figdict.get('subplot_title') for figdict in self.all_figures]         return np.array(grouped_df.index), np.array(grouped_df.values.tolist())                 data = pickle.load(f)         super().__init__(nqubits, depths, runs, qubits)                 circuitfactory = pickle.load(f)         an iterable into a list.         """Draws the parameters and builds the unitary Clifford gates for         return pd.DataFrame(self.data)         return self             plot_list = fig_dict['figs']         myfigs.append(fig)         return circuit