{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Clifford Randomized Benchmarking" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This tutorial contains a few details on how to run [Clifford Randomized Benchmarking](http://journals.aps.org/prl/abstract/10.1103/PhysRevLett.106.180504) that are not covered in the [RB overview tutorial](RB-Overview.ipynb). \n", "\n", "\n", "## What is Clifford RB? \n", "\n", "By Clifford randomized benchmarking we mean RB of the $n$-qubit Clifford group, as defined by Magesan *et al.* in [*Scalable and Robust Benchmarking of Quantum Processes*](http://journals.aps.org/prl/abstract/10.1103/PhysRevLett.106.180504). This protocol is routinely run on 1 and 2 qubits." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function #python 2 & 3 compatibility\n", "import pygsti\n", "from pygsti.processors import QubitProcessorSpec as QPS\n", "from pygsti.processors import CliffordCompilationRules as CCR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a Clifford RB experiment design\n", "\n", "The only aspects of running Clifford RB with pyGSTi that are not covered in the [RB overview tutorial](RB-Overview.ipynb) are some subtleties in generating a Clifford RB experiment design (and what those subtleties mean for interpretting the results). To cover these subtleties, here we go through the inputs used to generate a Clifford RB experiment design in more detail.\n", "\n", "### 1. Generic RB inputs\n", "\n", "The first inputs to create an RB experiment design are the same as in all RB protocols, and these are covered in the [RB overview tutorial](RB-Overview.ipynb). They are:\n", "\n", "- The device to benchmark (`pspec`).\n", "- The \"RB depths\" at which we will sample circuits (`depths`). For Clifford RB on $n$ qubits, the RB depth is the number of (uncompiled) $n$-qubit Clifford gates in the sequence minus two. This convention is chosen so that zero is the minimum RB depth for all RB methods in pyGSTi.\n", "- The number of circuits to sample at each length (`k`).\n", "- The qubits to benchmark (`qubits`).\n", "\n", "All other arguments to Clifford RB experiment design generation function are optional." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "n_qubits = 4\n", "qubit_labels = ['Q0','Q1','Q2','Q3'] \n", "gate_names = ['Gxpi2', 'Gxmpi2', 'Gypi2', 'Gympi2', 'Gcphase'] \n", "availability = {'Gcphase':[('Q0','Q1'), ('Q1','Q2'), ('Q2','Q3'), ('Q3','Q0')]}\n", "pspec = QPS(n_qubits, gate_names, availability=availability, qubit_labels=qubit_labels)\n", "\n", "compilations = {'absolute': CCR.create_standard(pspec, 'absolute', ('paulis', '1Qcliffords'), verbosity=0), \n", " 'paulieq': CCR.create_standard(pspec, 'paulieq', ('1Qcliffords', 'allcnots'), verbosity=0)}\n", "\n", "depths = [0,1,2,4,8]\n", "k = 10\n", "qubits = ['Q0','Q1']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. The target output\n", "In the standard formulation of Clifford RB, the circuit should always return the all-zeros bit-string if there is no errors. But it can be useful to randomized the \"target\" bit-string (e.g., then the asymptote in the RB decay is fixed to $1/2^n$ even with biased measurement errors). This randomization is specified via the `randomizeout` argument, and it defaults to `False` (the standard protocol)." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "randomizeout = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. The Clifford compilation algorithm\n", "To generate a Clifford RB circuit in terms of native gates, it is necessary to decompose each $n$-qubit Clifford gate into the native gates. pyGSTi has a few different Clifford gate compilation algorithms, that can be accessed via the `compilerargs` optional argument. Note: **The Clifford RB error rate is compiler dependent!** So it is not possible to properly interpret the Clifford RB error rate without understanding at least some aspects of the compilation algorithm (e.g., the mean two-qubit gate count in a compiled $n$-qubit Clifford circuit). This is one of the reasons that [Direct RB](RB-DirectRB.ipynb) is arguably a preferable method to Clifford RB.\n", "\n", "None of the Clifford compilation algorithms in pyGSTi are a simple look-up table with some optimized property (e.g., minimized two-qubit gate count or depth). Look-up tables like this are typically used for 1- and 2-qubit Clifford RB experiments, but we instead used a method that scales to many qubits.\n", "\n", "There are multiple compilation algorithms in pyGSTi, and the algorithm can be set using the `compilerargs` argument (see the `pygsti.algorithms.compile_clifford` function for some details on the available algorithms, and the `CliffordRBDesign` docstring for how to specify the desired algorithm). The default algorthm is the one that we estimate to be our \"best\" algorithm in the regime of 1-20ish qubits. This algorithm (and some of the other algorithms) are randomized. So when creating a `CliffordRBDesign` you can also specify the number of randomization, via `citerations`. Increasing this will reduce the average depth and two-qubit gate count of each $n$-qubit Clifford gate, up to a point, making Clifford RB feasable on more qubits. \n", "But note that time to generate the circuits can increase quickly as `citerations` increases (because a depth $m$ circuit contains $(m+2)$ $n$-qubit Clifford gates to compile)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "citerations = 20" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From here, everything proceeds as in the RB overview tutorial (except for adding in the optional arguments)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# Here we construct an error model with 1% local depolarization on each qubit after each gate.\n", "def simulate_taking_data(data_template_filename):\n", " \"\"\"Simulate taking data and filling the results into a template dataset.txt file\"\"\"\n", " noisemodel = pygsti.models.create_crosstalk_free_model(pspec, depolarization_strengths={g:0.01 for g in pspec.gate_names})\n", " pygsti.io.fill_in_empty_dataset_with_fake_data(data_template_filename, noisemodel, num_samples=1000, seed=1234)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "- Sampling 10 circuits at CRB length 0 (1 of 5 depths) with seed 75116\n", "- Sampling 10 circuits at CRB length 1 (2 of 5 depths) with seed 75126\n", "- Sampling 10 circuits at CRB length 2 (3 of 5 depths) with seed 75136\n", "- Sampling 10 circuits at CRB length 4 (4 of 5 depths) with seed 75146\n", "- Sampling 10 circuits at CRB length 8 (5 of 5 depths) with seed 75156\n" ] }, { "ename": "FileNotFoundError", "evalue": "[Errno 2] No such file or directory: '../tutorial_files/test_rb_dir/data/dataset_crb.txt'", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", "File \u001b[1;32m~\\Documents\\pyGSTi_random_bugfixes\\pygsti\\io\\readers.py:98\u001b[0m, in \u001b[0;36mread_dataset\u001b[1;34m(filename, cache, collision_action, record_zero_counts, ignore_zero_count_lines, with_times, circuit_parse_cache, verbosity)\u001b[0m\n\u001b[0;32m 96\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 97\u001b[0m \u001b[38;5;66;03m# a saved Dataset object is ok\u001b[39;00m\n\u001b[1;32m---> 98\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43m_data\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mDataSet\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile_to_load_from\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfilename\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 99\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m 100\u001b[0m \n\u001b[0;32m 101\u001b[0m \u001b[38;5;66;03m#Parser functions don't take a VerbosityPrinter yet, and so\u001b[39;00m\n\u001b[0;32m 102\u001b[0m \u001b[38;5;66;03m# always output to stdout (TODO)\u001b[39;00m\n", "File \u001b[1;32m~\\Documents\\pyGSTi_random_bugfixes\\pygsti\\data\\dataset.py:1003\u001b[0m, in \u001b[0;36mDataSet.__init__\u001b[1;34m(self, oli_data, time_data, rep_data, circuits, circuit_indices, outcome_labels, outcome_label_indices, static, file_to_load_from, collision_action, comment, aux_info)\u001b[0m\n\u001b[0;32m 1000\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m(oli_data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m time_data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m rep_data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 1001\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m circuits \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m circuit_indices \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 1002\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m outcome_labels \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m outcome_label_indices \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[1;32m-> 1003\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_binary\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile_to_load_from\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1004\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n", "File \u001b[1;32m~\\Documents\\pyGSTi_random_bugfixes\\pygsti\\data\\dataset.py:2950\u001b[0m, in \u001b[0;36mDataSet.read_binary\u001b[1;34m(self, file_or_filename)\u001b[0m\n\u001b[0;32m 2949\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 2950\u001b[0m f \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mfile_or_filename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mrb\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2951\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '../tutorial_files/test_rb_dir/data/dataset_crb.txt'", "\nDuring handling of the above exception, another exception occurred:\n", "\u001b[1;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", "Cell \u001b[1;32mIn[6], line 7\u001b[0m\n\u001b[0;32m 4\u001b[0m pygsti\u001b[38;5;241m.\u001b[39mio\u001b[38;5;241m.\u001b[39mwrite_empty_protocol_data(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m../tutorial_files/test_rb_dir\u001b[39m\u001b[38;5;124m'\u001b[39m, design, clobber_ok\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m 6\u001b[0m \u001b[38;5;66;03m# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --\u001b[39;00m\n\u001b[1;32m----> 7\u001b[0m \u001b[43msimulate_taking_data\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m../tutorial_files/test_rb_dir/data/dataset_crb.txt\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# REPLACE with actual data-taking\u001b[39;00m\n\u001b[0;32m 9\u001b[0m data \u001b[38;5;241m=\u001b[39m pygsti\u001b[38;5;241m.\u001b[39mio\u001b[38;5;241m.\u001b[39mread_data_from_dir(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m../tutorial_files/test_rb_dir\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m 11\u001b[0m protocol \u001b[38;5;241m=\u001b[39m pygsti\u001b[38;5;241m.\u001b[39mprotocols\u001b[38;5;241m.\u001b[39mRB() \n", "Cell \u001b[1;32mIn[5], line 5\u001b[0m, in \u001b[0;36msimulate_taking_data\u001b[1;34m(data_template_filename)\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Simulate taking data and filling the results into a template dataset.txt file\"\"\"\u001b[39;00m\n\u001b[0;32m 4\u001b[0m noisemodel \u001b[38;5;241m=\u001b[39m pygsti\u001b[38;5;241m.\u001b[39mmodels\u001b[38;5;241m.\u001b[39mcreate_crosstalk_free_model(pspec, depolarization_strengths\u001b[38;5;241m=\u001b[39m{g:\u001b[38;5;241m0.01\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m g \u001b[38;5;129;01min\u001b[39;00m pspec\u001b[38;5;241m.\u001b[39mgate_names})\n\u001b[1;32m----> 5\u001b[0m \u001b[43mpygsti\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfill_in_empty_dataset_with_fake_data\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata_template_filename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnoisemodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_samples\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m1000\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m1234\u001b[39;49m\u001b[43m)\u001b[49m\n", "File \u001b[1;32m~\\Documents\\pyGSTi_random_bugfixes\\pygsti\\io\\writers.py:631\u001b[0m, in \u001b[0;36mfill_in_empty_dataset_with_fake_data\u001b[1;34m(dataset_filename, model, num_samples, sample_error, seed, rand_state, alias_dict, collision_action, record_zero_counts, comm, mem_limit, times, fixed_column_mode)\u001b[0m\n\u001b[0;32m 628\u001b[0m model, dataset_filename \u001b[38;5;241m=\u001b[39m dataset_filename, model\n\u001b[0;32m 630\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpygsti\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdata\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdatasetconstruction\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m simulate_data \u001b[38;5;28;01mas\u001b[39;00m _simulate_data\n\u001b[1;32m--> 631\u001b[0m ds_template \u001b[38;5;241m=\u001b[39m \u001b[43m_readers\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_dataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdataset_filename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mignore_zero_count_lines\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mwith_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mverbosity\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 632\u001b[0m ds \u001b[38;5;241m=\u001b[39m _simulate_data(model, \u001b[38;5;28mlist\u001b[39m(ds_template\u001b[38;5;241m.\u001b[39mkeys()), num_samples,\n\u001b[0;32m 633\u001b[0m sample_error, seed, rand_state, alias_dict,\n\u001b[0;32m 634\u001b[0m collision_action, record_zero_counts, comm,\n\u001b[0;32m 635\u001b[0m mem_limit, times)\n\u001b[0;32m 636\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m fixed_column_mode \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mauto\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n", "File \u001b[1;32m~\\Documents\\pyGSTi_random_bugfixes\\pygsti\\io\\readers.py:133\u001b[0m, in \u001b[0;36mread_dataset\u001b[1;34m(filename, cache, collision_action, record_zero_counts, ignore_zero_count_lines, with_times, circuit_parse_cache, verbosity)\u001b[0m\n\u001b[0;32m 130\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 131\u001b[0m \u001b[38;5;66;03m# otherwise must use standard dataset file format\u001b[39;00m\n\u001b[0;32m 132\u001b[0m parser \u001b[38;5;241m=\u001b[39m _stdinput\u001b[38;5;241m.\u001b[39mStdInputParser()\n\u001b[1;32m--> 133\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mparser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_datafile\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbToStdout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 134\u001b[0m \u001b[43m \u001b[49m\u001b[43mcollision_action\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcollision_action\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 135\u001b[0m \u001b[43m \u001b[49m\u001b[43mrecord_zero_counts\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrecord_zero_counts\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 136\u001b[0m \u001b[43m \u001b[49m\u001b[43mignore_zero_count_lines\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mignore_zero_count_lines\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 137\u001b[0m \u001b[43m \u001b[49m\u001b[43mwith_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mwith_times\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 138\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ds\n", "File \u001b[1;32m~\\Documents\\pyGSTi_random_bugfixes\\pygsti\\io\\stdinput.py:403\u001b[0m, in \u001b[0;36mStdInputParser.parse_datafile\u001b[1;34m(self, filename, show_progress, collision_action, record_zero_counts, ignore_zero_count_lines, with_times)\u001b[0m\n\u001b[0;32m 401\u001b[0m preamble_directives \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m 402\u001b[0m preamble_comments \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m--> 403\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mr\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m datafile:\n\u001b[0;32m 404\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m line \u001b[38;5;129;01min\u001b[39;00m datafile:\n\u001b[0;32m 405\u001b[0m line \u001b[38;5;241m=\u001b[39m line\u001b[38;5;241m.\u001b[39mstrip()\n", "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '../tutorial_files/test_rb_dir/data/dataset_crb.txt'" ] } ], "source": [ "design = pygsti.protocols.CliffordRBDesign(pspec, compilations, depths, k, qubit_labels=qubits, \n", " randomizeout=randomizeout, citerations=citerations)\n", "\n", "pygsti.io.write_empty_protocol_data('../tutorial_files/test_crb_dir', design, clobber_ok=True)\n", "\n", "# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --\n", "simulate_taking_data('../tutorial_files/test_crb_dir/data/dataset.txt') # REPLACE with actual data-taking\n", "\n", "data = pygsti.io.read_data_from_dir('../tutorial_files/test_crb_dir')\n", "\n", "protocol = pygsti.protocols.RB() \n", "results = protocol.run(data)\n", "ws = pygsti.report.Workspace()\n", "ws.init_notebook_mode(autodisplay=True)\n", "ws.RandomizedBenchmarkingPlot(results)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "random_pygsti_debugging", "language": "python", "name": "random_pygsti_debugging" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }