From af0a4128fb98519c79dad195b22c429ed3e49e13 Mon Sep 17 00:00:00 2001 From: Tal Date: Thu, 10 Jun 2021 08:50:35 +0300 Subject: [PATCH] Merge pull request #2 * Initialized poetry project and added bakery files * adding ci * updating dependencies * formatting code * fixing dependencies for 3.7 --- .github/workflows/create-release.yml | 27 + .github/workflows/on-pull-request.yml | 22 + .gitignore | 260 +++---- README.md | 0 .../RB_1_qubit/RB_1qb_configuration.py | 171 +++++ .../bakery_examples/RB_1_qubit/RB_1qubit.py | 81 +++ .../RB_1_qubit/RB_first_trial.py | 50 ++ .../RB_1_qubit/RB_first_trial_utils.py | 197 +++++ .../bakery_examples/RB_1_qubit/RB_utils.py | 208 ++++++ .../RB_1_qubit/c1_cayley_table.csv | 25 + .../RamseyGauss_configuration.py | 222 ++++++ .../Ramsey_fringes/Ramsey_Gauss_baking.py | 36 + examples/bakery_examples/XEB/xeb_config.py | 188 +++++ examples/bakery_examples/XEB/xeb_example.py | 35 + examples/bakery_examples/XEB/xeb_utils.py | 43 ++ poetry.lock | 627 ++++++++++++++++ pyproject.toml | 24 + qualang_tools/__init__.py | 0 qualang_tools/bakery/__init__.py | 0 qualang_tools/bakery/bakery.py | 671 ++++++++++++++++++ qualang_tools/tests/__init__.py | 0 21 files changed, 2758 insertions(+), 129 deletions(-) create mode 100644 .github/workflows/create-release.yml create mode 100644 .github/workflows/on-pull-request.yml create mode 100644 README.md create mode 100644 examples/bakery_examples/RB_1_qubit/RB_1qb_configuration.py create mode 100644 examples/bakery_examples/RB_1_qubit/RB_1qubit.py create mode 100644 examples/bakery_examples/RB_1_qubit/RB_first_trial.py create mode 100644 examples/bakery_examples/RB_1_qubit/RB_first_trial_utils.py create mode 100644 examples/bakery_examples/RB_1_qubit/RB_utils.py create mode 100644 examples/bakery_examples/RB_1_qubit/c1_cayley_table.csv create mode 100644 examples/bakery_examples/Ramsey_fringes/RamseyGauss_configuration.py create mode 100644 examples/bakery_examples/Ramsey_fringes/Ramsey_Gauss_baking.py create mode 100644 examples/bakery_examples/XEB/xeb_config.py create mode 100644 examples/bakery_examples/XEB/xeb_example.py create mode 100644 examples/bakery_examples/XEB/xeb_utils.py create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 qualang_tools/__init__.py create mode 100644 qualang_tools/bakery/__init__.py create mode 100644 qualang_tools/bakery/bakery.py create mode 100644 qualang_tools/tests/__init__.py diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 00000000..229e9425 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,27 @@ +name: release-version +on: + workflow_dispatch: + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: 3.7 + - uses: Gr1N/setup-poetry@v4 + - name: Build package + run: | + export RELEASE_VERSION="${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + poetry version ${RELEASE_VERSION} + poetry build + - name: Upload assets + uses: qm-devops/qm-upload-release-assets@v1 + with: + token: ${{secrets.GITHUB_TOKEN}} + path: | + ./dist/*.tar.gz + ./dist/*.whl diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml new file mode 100644 index 00000000..c299acfa --- /dev/null +++ b/.github/workflows/on-pull-request.yml @@ -0,0 +1,22 @@ +name: on-pull-request +on: [workflow_dispatch,pull_request] +jobs: + build-n-test: + env: + POETRY_CACHE_FOLDER: '/home/runner/.cache/pypoetry/virtualenvs' + JUNIT_REPORT_PATH: pytest-junit-report + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: 3.7 + - uses: Gr1N/setup-poetry@v4 + - uses: actions/cache@v2 + with: + path: | + ${{ env.POETRY_CACHE_FOLDER }} + key: poetry-pr-${{ runner.os }}-${{ hashFiles('poetry.lock') }} + - run: poetry install + - id: black-check + run: poetry run poe check_format \ No newline at end of file diff --git a/.gitignore b/.gitignore index b6e47617..65ee059c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,129 +1,131 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ +.idea + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..e69de29b diff --git a/examples/bakery_examples/RB_1_qubit/RB_1qb_configuration.py b/examples/bakery_examples/RB_1_qubit/RB_1qb_configuration.py new file mode 100644 index 00000000..02c84540 --- /dev/null +++ b/examples/bakery_examples/RB_1_qubit/RB_1qb_configuration.py @@ -0,0 +1,171 @@ +import numpy as np + +pulse_len = 80 +readout_len = 400 +qubit_IF = 50e6 +rr_IF = 50e6 +qubit_LO = 6.345e9 +rr_LO = 4.755e9 + + +def gauss(amplitude, mu, sigma, length): + t = np.linspace(-length / 2, length / 2, length) + gauss_wave = amplitude * np.exp(-((t - mu) ** 2) / (2 * sigma ** 2)) + return [float(x) for x in gauss_wave] + + +def IQ_imbalance(g, phi): + c = np.cos(phi) + s = np.sin(phi) + N = 1 / ((1 - g ** 2) * (2 * c ** 2 - 1)) + return [float(N * x) for x in [(1 - g) * c, (1 + g) * s, (1 - g) * s, (1 + g) * c]] + + +gauss_pulse = gauss(0.2, 0, 20, pulse_len) + +config = { + "version": 1, + "controllers": { + "con1": { + "type": "opx1", + "analog_outputs": { + 1: {"offset": +0.0}, # qe-I + 2: {"offset": +0.0}, # qe-Q + 3: {"offset": +0.0}, # rr-I + 4: {"offset": +0.0}, # rr-Q + }, + "digital_outputs": { + 1: {}, + }, + "analog_inputs": { + 1: {"offset": +0.0}, + }, + } + }, + "elements": { + "qe1": { + "mixInputs": { + "I": ("con1", 1), + "Q": ("con1", 2), + "lo_frequency": qubit_LO, + "mixer": "mixer_qubit", + }, + "outputs": {"output1": ("con1", 1)}, + "intermediate_frequency": qubit_IF, + "operations": { + "I": "IPulse", + "X/2": "X/2Pulse", + "X": "XPulse", + "-X/2": "-X/2Pulse", + "Y/2": "Y/2Pulse", + "Y": "YPulse", + "-Y/2": "-Y/2Pulse", + }, + }, + "rr": { + "mixInputs": { + "I": ("con1", 3), + "Q": ("con1", 4), + "lo_frequency": rr_LO, + "mixer": "mixer_RR", + }, + "intermediate_frequency": rr_IF, + "operations": { + "readout": "readout_pulse", + }, + "outputs": {"out1": ("con1", 1)}, + "time_of_flight": 28, + "smearing": 0, + }, + }, + "pulses": { + "constPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "gauss_wf", "Q": "gauss_wf"}, + }, + "IPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "zero_wf", "Q": "zero_wf"}, + }, + "XPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "pi_wf", "Q": "zero_wf"}, + }, + "X/2Pulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "pi/2_wf", "Q": "zero_wf"}, + }, + "-X/2Pulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "-pi/2_wf", "Q": "zero_wf"}, + }, + "YPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "zero_wf", "Q": "pi_wf"}, + }, + "Y/2Pulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "zero_wf", "Q": "pi/2_wf"}, + }, + "-Y/2Pulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "zero_wf", "Q": "-pi/2_wf"}, + }, + "readout_pulse": { + "operation": "measurement", + "length": readout_len, + "waveforms": {"I": "readout_wf", "Q": "zero_wf"}, + "integration_weights": { + "integW1": "integW1", + "integW2": "integW2", + }, + "digital_marker": "ON", + }, + }, + "waveforms": { + "const_wf": {"type": "constant", "sample": 0.2}, + "gauss_wf": {"type": "arbitrary", "samples": gauss_pulse}, + "pi_wf": {"type": "arbitrary", "samples": gauss(0.2, 0, 12, pulse_len)}, + "-pi/2_wf": {"type": "arbitrary", "samples": gauss(-0.1, 0, 12, pulse_len)}, + "pi/2_wf": {"type": "arbitrary", "samples": gauss(0.1, 0, 12, pulse_len)}, + "zero_wf": {"type": "constant", "sample": 0}, + "readout_wf": {"type": "constant", "sample": 0.3}, + }, + "digital_waveforms": { + "ON": {"samples": [(1, 0)]}, + }, + "integration_weights": { + "integW1": { + "cosine": [1.0] * int(readout_len / 4), + "sine": [0.0] * int(readout_len / 4), + }, + "integW2": { + "cosine": [0.0] * int(readout_len / 4), + "sine": [1.0] * int(readout_len / 4), + }, + }, + "mixers": { + "mixer_qubit": [ + { + "intermediate_frequency": qubit_IF, + "lo_frequency": qubit_LO, + "correction": IQ_imbalance(0.0, 0.0), + } + ], + "mixer_RR": [ + { + "intermediate_frequency": rr_IF, + "lo_frequency": rr_LO, + "correction": IQ_imbalance(0.0, 0.0), + } + ], + }, +} diff --git a/examples/bakery_examples/RB_1_qubit/RB_1qubit.py b/examples/bakery_examples/RB_1_qubit/RB_1qubit.py new file mode 100644 index 00000000..7bad4e6e --- /dev/null +++ b/examples/bakery_examples/RB_1_qubit/RB_1qubit.py @@ -0,0 +1,81 @@ +from qm import SimulationConfig +from qm.qua import * +from qm.QmJob import QmJob +from qm.QuantumMachinesManager import QuantumMachinesManager +from rb_1qb_configuration import config, pulse_len +from rb_utils import RBOneQubit +import matplotlib.pyplot as plt + + +d_max = 300 # Maximum RB sequence length +K = 1 # Number of RB sequences + +RB = RBOneQubit(config, d_max, K, "qe1") +RB_sequences = RB.sequences +RB_baked_sequences = RB.baked_sequences +duration_trackers = RB.duration_trackers +inverse_ops = RB.inverse_ops + + +with program() as RB_prog: + truncate = declare(int) + inverse_op = declare(int) + + I = declare(fixed) + th = declare(fixed, value=0.0) + state = declare(bool) + + out_str = declare_stream() + + for k in range(K): + truncate_array = declare( + int, value=[x * pulse_len // 4 for x in duration_trackers[k]] + ) + + inverse_ops_QUA = declare(int, value=inverse_ops[k]) + with for_each_((truncate, inverse_op), (truncate_array, inverse_ops_QUA)): + + align("qe1", "rr") + wait(30, "qe1") + play( + RB_baked_sequences[k].operations["qe1"], "qe1", truncate=truncate + ) # Truncate for RB seq of smaller lengths + RB_sequences[k].play_revert_op2(inverse_op) + + align("qe1", "rr") + # Measurement + measure("readout", "rr", None, integration.full("integW1", I)) + # Active reset + with if_(state): + play("X", "qe1") + + assign(state, I > th) + + save(state, out_str) + save(inverse_op, "inv") + save(truncate, "truncate") + + with stream_processing(): + out_str.boolean_to_int().buffer(K, d_max).average().save("out_stream") + + +qmm = QuantumMachinesManager() +job: QmJob = qmm.simulate(config, RB_prog, SimulationConfig(20000)) +results = job.result_handles + +inv = results.inv.fetch_all()["value"] +truncate = results.truncate.fetch_all()["value"] + +# # Plot simulated samples +# samples = job.get_simulated_samples() +# samples.con1.plot() +# +# print('Inversion operations:', inv) +# print('Truncations indices:', truncate) +# +# # Plotting baked RB sequence +# baked_pulse_I = config["waveforms"]["qe1_baked_wf_I_0"]["samples"] +# baked_pulse_Q = config["waveforms"]["qe1_baked_wf_Q_0"]["samples"] +# t = np.arange(0, len(baked_pulse_I), 1) +# plt.plot(t, baked_pulse_I) +# plt.plot(t, baked_pulse_Q) diff --git a/examples/bakery_examples/RB_1_qubit/RB_first_trial.py b/examples/bakery_examples/RB_1_qubit/RB_first_trial.py new file mode 100644 index 00000000..ef50227f --- /dev/null +++ b/examples/bakery_examples/RB_1_qubit/RB_first_trial.py @@ -0,0 +1,50 @@ +from qm.QuantumMachinesManager import QuantumMachinesManager +from qm import SimulationConfig +from RB_1qb_configuration import * + +N_avg = 1 +circuit_depth_vec = list(range(1, 10, 2)) +t1 = 10 +b_list = [] + +# Prepare baked waveforms, one baking for each circuit depth +for depth in circuit_depth_vec: + with baking(config, padding_method="right") as b: + generate_cliffords(b, "qe1", pulse_length=10) + final_state = randomize_and_play_circuit(depth, b) + play_clifford(recovery_clifford(final_state), final_state, b) + + b_list.append(b) + +# Open communication with the server. +QMm = QuantumMachinesManager() +QM1 = QMm.open_qm(config, close_other_machines=True) + +# QUA Program for 1 qubit RB: +with program() as RBprog: + N = declare(int) + I = declare(fixed) + state = declare(bool) + out_str = declare_stream() + + with for_(N, 0, N < N_avg, N + 1): + for i in range(len(circuit_depth_vec)): + align("rr", "qe1") + b_list[i].run() + align("rr", "qe1") + measure_state(state, I) + save(state, out_str) + active_reset(state) + + with stream_processing(): + out_str.boolean_to_int().buffer(len(circuit_depth_vec)).average().save( + "out_stream" + ) + +job = QM1.simulate(RBprog, SimulationConfig(int(1000))) +res = job.result_handles +avg_state = res.out_stream.fetch_all() + +samples = job.get_simulated_samples() + +samples.con1.plot() diff --git a/examples/bakery_examples/RB_1_qubit/RB_first_trial_utils.py b/examples/bakery_examples/RB_1_qubit/RB_first_trial_utils.py new file mode 100644 index 00000000..6f86e678 --- /dev/null +++ b/examples/bakery_examples/RB_1_qubit/RB_first_trial_utils.py @@ -0,0 +1,197 @@ +# The list of 1 Qubit cliffords, X are pi rotations, X/2 are pi/2 rotations around the X axis (Y accordingly) +from qm.qua import * +from bakery.bakery import * +from rb_1qb_configuration import gauss + + +cliffords = [ + ["I"], + ["X"], + ["Y"], + ["Y", "X"], + ["X/2", "Y/2"], + ["X/2", "-Y/2"], + ["-X/2", "Y/2"], + ["-X/2", "-Y/2"], + ["Y/2", "X/2"], + ["Y/2", "-X/2"], + ["-Y/2", "X/2"], + ["-Y/2", "-X/2"], + ["X/2"], + ["-X/2"], + ["Y/2"], + ["-Y/2"], + ["-X/2", "Y/2", "X/2"], + ["-X/2", "-Y/2", "X/2"], + ["X", "Y/2"], + ["X", "-Y/2"], + ["Y", "X/2"], + ["Y", "-X/2"], + ["X/2", "Y/2", "X/2"], + ["-X/2", "Y/2", "-X/2"], +] +operations = { + "z": ["I"], + "-x": ["-Y/2"], + "y": ["X/2"], + "-y": ["-X/2"], + "x": ["Y/2"], + "-z": ["X"], +} + +transformations = { + "x": { + "I": "x", + "X/2": "x", + "X": "x", + "-X/2": "x", + "Y/2": "z", + "Y": "-x", + "-Y/2": "-z", + }, + "-x": { + "I": "-x", + "X/2": "-x", + "X": "-x", + "-X/2": "-x", + "Y/2": "-z", + "Y": "x", + "-Y/2": "z", + }, + "y": { + "I": "y", + "X/2": "z", + "X": "-y", + "-X/2": "-z", + "Y/2": "y", + "Y": "y", + "-Y/2": "y", + }, + "-y": { + "I": "-y", + "X/2": "-z", + "X": "y", + "-X/2": "z", + "Y/2": "-y", + "Y": "-y", + "-Y/2": "-y", + }, + "z": { + "I": "z", + "X/2": "-y", + "X": "-z", + "-X/2": "y", + "Y/2": "-x", + "Y": "-z", + "-Y/2": "x", + }, + "-z": { + "I": "-z", + "X/2": "y", + "X": "z", + "-X/2": "-y", + "Y/2": "x", + "Y": "z", + "-Y/2": "-x", + }, +} + + +def recovery_clifford(state: str): + """ + Returns the required clifford to return to the ground state based on the position on the bloch sphere + :param state: The current position on the Bloch sphere + :return: A string representing the recovery clifford + """ + # operations = {'x': ['I'], '-x': ['Y'], 'y': ['X/2', '-Y/2'], '-y': ['-X/2', '-Y/2'], 'z': ['-Y/2'], '-z': ['Y/2']} + + return operations[state] + + +def transform_state(input_state: str, transformation: str): + """ + A function to track the next position on the Bloch sphere based on the current position and the applied clifford + :param input_state: Position on the bloch sphere (one of the six poles) + :param transformation: A clifford operation + :return: The next state on the bloch sphere + """ + + return transformations[input_state][transformation] + + +def play_clifford(clifford: list, state: str, b: Baking): + """ + + :param clifford: a list of cliffords + :param state: a string representing the current state on the bloch sphere + :param b: Baking object where the sequence should be generated + :return: the final state on the bloch sphere + """ + for op in clifford: + state = transform_state(state, op) + if op != "I": + b.play(op, "qe1") + return state + + +def randomize_and_play_circuit(n_gates: int, b: Baking, init_state: str = "z"): + """ + + :param n_gates: the depth of the circuit + :param init_state: starting position on the bloch sphere + :param b: Baking object + :return: + """ + state = init_state + for ind in range(n_gates): + state = play_clifford(cliffords[np.random.randint(0, len(cliffords))], state, b) + return state + + +def randomize_interleaved_circuit(interleave_op: list, d: int, init_state: str = "z"): + """ + :param interleave_op: The operation to interleave represented as a list of cliffords + :param d: the depth of the circuit + :param init_state: the initial state on the bloch sphere + :return: the final state on the bloch spehre + """ + state = init_state + for ind in range(d): + state = play_clifford(cliffords[np.random.randint(0, len(cliffords))], state) + state = play_clifford(interleave_op, state) + return state + + +def generate_cliffords(b: Baking, qe: str, pulse_length: int): + + short_pi = gauss(0.2, 0, 1, pulse_length) + short_pi_2 = gauss(0.1, 0.0, 1, pulse_length) + short_minus_pi_2 = gauss(-0.1, 0.0, 1, pulse_length) + short_0 = [0.0] * pulse_length + + b.add_Op("X", qe, [short_pi, short_0]) + b.add_Op("Y", qe, [short_0, short_pi]) + b.add_Op("X/2", qe, [short_pi_2, short_0]) + b.add_Op("Y/2", qe, [short_0, short_pi_2]) + b.add_Op("-X/2", qe, [short_minus_pi_2, short_0]) + b.add_Op("-Y/2", qe, [short_0, short_minus_pi_2]) + + +def measure_state(state, I): + """ + A measurement function depending on the type of qubit. + This example implementation is typical of a SC qubit measurement (via a dispersive readout) + :param state: a QUA var where the state will be saved + :param I: a QUA var containing the demod result + :return: none + """ + th = 0 + align("qe1", "rr") + measure("readout", "rr", None, integration.full("integW1", I)) + assign(state, I > th) + + +def active_reset(state): + + with if_(state): + play("X", "qe1") diff --git a/examples/bakery_examples/RB_1_qubit/RB_utils.py b/examples/bakery_examples/RB_1_qubit/RB_utils.py new file mode 100644 index 00000000..16756a5e --- /dev/null +++ b/examples/bakery_examples/RB_1_qubit/RB_utils.py @@ -0,0 +1,208 @@ +import copy + +import pandas as pd +from bakery.bakery import * +from qm.qua import * + +c1_ops = [ # Clifford operations + ("I",), + ("X",), + ("Y",), + ("Y", "X"), + ("X/2", "Y/2"), + ("X/2", "-Y/2"), + ("-X/2", "Y/2"), + ("-X/2", "-Y/2"), + ("Y/2", "X/2"), + ("Y/2", "-X/2"), + ("-Y/2", "X/2"), + ("-Y/2", "-X/2"), + ("X/2",), + ("-X/2",), + ("Y/2",), + ("-Y/2",), + ("-X/2", "Y/2", "X/2"), + ("-X/2", "-Y/2", "X/2"), + ("X", "Y/2"), + ("X", "-Y/2"), + ("Y", "X/2"), + ("Y", "-X/2"), + ("X/2", "Y/2", "X/2"), + ("-X/2", "Y/2", "-X/2"), +] + +# Cayley table corresponding to above Clifford group structure + +c1_table = pd.read_csv("c1_cayley_table.csv").to_numpy()[:, 1:] + + +class RBOneQubit: + def __init__(self, config: dict, d_max: int, K: int, qubit: str): + """ + Class to retrieve easily baked RB sequences and their inverse operations + :param config Configuration file + :param d_max Maximum length of desired RB sequence + :param K Number of RB sequences + :param qubit Name of the quantum element designating the qubit + """ + if not (qubit in config["elements"]): + raise KeyError(f"Quantum element {qubit} is not in the config") + + self.sequences = [RBSequence(config, d_max, qubit) for _ in range(K)] + self.inverse_ops = [seq.revert_ops for seq in self.sequences] + self.duration_trackers = [seq.duration_tracker for seq in self.sequences] + self.baked_sequences = [seq.sequence for seq in self.sequences] + + +def find_revert_op(input_state_index: int): + """Looks in the Cayley table the operation needed to reset the state to ground state from input state_tracker + :param input_state_index Index of the current state tracker + :return index of the next Clifford to apply to invert RB sequence""" + for i in range(len(c1_ops)): + if c1_table[input_state_index][i] == 0: + return i + + +class RBSequence: + def __init__(self, config: dict, d_max: int, qubit: str): + self.d_max = d_max + self.config = config + self.qubit = qubit + self.state_tracker = [ + 0 + ] * d_max # Keeps track of all transformations done on qubit state + self.state_init = 0 + self.revert_ops = [ + 0 + ] * d_max # Keeps track of inverse op index associated to each sequence + self.duration_tracker = [0] * d_max # Keeps track of each Clifford's duration + # self.baked_cliffords = self.generate_cliffords() # List of baking objects for running Cliffords + self.sequence = self.generate_RB_sequence() # Store the RB sequence + + def play_revert_op(self, index: int): + """Plays an operation resetting qubit in its ground state based on the + transformation provided by the index in Cayley table (switch using baked Cliffords) + :param index index of the transformed qubit state""" + + with switch_(index): + for i in range(len(self.baked_cliffords)): + with case_(i): + self.baked_cliffords[i].run() + + def play_revert_op2(self, index: int): + """Plays an operation resetting qubit in its ground state based on the + transformation provided by the index in Cayley table (explicit switch case) + :param index index of the transformed qubit state""" + qubit = self.qubit + + with switch_(index): + with case_(0): + play("I", qubit) + with case_(1): + play("X", qubit) + with case_(2): + play("Y", qubit) + with case_(3): + play("Y", qubit) + play("X", qubit) + with case_(4): + play("X/2", qubit) + play("Y/2", qubit) + with case_(5): + play("X/2", qubit) + play("-Y/2", qubit) + with case_(6): + play("-X/2", qubit) + play("Y/2", qubit) + with case_(7): + play("-X/2", qubit) + play("-Y/2", qubit) + with case_(8): + play("Y/2", qubit) + play("X/2", qubit) + with case_(9): + play("Y/2", qubit) + play("-X/2", qubit) + with case_(10): + play("-Y/2", qubit) + play("X/2", qubit) + with case_(11): + play("-Y/2", qubit) + play("-X/2", qubit) + with case_(12): + play("X/2", qubit) + with case_(13): + play("-X/2", qubit) + with case_(14): + play("Y/2", qubit) + with case_(15): + play("-Y/2", qubit) + with case_(16): + play("-X/2", qubit) + play("Y/2", qubit) + play("X/2", qubit) + with case_(17): + play("-X/2", qubit) + play("-Y/2", qubit) + play("X/2", qubit) + with case_(18): + play("X", qubit) + play("Y/2", qubit) + with case_(19): + play("X", qubit) + play("-Y/2", qubit) + with case_(20): + play("Y", qubit) + play("X/2", qubit) + with case_(21): + play("Y", qubit) + play("-X/2", qubit) + with case_(22): + play("X/2", qubit) + play("Y/2", qubit) + play("X/2", qubit) + with case_(23): + play("-X/2", qubit) + play("Y/2", qubit) + play("-X/2", qubit) + + def generate_cliffords(self): + """ + Returns a list of baking object giving access to baked Clifford waveforms + """ + + baked_clifford = [None] * len(c1_ops) + for i in range(len(c1_ops)): + with baking(self.config) as b2: + for op in c1_ops[i]: + b2.play(op, self.qubit) + baked_clifford[i] = b2 + return baked_clifford + + def generate_RB_sequence(self): + """ + Creates a baking object generating a random Clifford sequence of length d_max + """ + + with baking(self.config) as b: + for d in range(self.d_max): + i = np.random.randint(0, len(c1_ops)) + if d > 0: + self.duration_tracker[d] = self.duration_tracker[ + d - 1 + ] # Set duration to value of the sequence step + + # Play the random Clifford + random_clifford = c1_ops[i] + for op in random_clifford: + b.play(op, self.qubit) + self.duration_tracker[ + d + ] += 1 # Add additional duration for each pulse played to build Clifford + + if d == 0: # Handle the case for qubit set to original/ground state + self.state_tracker[d] = c1_table[self.state_init][i] + else: # Get the newly transformed state within th Cayley table based on previous step + self.state_tracker[d] = c1_table[self.state_tracker[d - 1]][i] + self.revert_ops[d] = find_revert_op(self.state_tracker[d]) + return b diff --git a/examples/bakery_examples/RB_1_qubit/c1_cayley_table.csv b/examples/bakery_examples/RB_1_qubit/c1_cayley_table.csv new file mode 100644 index 00000000..1bf1926d --- /dev/null +++ b/examples/bakery_examples/RB_1_qubit/c1_cayley_table.csv @@ -0,0 +1,25 @@ +,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 +0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 +1,1,0,3,2,6,7,4,5,11,10,9,8,13,12,18,19,22,23,14,15,21,20,16,17 +2,2,3,0,1,7,6,5,4,10,11,8,9,20,21,15,14,23,22,19,18,12,13,17,16 +3,3,2,1,0,5,4,7,6,9,8,11,10,21,20,19,18,17,16,15,14,13,12,23,22 +4,4,7,5,6,11,8,9,10,2,3,1,0,22,17,21,12,14,18,13,20,23,16,15,19 +5,5,6,4,7,10,9,8,11,1,0,2,3,23,16,12,21,19,15,20,13,22,17,18,14 +6,6,5,7,4,8,11,10,9,3,2,0,1,16,23,20,13,18,14,12,21,17,22,19,15 +7,7,4,6,5,9,10,11,8,0,1,3,2,17,22,13,20,15,19,21,12,16,23,14,18 +8,8,9,11,10,1,3,2,0,7,4,5,6,19,14,22,16,20,12,23,17,15,18,13,21 +9,9,8,10,11,2,0,1,3,6,5,4,7,14,19,23,17,13,21,22,16,18,15,20,12 +10,10,11,9,8,3,1,0,2,4,7,6,5,18,15,17,23,12,20,16,22,14,19,21,13 +11,11,10,8,9,0,2,3,1,5,6,7,4,15,18,16,22,21,13,17,23,19,14,12,20 +12,12,13,21,20,18,19,14,15,22,17,23,16,1,0,4,5,8,10,6,7,2,3,11,9 +13,13,12,20,21,14,15,18,19,16,23,17,22,0,1,6,7,11,9,4,5,3,2,8,10 +14,14,19,15,18,22,16,23,17,20,21,12,13,8,9,2,0,6,4,1,3,10,11,7,5 +15,15,18,14,19,17,23,16,22,12,13,20,21,10,11,0,2,5,7,3,1,8,9,4,6 +16,16,23,22,17,12,21,20,13,19,14,15,18,5,6,8,11,3,0,10,9,7,4,1,2 +17,17,22,23,16,21,12,13,20,14,19,18,15,4,7,9,10,0,3,11,8,6,5,2,1 +18,18,15,19,14,16,22,17,23,21,20,13,12,11,10,3,1,4,6,0,2,9,8,5,7 +19,19,14,18,15,23,17,22,16,13,12,21,20,9,8,1,3,7,5,2,0,11,10,6,4 +20,20,21,13,12,19,18,15,14,17,22,16,23,3,2,7,6,10,8,5,4,0,1,9,11 +21,21,20,12,13,15,14,19,18,23,16,22,17,2,3,5,4,9,11,7,6,1,0,10,8 +22,22,17,16,23,13,20,21,12,15,18,19,14,7,4,11,8,2,1,9,10,5,6,0,3 +23,23,16,17,22,20,13,12,21,18,15,14,19,6,5,10,9,1,2,8,11,4,7,3,0 diff --git a/examples/bakery_examples/Ramsey_fringes/RamseyGauss_configuration.py b/examples/bakery_examples/Ramsey_fringes/RamseyGauss_configuration.py new file mode 100644 index 00000000..b0a3e81d --- /dev/null +++ b/examples/bakery_examples/Ramsey_fringes/RamseyGauss_configuration.py @@ -0,0 +1,222 @@ +import numpy as np + + +def gauss(amplitude, mu, sigma, length): + t = np.linspace(-length / 2, length / 2, length) + gauss_wave = amplitude * np.exp(-((t - mu) ** 2) / (2 * sigma ** 2)) + return [float(x) for x in gauss_wave] + + +def IQ_imbalance_corr(g, phi): + c = np.cos(phi) + s = np.sin(phi) + N = 1 / ((1 - g ** 2) * (2 * c ** 2 - 1)) + return [float(N * x) for x in [(1 - g) * c, (1 + g) * s, (1 - g) * s, (1 + g) * c]] + + +Resonator_TOF = 332 +Readout_pulse_length = 500 +Load_pulse_length = 172 +Resonator_freq = 6.6e9 +Drive_freq = 5e9 +Tpihalf = 32 + +Resonator_IF = 50e6 +Resonator_LO = Resonator_freq - Resonator_IF + +Drive_IF = 31.25e6 +Drive_LO = Drive_freq - Drive_IF + +Readout_Amp = 0.1 # meas pulse amplitude +Loadpulse_Amp = 0.3 # prepulse to fast load cavity /!\ <0.5 V + +Drive_gauss_pulse_length = Tpihalf +Drive_Amp = 0.1 +gauss_drive_amp = 0.1 +gauss_drive_mu = 0 +gauss_drive_sigma = Drive_gauss_pulse_length / 6 + +Resonator_I0 = 0.0 +Resonator_Q0 = 0.0 +Resonator_g = 0.0 +Resonator_phi = 0.0 + +Drive_I0 = 0.0 +Drive_Q0 = 0.0 +Drive_g = 0.0 +Drive_phi = 0.0 + +Resonator_correction_matrix = IQ_imbalance_corr(Resonator_g, Resonator_phi) +Drive_correction_matrix = IQ_imbalance_corr(Drive_g, Drive_phi) + +Input1_offset = 0.0 +Input2_offset = 0.0 + +config = { + "version": 1, + "controllers": { + "con1": { + "type": "opx1", + "analog_outputs": { + 1: {"offset": Resonator_I0}, # Resonator I + 2: {"offset": Resonator_Q0}, # Resonator Q + 3: {"offset": Drive_I0}, # Drive I + 4: {"offset": Drive_Q0}, # Drive Q + 5: { + "offset": 0 + }, # Drive LO amplitude modulation ---------------> SHOULD BE A DIGITAL OUTPUT + }, + "digital_outputs": { + 1: {}, # Resonator digital marker + 2: {}, # Drive digital marker + 3: {}, # Drive LO synthesizer trigger + 4: {}, # Drive LO microwave source trigger -------------------------- + }, + "analog_inputs": { + 1: {"offset": Input1_offset}, # I readout from resonator demod + 2: {"offset": Input2_offset}, # Q readout from resonator demod + }, + }, + }, + "elements": { + "Resonator": { # Resonator element + "mixInputs": { + "I": ("con1", 1), + "Q": ("con1", 2), + "lo_frequency": Resonator_LO, + "mixer": "Resonator_mixer", + }, + "intermediate_frequency": Resonator_IF, + "operations": { + "readout": "readout_pulse", # play('readout', 'Resonator'), + "chargecav": "load_pulse", + }, + "digitalInputs": { + "switchR": { + "port": ("con1", 1), + "delay": 144, + "buffer": 0, + } + }, + "outputs": { + "out1": ("con1", 1), + "out2": ("con1", 2), + }, + "time_of_flight": Resonator_TOF, + "smearing": 0, + }, + "Drive": { # Drive element + "mixInputs": { + "I": ("con1", 4), + "Q": ("con1", 3), + "lo_frequency": Drive_LO, + "mixer": "Drive_mixer", + }, + "digitalInputs": { + "switchE": { + "port": ("con1", 3), + "delay": 144, + "buffer": 0, + } + }, + "intermediate_frequency": Drive_IF, + "operations": { + "gauss_drive": "gauss_drive_pulse", + }, + }, + }, + "pulses": { + "readout_pulse": { + "operation": "measurement", + "length": Readout_pulse_length, + "waveforms": { + "I": "readout_wf", + "Q": "zero_wf", + }, + "integration_weights": { + "integW_cos": "integW_cosine", + "integW_sin": "integW_sine", + }, + "digital_marker": "ON", # Put ON instead of Modulate if you want raw adc time traces + }, + "load_pulse": { + "operation": "control", + "length": Load_pulse_length, + "waveforms": { + "I": "loadpulse_wf", + "Q": "zero_wf", + }, + "digital_marker": "ON", + }, + "gauss_drive_pulse": { + "operation": "control", + "length": Drive_gauss_pulse_length, + "waveforms": { + "I": "gauss_drive_wf", + "Q": "zero_wf", + }, + "digital_marker": "ON", + }, + }, + "waveforms": { + "readout_wf": { + "type": "constant", + "sample": Readout_Amp, + }, + "loadpulse_wf": { + "type": "constant", + "sample": Loadpulse_Amp, + }, + "zero_wf": { + "type": "constant", + "sample": 0.0, + }, + "drive_wf": { + "type": "constant", + "sample": Drive_Amp, + }, + "gauss_drive_wf": { + "type": "arbitrary", + "samples": gauss( + gauss_drive_amp, + gauss_drive_mu, + gauss_drive_sigma, + Drive_gauss_pulse_length, + ), + }, + }, + "digital_waveforms": { + "ON": {"samples": [(1, 0)]}, + "OFF": {"samples": [(0, 0)]}, # [(value, length)] + "Modulate": { + "samples": [(1, Readout_pulse_length / 20), (0, Readout_pulse_length / 20)] + * 10 # [(value, length)] + }, + }, + "integration_weights": { + "integW_cosine": { + "cosine": [1.0] * int(Readout_pulse_length / 4), + "sine": [0.0] * int(Readout_pulse_length / 4), + }, + "integW_sine": { + "cosine": [0.0] * int(Readout_pulse_length / 4), + "sine": [1.0] * int(Readout_pulse_length / 4), + }, + }, + "mixers": { + "Resonator_mixer": [ + { + "intermediate_frequency": Resonator_IF, + "lo_frequency": Resonator_LO, + "correction": Resonator_correction_matrix, + } + ], + "Drive_mixer": [ + { + "intermediate_frequency": Drive_IF, + "lo_frequency": Drive_LO, + "correction": Drive_correction_matrix, + } + ], + }, +} diff --git a/examples/bakery_examples/Ramsey_fringes/Ramsey_Gauss_baking.py b/examples/bakery_examples/Ramsey_fringes/Ramsey_Gauss_baking.py new file mode 100644 index 00000000..2d81b494 --- /dev/null +++ b/examples/bakery_examples/Ramsey_fringes/Ramsey_Gauss_baking.py @@ -0,0 +1,36 @@ +from qm.QuantumMachinesManager import QuantumMachinesManager +from qm.qua import * +from qm import SimulationConfig +from qualang_tools.bakery import * + +from RamseyGauss_configuration import * + +from time import sleep +from matplotlib import pyplot as plt + +dephasingStep = 0 +number_of_pulses = 32 + +baking_list = [] # Stores the baking objects +for i in range(number_of_pulses): # Create 16 different baked sequences + with baking(config, padding_method="left") as b: + init_delay = number_of_pulses # Put initial delay to ensure that all of the pulses will have the same length + + b.frame_rotation(dephasingStep, "Drive") + b.wait( + init_delay, "Drive" + ) # This is to compensate for the extra delay the Resonator is experiencing. + + # Play uploads the sample in the original config file (here we use an existing pulse in the config) + b.play("gauss_drive", "Drive", amp=1) # duration Tpihalf+16 + b.play_at("gauss_drive", "Drive", init_delay - i) # duration Tpihalf + + # Append the baking object in the list to call it from the QUA program + baking_list.append(b) +# You can retrieve and see the pulse you built for each baking object by modifying +# index of the waveform +plt.figure() +for i in range(number_of_pulses): + baked_pulse = config["waveforms"][f"Drive_baked_wf_I_{i}"]["samples"] + t = np.arange(0, len(baked_pulse), 1) + plt.plot(t, baked_pulse) diff --git a/examples/bakery_examples/XEB/xeb_config.py b/examples/bakery_examples/XEB/xeb_config.py new file mode 100644 index 00000000..2957a892 --- /dev/null +++ b/examples/bakery_examples/XEB/xeb_config.py @@ -0,0 +1,188 @@ +import numpy as np + +pulse_len = 80 +readout_len = 400 +qubit_IF = 50e6 +rr_IF = 50e6 +qubit_LO = 6.345e9 +rr_LO = 4.755e9 +coupler_IF = 50e6 +coupler_LO = 6e9 + + +def gauss(amplitude, mu, sigma, length): + t = np.linspace(-length / 2, length / 2, length) + gauss_wave = amplitude * np.exp(-((t - mu) ** 2) / (2 * sigma ** 2)) + return [float(x) for x in gauss_wave] + + +def IQ_imbalance(g, phi): + c = np.cos(phi) + s = np.sin(phi) + N = 1 / ((1 - g ** 2) * (2 * c ** 2 - 1)) + return [float(N * x) for x in [(1 - g) * c, (1 + g) * s, (1 - g) * s, (1 + g) * c]] + + +gauss_pulse = gauss(0.2, 0, 20, pulse_len) + +config = { + "version": 1, + "controllers": { + "con1": { + "type": "opx1", + "analog_outputs": { + 1: {"offset": +0.0}, # q1-I + 2: {"offset": +0.0}, # q1-Q + 3: {"offset": +0.0}, # q2-I + 4: {"offset": +0.0}, # q2-Q + 5: {"offset": +0.0}, # coupler-I + 6: {"offset": +0.0}, # coupler-Q + 7: {"offset": +0.0}, # rr-I + 8: {"offset": +0.0}, # rr-Q + }, + "digital_outputs": { + 1: {}, + }, + "analog_inputs": { + 1: {"offset": +0.0}, + }, + } + }, + "elements": { + "q1": { + "mixInputs": { + "I": ("con1", 1), + "Q": ("con1", 2), + "lo_frequency": qubit_LO, + "mixer": "mixer_qubit", + }, + "intermediate_frequency": qubit_IF, + "operations": { + "sx": "sxPulse", + "sy": "syPulse", + "sw": "swPulse", + }, + }, + "q2": { + "mixInputs": { + "I": ("con1", 3), + "Q": ("con1", 4), + "lo_frequency": qubit_LO, + "mixer": "mixer_qubit", + }, + "intermediate_frequency": qubit_IF, + "operations": { + "sx": "sxPulse", + "sy": "syPulse", + "sw": "swPulse", + }, + }, + "coupler": { + "mixInputs": { + "I": ("con1", 5), + "Q": ("con1", 6), + }, + "intermediate_frequency": 0, + "operations": { + "coupler_op": "couplerPulse", + }, + }, + "rr": { + "mixInputs": { + "I": ("con1", 7), + "Q": ("con1", 8), + "lo_frequency": rr_LO, + "mixer": "mixer_RR", + }, + "intermediate_frequency": rr_IF, + "operations": { + "readout": "readout_pulse", + }, + "outputs": {"out1": ("con1", 1)}, + "time_of_flight": 28, + "smearing": 0, + }, + }, + "pulses": { + "constPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "gauss_wf", "Q": "gauss_wf"}, + }, + "sxPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "zero_wf", "Q": "zero_wf"}, + }, + "syPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "pi_wf", "Q": "zero_wf"}, + }, + "swPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "pi/2_wf", "Q": "zero_wf"}, + }, + "couplerPulse": { + "operation": "control", + "length": pulse_len, + "waveforms": {"I": "-pi/2_wf", "Q": "zero_wf"}, + }, + "readout_pulse": { + "operation": "measurement", + "length": readout_len, + "waveforms": {"I": "readout_wf", "Q": "zero_wf"}, + "integration_weights": { + "integW1": "integW1", + "integW2": "integW2", + }, + "digital_marker": "ON", + }, + }, + "waveforms": { + "const_wf": {"type": "constant", "sample": 0.2}, + "gauss_wf": {"type": "arbitrary", "samples": gauss_pulse}, + "pi_wf": {"type": "arbitrary", "samples": gauss(0.2, 0, 12, pulse_len)}, + "-pi/2_wf": {"type": "arbitrary", "samples": gauss(-0.1, 0, 12, pulse_len)}, + "pi/2_wf": {"type": "arbitrary", "samples": gauss(0.1, 0, 12, pulse_len)}, + "zero_wf": {"type": "constant", "sample": 0}, + "readout_wf": {"type": "constant", "sample": 0.3}, + }, + "digital_waveforms": { + "ON": {"samples": [(1, 0)]}, + }, + "integration_weights": { + "integW1": { + "cosine": [1.0] * int(readout_len / 4), + "sine": [0.0] * int(readout_len / 4), + }, + "integW2": { + "cosine": [0.0] * int(readout_len / 4), + "sine": [1.0] * int(readout_len / 4), + }, + }, + "mixers": { + "mixer_qubit": [ + { + "intermediate_frequency": qubit_IF, + "lo_frequency": qubit_LO, + "correction": IQ_imbalance(0.0, 0.0), + } + ], + "mixer_RR": [ + { + "intermediate_frequency": rr_IF, + "lo_frequency": rr_LO, + "correction": IQ_imbalance(0.0, 0.0), + } + ], + "mixer_coupler": [ + { + "intermediate_frequency": coupler_IF, + "lo_frequency": coupler_LO, + "correction": IQ_imbalance(0.0, 0.0), + } + ], + }, +} diff --git a/examples/bakery_examples/XEB/xeb_example.py b/examples/bakery_examples/XEB/xeb_example.py new file mode 100644 index 00000000..d29b371f --- /dev/null +++ b/examples/bakery_examples/XEB/xeb_example.py @@ -0,0 +1,35 @@ +from xeb_utils import XEB +from xeb_config import config +from qm import SimulationConfig +from qm.QmJob import QmJob +from qm.qua import * +from qm.QuantumMachinesManager import QuantumMachinesManager + +m_max = 100 +xeb = XEB(config, m_max, ["q1", "q2", "coupler"]) +xeb_sequence = xeb.baked_sequence +xeb_op_list = xeb.operations_list +xeb_duration_tracker = xeb.duration_tracker + +with program() as xeb_program: + update_frequency("q1", 0) + update_frequency("q2", 0) + align("q1", "q2") + truncate = declare(int) + truncate_array = declare(int, value=[x // 4 for x in xeb_duration_tracker]) + + I1 = declare(fixed) + I2 = declare(fixed) + with for_each_(truncate, truncate_array): + align(*xeb_sequence.elements) + play(xeb_sequence.operations["q1"], "q1", truncate=truncate) + play(xeb_sequence.operations["q2"], "q2", truncate=truncate) + play(xeb_sequence.operations["coupler"], "coupler", truncate=truncate) + align(*xeb_sequence.elements) + measure("readout", "rr", None, demod.full("integW1", I1, "out1")) + save(I1, "I1") + measure("readout", "rr", None, demod.full("integW1", I2, "out1")) + save(I2, "I2") + +qmm = QuantumMachinesManager() +job: QmJob = qmm.simulate(config, xeb_program, SimulationConfig(1500)) diff --git a/examples/bakery_examples/XEB/xeb_utils.py b/examples/bakery_examples/XEB/xeb_utils.py new file mode 100644 index 00000000..4f4db2db --- /dev/null +++ b/examples/bakery_examples/XEB/xeb_utils.py @@ -0,0 +1,43 @@ +from bakery.bakery import * + + +rnd_gate_map = {0: "sx", 1: "sy", 2: "sw"} + + +class XEB: + def __init__(self, config: dict, m_max: int, qe_list: List[str]): + """ + Class instance for cross-entropy benchmarking sequence generation. + :param config Configuration file + :param m_max Maximum length of XEB sequence + :param qe_list List of quantum elements used to do the sequence ([qubit1, qubit2, coupler]) + + """ + self.config = config + self.qe_list = qe_list + self.m_max = m_max + self.duration_tracker = [0] * m_max + self.operations_list = {qe: [] for qe in qe_list} + self.baked_sequence = self.generate_xeb_sequence() + + def generate_xeb_sequence(self): + rand_seq1 = np.random.randint(3, size=self.m_max) + rand_seq2 = np.random.randint(3, size=self.m_max) + q1 = self.qe_list[0] + q2 = self.qe_list[1] + coupler = self.qe_list[2] + with baking(self.config) as b: + i = 0 + for rnd1, rnd2 in zip(rand_seq1, rand_seq2): + b.align(q1, q2, coupler) + b.play(rnd_gate_map[rnd1], q1) + b.play(rnd_gate_map[rnd2], q2) + b.align(q1, q2, coupler) + b.play("coupler_op", coupler) + self.operations_list[q1].append(rnd_gate_map[rnd1]) + self.operations_list[q2].append(rnd_gate_map[rnd2]) + self.operations_list[q2].append("coupler_op") + + self.duration_tracker[i] = b.get_current_length(coupler) + i += 1 + return b diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..d47a4000 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,627 @@ +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "black" +version = "21.5b2" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.8.1,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] +python2 = ["typed-ast (>=1.4.2)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.0.1" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "cycler" +version = "0.10.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[[package]] +name = "importlib-metadata" +version = "4.5.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "kiwisolver" +version = "1.3.1" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "matplotlib" +version = "3.4.2" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +cycler = ">=0.10" +kiwisolver = ">=1.0.1" +numpy = ">=1.16" +pillow = ">=6.2.0" +pyparsing = ">=2.2.1" +python-dateutil = ">=2.7" + +[[package]] +name = "more-itertools" +version = "8.8.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[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 = "numpy" +version = "1.20.3" +description = "NumPy is the fundamental package for array computing with Python." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "packaging" +version = "20.9" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[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.8.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pillow" +version = "8.2.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +name = "poethepoet" +version = "0.10.0" +description = "A task runner that works well with poetry." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +pastel = ">=0.2.0,<0.3.0" +tomlkit = ">=0.6.0,<1.0.0" + +[[package]] +name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "pytest" +version = "5.4.3" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=17.4.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +more-itertools = ">=4.0.0" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.5.0" +wcwidth = "*" + +[package.extras] +checkqa-mypy = ["mypy (==v0.761)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "python-dateutil" +version = "2.8.1" +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 = "regex" +version = "2021.4.4" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = "*" + +[[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 = "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 = "tomlkit" +version = "0.7.2" +description = "Style preserving TOML library" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "typed-ast" +version = "1.4.3" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "3.10.0.0" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "zipp" +version = "3.4.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "ff8c10a4f4a3e3e3d7479cb19769df6a2dad32c5e9bc329daf7473e5bd26b615" + +[metadata.files] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +black = [ + {file = "black-21.5b2-py3-none-any.whl", hash = "sha256:e5cf21ebdffc7a9b29d73912b6a6a9a4df4ce70220d523c21647da2eae0751ef"}, + {file = "black-21.5b2.tar.gz", hash = "sha256:1fc0e0a2c8ae7d269dfcf0c60a89afa299664f3e811395d40b1922dff8f854b5"}, +] +click = [ + {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, + {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +cycler = [ + {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, + {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"}, + {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"}, +] +kiwisolver = [ + {file = "kiwisolver-1.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd34fbbfbc40628200730bc1febe30631347103fc8d3d4fa012c21ab9c11eca9"}, + {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d3155d828dec1d43283bd24d3d3e0d9c7c350cdfcc0bd06c0ad1209c1bbc36d0"}, + {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5a7a7dbff17e66fac9142ae2ecafb719393aaee6a3768c9de2fd425c63b53e21"}, + {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f8d6f8db88049a699817fd9178782867bf22283e3813064302ac59f61d95be05"}, + {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:5f6ccd3dd0b9739edcf407514016108e2280769c73a85b9e59aa390046dbf08b"}, + {file = "kiwisolver-1.3.1-cp36-cp36m-win32.whl", hash = "sha256:225e2e18f271e0ed8157d7f4518ffbf99b9450fca398d561eb5c4a87d0986dd9"}, + {file = "kiwisolver-1.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cf8b574c7b9aa060c62116d4181f3a1a4e821b2ec5cbfe3775809474113748d4"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:232c9e11fd7ac3a470d65cd67e4359eee155ec57e822e5220322d7b2ac84fbf0"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b38694dcdac990a743aa654037ff1188c7a9801ac3ccc548d3341014bc5ca278"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ca3820eb7f7faf7f0aa88de0e54681bddcb46e485beb844fcecbcd1c8bd01689"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fd0f1ae9d92b42854b2979024d7597685ce4ada367172ed7c09edf2cef9cb8"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:1e1bc12fb773a7b2ffdeb8380609f4f8064777877b2225dec3da711b421fda31"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-win32.whl", hash = "sha256:72c99e39d005b793fb7d3d4e660aed6b6281b502e8c1eaf8ee8346023c8e03bc"}, + {file = "kiwisolver-1.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8be8d84b7d4f2ba4ffff3665bcd0211318aa632395a1a41553250484a871d454"}, + {file = "kiwisolver-1.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31dfd2ac56edc0ff9ac295193eeaea1c0c923c0355bf948fbd99ed6018010b72"}, + {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:563c649cfdef27d081c84e72a03b48ea9408c16657500c312575ae9d9f7bc1c3"}, + {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:78751b33595f7f9511952e7e60ce858c6d64db2e062afb325985ddbd34b5c131"}, + {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a357fd4f15ee49b4a98b44ec23a34a95f1e00292a139d6015c11f55774ef10de"}, + {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:5989db3b3b34b76c09253deeaf7fbc2707616f130e166996606c284395da3f18"}, + {file = "kiwisolver-1.3.1-cp38-cp38-win32.whl", hash = "sha256:c08e95114951dc2090c4a630c2385bef681cacf12636fb0241accdc6b303fd81"}, + {file = "kiwisolver-1.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:44a62e24d9b01ba94ae7a4a6c3fb215dc4af1dde817e7498d901e229aaf50e4e"}, + {file = "kiwisolver-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:50af681a36b2a1dee1d3c169ade9fdc59207d3c31e522519181e12f1b3ba7000"}, + {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a53d27d0c2a0ebd07e395e56a1fbdf75ffedc4a05943daf472af163413ce9598"}, + {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:834ee27348c4aefc20b479335fd422a2c69db55f7d9ab61721ac8cd83eb78882"}, + {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5c3e6455341008a054cccee8c5d24481bcfe1acdbc9add30aa95798e95c65621"}, + {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:acef3d59d47dd85ecf909c359d0fd2c81ed33bdff70216d3956b463e12c38a54"}, + {file = "kiwisolver-1.3.1-cp39-cp39-win32.whl", hash = "sha256:c5518d51a0735b1e6cee1fdce66359f8d2b59c3ca85dc2b0813a8aa86818a030"}, + {file = "kiwisolver-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:b9edd0110a77fc321ab090aaa1cfcaba1d8499850a12848b81be2222eab648f6"}, + {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0cd53f403202159b44528498de18f9285b04482bab2a6fc3f5dd8dbb9352e30d"}, + {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:33449715e0101e4d34f64990352bce4095c8bf13bed1b390773fc0a7295967b3"}, + {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:401a2e9afa8588589775fe34fc22d918ae839aaaf0c0e96441c0fdbce6d8ebe6"}, + {file = "kiwisolver-1.3.1.tar.gz", hash = "sha256:950a199911a8d94683a6b10321f9345d5a3a8433ec58b217ace979e18f16e248"}, +] +matplotlib = [ + {file = "matplotlib-3.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c541ee5a3287efe066bbe358320853cf4916bc14c00c38f8f3d8d75275a405a9"}, + {file = "matplotlib-3.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3a5c18dbd2c7c366da26a4ad1462fe3e03a577b39e3b503bbcf482b9cdac093c"}, + {file = "matplotlib-3.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a9d8cb5329df13e0cdaa14b3b43f47b5e593ec637f13f14db75bb16e46178b05"}, + {file = "matplotlib-3.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:7ad19f3fb6145b9eb41c08e7cbb9f8e10b91291396bee21e9ce761bb78df63ec"}, + {file = "matplotlib-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:7a58f3d8fe8fac3be522c79d921c9b86e090a59637cb88e3bc51298d7a2c862a"}, + {file = "matplotlib-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6382bc6e2d7e481bcd977eb131c31dee96e0fb4f9177d15ec6fb976d3b9ace1a"}, + {file = "matplotlib-3.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a6a44f27aabe720ec4fd485061e8a35784c2b9ffa6363ad546316dfc9cea04e"}, + {file = "matplotlib-3.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1c1779f7ab7d8bdb7d4c605e6ffaa0614b3e80f1e3c8ccf7b9269a22dbc5986b"}, + {file = "matplotlib-3.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5826f56055b9b1c80fef82e326097e34dc4af8c7249226b7dd63095a686177d1"}, + {file = "matplotlib-3.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0bea5ec5c28d49020e5d7923c2725b837e60bc8be99d3164af410eb4b4c827da"}, + {file = "matplotlib-3.4.2-cp38-cp38-win32.whl", hash = "sha256:6475d0209024a77f869163ec3657c47fed35d9b6ed8bccba8aa0f0099fbbdaa8"}, + {file = "matplotlib-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:21b31057bbc5e75b08e70a43cefc4c0b2c2f1b1a850f4a0f7af044eb4163086c"}, + {file = "matplotlib-3.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b26535b9de85326e6958cdef720ecd10bcf74a3f4371bf9a7e5b2e659c17e153"}, + {file = "matplotlib-3.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:32fa638cc10886885d1ca3d409d4473d6a22f7ceecd11322150961a70fab66dd"}, + {file = "matplotlib-3.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:956c8849b134b4a343598305a3ca1bdd3094f01f5efc8afccdebeffe6b315247"}, + {file = "matplotlib-3.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:85f191bb03cb1a7b04b5c2cca4792bef94df06ef473bc49e2818105671766fee"}, + {file = "matplotlib-3.4.2-cp39-cp39-win32.whl", hash = "sha256:b1d5a2cedf5de05567c441b3a8c2651fbde56df08b82640e7f06c8cd91e201f6"}, + {file = "matplotlib-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:df815378a754a7edd4559f8c51fc7064f779a74013644a7f5ac7a0c31f875866"}, + {file = "matplotlib-3.4.2.tar.gz", hash = "sha256:d8d994cefdff9aaba45166eb3de4f5211adb4accac85cbf97137e98f26ea0219"}, +] +more-itertools = [ + {file = "more-itertools-8.8.0.tar.gz", hash = "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"}, + {file = "more_itertools-8.8.0-py3-none-any.whl", hash = "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d"}, +] +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"}, +] +numpy = [ + {file = "numpy-1.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:70eb5808127284c4e5c9e836208e09d685a7978b6a216db85960b1a112eeace8"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6ca2b85a5997dabc38301a22ee43c82adcb53ff660b89ee88dded6b33687e1d8"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5bf0e132acf7557fc9bb8ded8b53bbbbea8892f3c9a1738205878ca9434206a"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db250fd3e90117e0312b611574cd1b3f78bec046783195075cbd7ba9c3d73f16"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:637d827248f447e63585ca3f4a7d2dfaa882e094df6cfa177cc9cf9cd6cdf6d2"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8b7bb4b9280da3b2856cb1fc425932f46fba609819ee1c62256f61799e6a51d2"}, + {file = "numpy-1.20.3-cp37-cp37m-win32.whl", hash = "sha256:67d44acb72c31a97a3d5d33d103ab06d8ac20770e1c5ad81bdb3f0c086a56cf6"}, + {file = "numpy-1.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:43909c8bb289c382170e0282158a38cf306a8ad2ff6dfadc447e90f9961bef43"}, + {file = "numpy-1.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f1452578d0516283c87608a5a5548b0cdde15b99650efdfd85182102ef7a7c17"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6e51534e78d14b4a009a062641f465cfaba4fdcb046c3ac0b1f61dd97c861b1b"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e515c9a93aebe27166ec9593411c58494fa98e5fcc219e47260d9ab8a1cc7f9f"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1c09247ccea742525bdb5f4b5ceeacb34f95731647fe55774aa36557dbb5fa4"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66fbc6fed94a13b9801fb70b96ff30605ab0a123e775a5e7a26938b717c5d71a"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ea9cff01e75a956dbee133fa8e5b68f2f92175233de2f88de3a682dd94deda65"}, + {file = "numpy-1.20.3-cp38-cp38-win32.whl", hash = "sha256:f39a995e47cb8649673cfa0579fbdd1cdd33ea497d1728a6cb194d6252268e48"}, + {file = "numpy-1.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:1676b0a292dd3c99e49305a16d7a9f42a4ab60ec522eac0d3dd20cdf362ac010"}, + {file = "numpy-1.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:830b044f4e64a76ba71448fce6e604c0fc47a0e54d8f6467be23749ac2cbd2fb"}, + {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55b745fca0a5ab738647d0e4db099bd0a23279c32b31a783ad2ccea729e632df"}, + {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5d050e1e4bc9ddb8656d7b4f414557720ddcca23a5b88dd7cff65e847864c400"}, + {file = "numpy-1.20.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9c65473ebc342715cb2d7926ff1e202c26376c0dcaaee85a1fd4b8d8c1d3b2f"}, + {file = "numpy-1.20.3-cp39-cp39-win32.whl", hash = "sha256:16f221035e8bd19b9dc9a57159e38d2dd060b48e93e1d843c49cb370b0f415fd"}, + {file = "numpy-1.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:6690080810f77485667bfbff4f69d717c3be25e5b11bb2073e76bb3f578d99b4"}, + {file = "numpy-1.20.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e465afc3b96dbc80cf4a5273e5e2b1e3451286361b4af70ce1adb2984d392f9"}, + {file = "numpy-1.20.3.zip", hash = "sha256:e55185e51b18d788e49fe8305fd73ef4470596b33fc2c1ceb304566b99c71a69"}, +] +packaging = [ + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, +] +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.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, +] +pillow = [ + {file = "Pillow-8.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9"}, + {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b"}, + {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b"}, + {file = "Pillow-8.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9"}, + {file = "Pillow-8.2.0-cp36-cp36m-win32.whl", hash = "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727"}, + {file = "Pillow-8.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f"}, + {file = "Pillow-8.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d"}, + {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a"}, + {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9"}, + {file = "Pillow-8.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388"}, + {file = "Pillow-8.2.0-cp37-cp37m-win32.whl", hash = "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5"}, + {file = "Pillow-8.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2"}, + {file = "Pillow-8.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4"}, + {file = "Pillow-8.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812"}, + {file = "Pillow-8.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178"}, + {file = "Pillow-8.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb"}, + {file = "Pillow-8.2.0-cp38-cp38-win32.whl", hash = "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232"}, + {file = "Pillow-8.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797"}, + {file = "Pillow-8.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5"}, + {file = "Pillow-8.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484"}, + {file = "Pillow-8.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602"}, + {file = "Pillow-8.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"}, + {file = "Pillow-8.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef"}, + {file = "Pillow-8.2.0-cp39-cp39-win32.whl", hash = "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713"}, + {file = "Pillow-8.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c"}, + {file = "Pillow-8.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9"}, + {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9"}, + {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8b56553c0345ad6dcb2e9b433ae47d67f95fc23fe28a0bde15a120f25257e291"}, + {file = "Pillow-8.2.0.tar.gz", hash = "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +poethepoet = [ + {file = "poethepoet-0.10.0-py3-none-any.whl", hash = "sha256:6fb3021603d4421c6fcc40072bbcf150a6c52ef70ff4d3be089b8b04e015ef5a"}, + {file = "poethepoet-0.10.0.tar.gz", hash = "sha256:70b97cb194b978dc464c70793e85e6f746cddf82b84a38bfb135946ad71ae19c"}, +] +py = [ + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, + {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +regex = [ + {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, + {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, + {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, + {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, + {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, + {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, + {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, + {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, + {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, + {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, + {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, + {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, + {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomlkit = [ + {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, + {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, +] +typed-ast = [ + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, +] +typing-extensions = [ + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +zipp = [ + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f02bb847 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,24 @@ +[tool.poetry] +name = "qualang-tools" +version = "0.1.0" +description = "" +authors = ["gal "] + +[tool.poetry.dependencies] +python = "^3.7" +matplotlib = "^3.4.2" +numpy = "^1.20.3" + +[tool.poetry.dev-dependencies] +pytest = "^5.2" +black = "^21.5b2" +poethepoet = "^0.10.0" + +[tool.poe.tasks] +format = "black ." +check_format = "black --check ." +test = "pytest" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/qualang_tools/__init__.py b/qualang_tools/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qualang_tools/bakery/__init__.py b/qualang_tools/bakery/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qualang_tools/bakery/bakery.py b/qualang_tools/bakery/bakery.py new file mode 100644 index 00000000..7f4c6e46 --- /dev/null +++ b/qualang_tools/bakery/bakery.py @@ -0,0 +1,671 @@ +"""bakery.py: Framework to generate arbitrary waveforms to be played into QUA program +Author: Arthur Strauss - Quantum Machines +Created: 23/02/2021 +""" + +from typing import Set, List, Union +import numpy as np +from qm import qua +import copy + + +def baking(config, padding_method="right"): + return Baking(config, padding_method) + + +class Baking: + def __init__(self, config, padding_method="right"): + self._config = config + self._padding_method = padding_method + self._local_config = copy.deepcopy(config) + self._samples_dict, self._qe_dict = self._init_dict() + self._ctr = self._get_baking_index() # unique name counter + self._qe_set = set() + + def __enter__(self): + return self + + @property + def elements(self): + """ + Return the set of quantum elements involved in the baking + """ + return self._qe_set + + @property + def operations(self): + """ + Access operations defined by the baking environment + """ + return BakingOperations(self) + + @property + def config(self): + return self._config + + def _get_baking_index(self): + index = 0 + max_index = 0 + for qe in self._config["elements"].keys(): + for op in self._config["elements"][qe]["operations"]: + if op.find("baked") != -1: + index += 1 + if max_index < index: + max_index = index + index = 0 + return max_index + + def _init_dict(self): + sample_dict = {} + qe_dict = {} + for qe in self._config["elements"].keys(): + if "mixInputs" in self._local_config["elements"][qe]: + sample_dict[qe] = {"I": [], "Q": []} + qe_dict[qe] = { + "time": 0, + "phase": 0, + "time_track": 0, + "phase_track": [0], + } + elif "singleInput" in self._local_config["elements"][qe]: + sample_dict[qe] = [] + qe_dict[qe] = {"time": 0, "time_track": 0} + return sample_dict, qe_dict + + def __exit__(self, exc_type, exc_value, exc_traceback): + """ + Updates the configuration dictionary upon exit + """ + if exc_type: + return + + elements = self._local_config["elements"] + for qe in elements: + wait_duration = 0 # Stores the duration that has to be padded with 0s to make a valid sample for QUA + # in original config file + + if ( + self._qe_dict[qe]["time"] > 0 + ): # Check if a sample was added to the quantum element + # otherwise we do not add any Op + self._qe_set.add(qe) + if ( + self._qe_dict[qe]["time"] < 16 + ): # Sample length must be at least 16 ns long + wait_duration += 16 - self._qe_dict[qe]["time"] + self.wait(16 - self._qe_dict[qe]["time"], qe) + if not ( + self._qe_dict[qe]["time"] % 4 == 0 + ): # Sample length must be a multiple of 4 + wait_duration += 4 - self._qe_dict[qe]["time"] % 4 + self.wait(4 - self._qe_dict[qe]["time"] % 4, qe) + + qe_samples = self._samples_dict[qe] + end_samples = 0 + if "mixInputs" in elements[qe]: + end_samples = len(qe_samples["I"]) - wait_duration + elif "singleInput" in elements[qe]: + end_samples = len(qe_samples) - wait_duration + + # Padding done according to desired method, can be either right, left, symmetric left or symmetric right + + if self._padding_method == "left": + if "mixInputs" in elements[qe]: + qe_samples["I"] = ( + qe_samples["I"][end_samples:] + + qe_samples["I"][0:end_samples] + ) + qe_samples["Q"] = ( + qe_samples["Q"][end_samples:] + + qe_samples["Q"][0:end_samples] + ) + elif "singleInput" in elements[qe]: + qe_samples = ( + qe_samples[end_samples:] + qe_samples[0:end_samples] + ) + + elif (self._padding_method == "symmetric_l") or ( + wait_duration % 2 == 0 + ): + if "mixInputs" in elements[qe]: + qe_samples["I"] = ( + qe_samples["I"][end_samples + wait_duration // 2 :] + + qe_samples["I"][0 : end_samples + wait_duration // 2] + ) + qe_samples["Q"] = ( + qe_samples["Q"][end_samples + wait_duration // 2 :] + + qe_samples["Q"][0 : end_samples + wait_duration // 2] + ) + elif "singleInput" in elements[qe]: + qe_samples = ( + qe_samples[end_samples + wait_duration // 2 :] + + qe_samples[0 : end_samples + wait_duration // 2] + ) + + elif self._padding_method == "symmetric_r": + if "mixInputs" in elements[qe]: + qe_samples["I"] = ( + qe_samples["I"][end_samples + wait_duration // 2 + 1 :] + + qe_samples["I"][0 : end_samples + wait_duration // 2 + 1] + ) + qe_samples["Q"] = ( + qe_samples["Q"][end_samples + wait_duration // 2 + 1 :] + + qe_samples["Q"][0 : end_samples + wait_duration // 2 + 1] + ) + elif "singleInput" in elements[qe]: + qe_samples = ( + qe_samples[end_samples + wait_duration // 2 + 1 :] + + qe_samples[0 : end_samples + wait_duration // 2 + 1] + ) + + # Generates new Op, pulse, and waveform for each qe to be added in the original config file + + self._config["elements"][qe]["operations"][ + f"baked_Op_{self._ctr}" + ] = f"{qe}_baked_pulse_{self._ctr}" + if "I" in qe_samples: + self._config["pulses"][f"{qe}_baked_pulse_{self._ctr}"] = { + "operation": "control", + "length": len(qe_samples["I"]), + "waveforms": { + "I": f"{qe}_baked_wf_I_{self._ctr}", + "Q": f"{qe}_baked_wf_Q_{self._ctr}", + }, + } + self._config["waveforms"][f"{qe}_baked_wf_I_{self._ctr}"] = { + "type": "arbitrary", + "samples": qe_samples["I"], + } + self._config["waveforms"][f"{qe}_baked_wf_Q_{self._ctr}"] = { + "type": "arbitrary", + "samples": qe_samples["Q"], + } + + elif type(qe_samples) == list: + self._config["pulses"][f"{qe}_baked_pulse_{self._ctr}"] = { + "operation": "control", + "length": len(qe_samples), + "waveforms": {"single": f"{qe}_baked_wf_{self._ctr}"}, + } + self._config["waveforms"][f"{qe}_baked_wf_{self._ctr}"] = { + "type": "arbitrary", + "samples": qe_samples, + } + + def _get_samples(self, pulse: str) -> Union[List[float], List[List]]: + """ + Returns samples associated with a pulse + :param pulse: + :returns: Python list containing samples, [samples_I, samples_Q] in case of mixInputs + """ + try: + if "single" in self._local_config["pulses"][pulse]["waveforms"]: + wf = self._local_config["pulses"][pulse]["waveforms"]["single"] + if self._local_config["waveforms"][wf]["type"] == "constant": + return [ + self._local_config["waveforms"][wf]["sample"] + ] * self._local_config["pulses"][pulse]["length"] + else: + return list(self._local_config["waveforms"][wf]["samples"]) + elif "I" in self._local_config["pulses"][pulse]["waveforms"]: + wf_I = self._local_config["pulses"][pulse]["waveforms"]["I"] + wf_Q = self._local_config["pulses"][pulse]["waveforms"]["Q"] + if self._local_config["waveforms"][wf_I]["type"] == "constant": + samples_I = [ + self._local_config["waveforms"][wf_I]["sample"] + ] * self._local_config["pulses"][pulse]["length"] + else: + samples_I = list(self._local_config["waveforms"][wf_I]["samples"]) + if self._local_config["waveforms"][wf_Q]["type"] == "constant": + samples_Q = [ + self._local_config["waveforms"][wf_Q]["sample"] + ] * self._local_config["pulses"][pulse]["length"] + else: + samples_Q = list(self._local_config["waveforms"][wf_Q]["samples"]) + return [samples_I, samples_Q] + + except KeyError: + raise KeyError(f"No waveforms found for pulse {pulse}") + + def get_current_length(self, qe: str): + """ + Retrieve within the baking the current length of the waveform being created (within the baking) + + :param qe quantum element + """ + if "mixInputs" in self._local_config["elements"][qe]: + return len(self._samples_dict[qe]["I"]) + elif "singleInput" in self._local_config["elements"][qe]: + return len(self._samples_dict[qe]) + else: + raise KeyError("quantum element not in the config") + + def _get_pulse_index(self, qe): + index = 0 + for pulse in self._local_config["pulses"]: + if pulse.find(f"{qe}_baked_pulse_b{self._ctr}") != -1: + index += 1 + return index + + def get_qe_set(self): + return self._qe_set + + def get_Op_name(self, qe: str): + """ + Get the baked operation issued from the baking object for quantum element qe + :param qe: quantum element for which the baked operation is intended to be played on + """ + if not (qe in self._qe_set): + raise KeyError( + f"{qe} is not in the set of quantum elements of the baking object " + ) + else: + return f"baked_Op_{self._ctr}" + + def get_Op_length(self, qe: str): + """ + Retrieve the length of the finalized baked waveform associated to quantum element qe (outside the baking) + :param qe: quantum element + """ + if not (qe in self._qe_set): + raise KeyError( + f"{qe} is not in the set of quantum elements of the baking object " + ) + else: + if "mixInputs" in self._config["elements"][qe]: + return len( + self._config["waveforms"][f"{qe}_baked_wf_I_{self._ctr}"]["samples"] + ) + else: + return len( + self._config["waveforms"][f"{qe}_baked_wf_{self._ctr}"]["samples"] + ) + + def add_Op(self, name: str, qe: str, samples: list, digital_marker: str = None): + """ + Adds in the configuration file a pulse element. + :param name: name of the Operation to be added for the quantum element + :param qe: targeted quantum element + :param samples: arbitrary waveform to be inserted into pulse definition + :param digital_marker: name of the digital marker sample associated to the generated pulse (assumed to be in the original config) + """ + + index = self._get_pulse_index(qe) + Op = {name: f"{qe}_baked_pulse_b{self._ctr}_{index}"} + if "mixInputs" in self._local_config["elements"][qe]: + pulse = { + f"{qe}_baked_pulse_b{self._ctr}_{index}": { + "operation": "control", + "length": len(samples), + "waveforms": { + "I": f"{qe}_baked_b{self._ctr}_{index}_wf_I", + "Q": f"{qe}_baked_b{self._ctr}_{index}_wf_Q", + }, + } + } + if digital_marker is not None: + pulse[f"{qe}_baked_pulse_b{self._ctr}_{index}"][ + "digital_marker" + ] = digital_marker + + waveform = { + f"{qe}_baked_b{self._ctr}_{index}_wf_I": { + "type": "arbitrary", + "samples": samples[0], + }, + f"{qe}_baked_b{self._ctr}_{index}_wf_Q": { + "type": "arbitrary", + "samples": samples[1], + }, + } + + elif "singleInput" in self._local_config["elements"][qe]: + pulse = { + f"{qe}_baked_pulse_b{self._ctr}_{index}": { + "operation": "control", + "length": len(samples), + "waveforms": {"single": f"{qe}_baked_b{self._ctr}_{index}_wf"}, + } + } + + if digital_marker is not None: + pulse[f"{qe}_baked_pulse_b{self._ctr}_{index}"][ + "digital_marker" + ] = digital_marker + waveform = { + f"{qe}_baked_b{self._ctr}_{index}_wf": { + "type": "arbitrary", + "samples": samples, + } + } + + self._local_config["pulses"].update(pulse) + self._local_config["waveforms"].update(waveform) + self._local_config["elements"][qe]["operations"].update(Op) + + def play(self, Op: str, qe: str, amp: float = 1.0) -> None: + """ + Add a pulse to the baked sequence + :param Op: operation to play to quantum element + :param qe: targeted quantum element + :param amp: amplitude of the pulse (replaces amp(a)*'pulse' in QUA) + :return: + """ + try: + if self._qe_dict[qe]["time_track"] == 0: + pulse = self._local_config["elements"][qe]["operations"][Op] + samples = self._get_samples(pulse) + + if "mixInputs" in self._local_config["elements"][qe]: + if (type(samples[0]) != list) or (type(samples[1]) != list): + raise TypeError( + f"Error : samples given do not correspond to mixInputs for element {qe} " + ) + + elif len(samples[0]) != len(samples[1]): + raise IndexError( + "Error : samples provided for I and Q do not have the same length" + ) + + I = samples[0] + Q = samples[1] + I2 = [None] * len(I) + Q2 = [None] * len(Q) + phi = self._qe_dict[qe]["phase"] + + for i in range(len(I)): + I2[i] = np.cos(phi) * I[i] - np.sin(phi) * Q[i] + Q2[i] = np.sin(phi) * I[i] + np.cos(phi) * Q[i] + self._samples_dict[qe]["I"].append(amp * I2[i]) + self._samples_dict[qe]["Q"].append(amp * Q2[i]) + self._qe_dict[qe]["phase_track"].append(phi) + + self._update_qe_time(qe, len(I)) + + elif "singleInput" in self._local_config["elements"][qe]: + if type(samples[0]) == list: + raise TypeError( + f"Error : samples given do not correspond to singleInput for element {qe} " + ) + for sample in samples: + self._samples_dict[qe].append(amp * sample) + self._update_qe_time(qe, len(samples)) + else: + self.play_at(Op, qe, self._qe_dict[qe]["time_track"], amp) + self._qe_dict[qe]["time_track"] = 0 + + except KeyError: + raise KeyError( + f'Op:"{Op}" does not exist in configuration and not manually added (use add_pulse)' + ) + + def play_at(self, Op: str, qe: str, t: int, amp: float = 1.0) -> None: + """ + Add a waveform to the sequence at the specified time index. + If indicated time is higher than the pulse duration for the specified quantum element, + a wait command followed by the given waveform at indicated time (in ns) occurs. + Otherwise, waveform is added (addition of samples) to the pre-existing sequence. + Finally, providing a negative index starts adding the sample with a prior negative wait of t + Note that the phase played for the newly formed sample is the one that was set before adding the new waveform + :param Op: operation to play to quantum element + :param qe: targeted quantum element + :param t: Time tag in ns where the pulse should be added + :param amp: amplitude of the pulse (replaces amp(a)*'pulse' in QUA) + :return: + """ + if type(t) != int: + if type(t) == float: + t = int(t) + else: + raise TypeError("Provided time is not an integer") + elif t < 0: + self.wait(t, qe) # Negative wait + self.play(Op, qe, amp) + elif t > self._qe_dict[qe]["time"]: + self.wait(t - self._qe_dict[qe]["time"], qe) + self.play(Op, qe, amp) + else: + try: + pulse = self._local_config["elements"][qe]["operations"][Op] + samples = self._get_samples(pulse) + new_samples = 0 + if "mixInputs" in self._local_config["elements"][qe]: + if (type(samples[0]) != list) or (type(samples[1]) != list): + raise TypeError( + f"Error : samples given do not correspond to mixInputs for element {qe}" + ) + elif len(samples[0]) != len(samples[1]): + raise IndexError( + "Error : samples provided for I and Q do not have the same length" + ) + I = samples[0] + Q = samples[1] + I2 = [None] * len(I) + Q2 = [None] * len(Q) + phi = self._qe_dict[qe]["phase_track"] + + for i in range(len(I)): + if t + i < len(self._samples_dict[qe]["I"]): + I2[i] = ( + np.cos(phi[t + i]) * I[i] - np.sin(phi[t + i]) * Q[i] + ) + Q2[i] = ( + np.sin(phi[t + i]) * I[i] + np.cos(phi[t + i]) * Q[i] + ) + self._samples_dict[qe]["I"][t + i] += amp * I2[i] + self._samples_dict[qe]["Q"][t + i] += amp * Q2[i] + else: + phi = self._qe_dict[qe]["phase"] + I2[i] = np.cos(phi) * I[i] - np.sin(phi) * Q[i] + Q2[i] = np.sin(phi) * I[i] + np.cos(phi) * Q[i] + self._samples_dict[qe]["I"].append(amp * I2[i]) + self._samples_dict[qe]["Q"].append(amp * Q2[i]) + self._qe_dict[qe]["phase_track"].append(phi) + new_samples += 1 + + elif "singleInput" in self._local_config["elements"][qe]: + if type(samples[0]) == list: + raise TypeError( + f"Error : samples given do not correspond to singleInput for element {qe} " + ) + for i in range(len(samples)): + if t + i < len(self._samples_dict[qe]): + self._samples_dict[qe][t + i] += amp * samples[i] + else: + self._samples_dict[qe].append(amp * samples[i]) + new_samples += 1 + + self._update_qe_time(qe, new_samples) + + except KeyError: + raise KeyError( + f'Op:"{Op}" does not exist in configuration and not manually added (use add_pulse)' + ) + + def frame_rotation(self, angle: float, qe: str): + """ + Shift the phase of the oscillator associated with a quantum element by the given angle. + This is typically used for virtual z-rotations. + :param angle: phase parameter + :param qe: quantum element + """ + if "mixInputs" in self._local_config["elements"][qe]: + self._update_qe_phase(qe, angle) + else: + raise TypeError( + f"frame rotation not available for singleInput quantum element ({qe})" + ) + + def frame_rotation_2pi(self, angle: float, qe: str): + """ + Shift the phase of the oscillator associated with a quantum element by the given angle. + This is typically used for virtual z-rotations. This performs a frame rotation of 2*π*angle + :param angle: phase parameter + :param qe: quantum element + """ + if "mixInputs" in self._local_config["elements"][qe]: + self._update_qe_phase(qe, 2 * np.pi * angle) + else: + raise TypeError( + f"frame rotation not available for singleInput quantum element ({qe})" + ) + + def reset_frame(self, *qe_set: Set[str]): + """ + Used to reset all of the frame updated made up to this statement. + :param qe_set: Set[str] of quantum elements + """ + for qe in qe_set: + if "mixInputs" in self._local_config["elements"][qe]: + self._update_qe_phase(qe, 0.0) + else: + raise TypeError( + f"reset frame not available for singleInput quantum element {qe}" + ) + + def ramp(self, amp: float, duration: int, qe: str): + """ + Analog of ramp function in QUA + :param amp: slope + :param duration: duration of ramping + :param qe: quantum element + """ + ramp_sample = [amp * t for t in range(duration)] + if "singleInput" in self._local_config["elements"][qe]: + self._samples_dict[qe] += ramp_sample + elif "mixInputs" in self._local_config["elements"][qe]: + self._samples_dict[qe]["Q"] += ramp_sample + self._samples_dict[qe]["I"] += [0] * duration + self._update_qe_time(qe, duration) + + def _update_qe_time(self, qe: str, dt: int): + self._qe_dict[qe]["time"] += dt + + def _update_qe_phase(self, qe: str, phi: float): + self._qe_dict[qe]["phase"] = phi + + def wait(self, duration: int, *qe_set: Set[str]): + """ + Wait for the given duration on all provided elements. + Here, the wait is simply adding 0 to the existing sample for a given duration. + + :param duration: waiting duration + :param qe_set: set of quantum elements + + """ + if duration >= 0: + for qe in qe_set: + if qe in self._samples_dict.keys(): + if "mixInputs" in self._local_config["elements"][qe].keys(): + self._samples_dict[qe]["I"] = ( + self._samples_dict[qe]["I"] + [0] * duration + ) + self._samples_dict[qe]["Q"] = ( + self._samples_dict[qe]["Q"] + [0] * duration + ) + self._qe_dict[qe]["phase_track"] += [ + self._qe_dict[qe]["phase"] + ] * duration + + elif "singleInput" in self._local_config["elements"][qe].keys(): + self._samples_dict[qe] = self._samples_dict[qe] + [0] * duration + + self._update_qe_time(qe, duration) + else: + for qe in qe_set: + # Duration is negative so just add for substraction + self._qe_dict[qe]["time_track"] = self._qe_dict[qe]["time"] + duration + + def align(self, *qe_set: Set[str]): + """ + Align several quantum elements together. + All of the quantum elements referenced in *elements will wait for all + the others to finish their currently running statement. + + :param qe_set : set of quantum elements to be aligned altogether + """ + last_qe = "" + last_t = 0 + for qe in qe_set: + qe_t = self._qe_dict[qe]["time"] + + if qe_t > last_t: + last_qe = qe + last_t = qe_t + + for qe in qe_set: + qe_t = self._qe_dict[qe]["time"] + if qe != last_qe: + self.wait(last_t - qe_t, qe) + + def run(self) -> None: + """ + Plays the baked waveform + This method should be used within a QUA program + :return None + """ + qe_set = self.get_qe_set() + + if len(qe_set) == 1: + for qe in qe_set: + qua.play(f"baked_Op_{self._ctr}", qe) + + else: + qua.align(*qe_set) + for qe in qe_set: + qua.play(f"baked_Op_{self._ctr}", qe) + + +def deterministic_run(baking_list): + """ + Generates a QUA macro for a binary tree ensuring a synchronized play of operations + listed in the various baking objects + :param baking_list: Python list of Baking objects + """ + depth = int(np.ceil(np.log2(len(baking_list)))) + l = 0 + h = len(baking_list) - 1 + + def QUA_deterministic_tree(j, low: int = l, high: int = h, count: int = 1): + """ + QUA macro to be used in a QUA program + :param j: QUA int indicating which element of the baking list should be accessed + :param low: index indicating start of the list + :param high: index indicating the end of the list + :param count: counts the number of iterations, which should be the same for each accessed element + """ + + mid = (high + low) // 2 + + if count == depth: + if mid + 1 <= h: + with qua.if_(j > mid): + qua.wait(4, *baking_list[mid + 1].get_qe_set()) + baking_list[mid + 1].run() + with qua.else_(): + qua.wait(4, *baking_list[mid].get_qe_set()) + baking_list[mid].run() + else: + baking_list[mid].run() + + else: + + with qua.if_(j > mid): + QUA_deterministic_tree(j, mid + 1, high, count + 1) + + with qua.else_(): + QUA_deterministic_tree(j, low, mid, count + 1) + + return QUA_deterministic_tree + + +class BakingOperations: + def __init__(self, b: Baking) -> None: + super().__init__() + self._baking = b + + def __getitem__(self, qe: str): + return self._baking.get_Op_name(qe) + + def length(self, qe): + return self._baking.get_Op_length(qe) diff --git a/qualang_tools/tests/__init__.py b/qualang_tools/tests/__init__.py new file mode 100644 index 00000000..e69de29b