{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "papermill": {
     "duration": 0.011471,
     "end_time": "2020-09-05T07:34:57.787610",
     "exception": false,
     "start_time": "2020-09-05T07:34:57.776139",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# ImageNet Network Lesion Analysis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2020-09-05T07:34:57.819371Z",
     "iopub.status.busy": "2020-09-05T07:34:57.818462Z",
     "iopub.status.idle": "2020-09-05T07:35:02.473836Z",
     "shell.execute_reply": "2020-09-05T07:35:02.475184Z"
    },
    "papermill": {
     "duration": 4.674019,
     "end_time": "2020-09-05T07:35:02.475856",
     "exception": false,
     "start_time": "2020-09-05T07:34:57.801837",
     "status": "completed"
    },
    "pycharm": {
     "name": "#%%\n"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import sys\n",
    "sys.path.append('..')\n",
    "from src.lesion.experimentation import perform_lesion_experiment_imagenet, do_lesion_hypo_tests_imagenet\n",
    "import warnings\n",
    "import pandas as pd\n",
    "from tqdm import tqdm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2020-09-05T07:35:02.508131Z",
     "iopub.status.busy": "2020-09-05T07:35:02.507022Z",
     "iopub.status.idle": "2020-09-05T07:35:02.510571Z",
     "shell.execute_reply": "2020-09-05T07:35:02.511413Z"
    },
    "papermill": {
     "duration": 0.024955,
     "end_time": "2020-09-05T07:35:02.511685",
     "exception": false,
     "start_time": "2020-09-05T07:35:02.486730",
     "status": "completed"
    },
    "pycharm": {
     "name": "#%%\n"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# nets = ['resnet18', 'resnet34', 'vgg16', 'vgg19', 'mobilenetv2', 'resnet50', 'inceptionv3']\n",
    "# nets = ['resnet18', 'resnet34', 'vgg16', 'vgg19']\n",
    "nets = ['resnet18', 'vgg16']\n",
    "n_shuffles = 24\n",
    "num_ranks = n_shuffles + 1\n",
    "min_percentile = 1 / num_ranks\n",
    "n_clust = 12\n",
    "# os.environ['CUDA_VISIBLE_DEVICES'] = '1,2,3'\n",
    "\n",
    "all_results = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false,
    "execution": {
     "iopub.execute_input": "2020-09-05T07:35:02.544816Z",
     "iopub.status.busy": "2020-09-05T07:35:02.543695Z",
     "iopub.status.idle": "2020-09-05T07:52:15.387544Z",
     "shell.execute_reply": "2020-09-05T07:52:15.388557Z"
    },
    "papermill": {
     "duration": 1032.867332,
     "end_time": "2020-09-05T07:52:15.388943",
     "exception": true,
     "start_time": "2020-09-05T07:35:02.521611",
     "status": "failed"
    },
    "pycharm": {
     "name": "#%%\n"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /root/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/tensorflow_core/python/ops/resource_variable_ops.py:1630: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "If using Keras pass *_constraint arguments to layers.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /root/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:4070: The name tf.nn.max_pool is deprecated. Please use tf.nn.max_pool2d instead.\n",
      "\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /root/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:422: The name tf.global_variables is deprecated. Please use tf.compat.v1.global_variables instead.\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /root/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/keras/backend/tensorflow_backend.py:422: The name tf.global_variables is deprecated. Please use tf.compat.v1.global_variables instead.\n",
      "\n"
     ]
    },
    {
     "ename": "ResourceExhaustedError",
     "evalue": "2 root error(s) found.\n  (0) Resource exhausted: OOM when allocating tensor with shape[2048,114,114] and type float on /job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc\n\t [[{{node zero_padding2d_2_37/Pad}}]]\nHint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info.\n\n  (1) Resource exhausted: OOM when allocating tensor with shape[2048,114,114] and type float on /job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc\n\t [[{{node zero_padding2d_2_37/Pad}}]]\nHint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info.\n\n\t [[softmax_38/Softmax/_23831]]\nHint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info.\n\n0 successful operations.\n0 derived errors ignored.",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mResourceExhaustedError\u001B[0m                    Traceback (most recent call last)",
      "\u001B[0;32m<ipython-input-3-8fb2e629739d>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[1;32m      6\u001B[0m         _, results = perform_lesion_experiment_imagenet(net, num_clusters=n_clust,\n\u001B[1;32m      7\u001B[0m                                                         \u001B[0mnum_shuffles\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mn_shuffles\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 8\u001B[0;31m                                                         downsampled=True)\n\u001B[0m\u001B[1;32m      9\u001B[0m         \u001B[0mhypo_test_results\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mdo_lesion_hypo_tests_imagenet\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mresults\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn_shuffles\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mn_shuffles\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     10\u001B[0m         \u001B[0mchi2_p_means\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mhypo_test_results\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m'chi2_p_means'\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/project/nn_clustering/src/lesion/experimentation.py\u001B[0m in \u001B[0;36mperform_lesion_experiment_imagenet\u001B[0;34m(network, num_clusters, num_shuffles, with_random, downsampled, eigen_solver, batch_size, data_dir, val_tar, downsampled_n_samples)\u001B[0m\n\u001B[1;32m    725\u001B[0m                                                                       \u001B[0mlabels_in_layers\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mnum_clusters\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    726\u001B[0m                                                                       \u001B[0msteps\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mbatch_size\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mval_dataset_object\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 727\u001B[0;31m                                                                       preprocess, num_samples=1)\n\u001B[0m\u001B[1;32m    728\u001B[0m     \u001B[0maccs_true\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0maccs_true\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m  \u001B[0;31m# it's a 1 element list, so just take the first\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    729\u001B[0m     \u001B[0mclass_props_true\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mclass_props_true\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m  \u001B[0;31m# same as line above\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/project/nn_clustering/src/lesion/experimentation.py\u001B[0m in \u001B[0;36mlesion_test_imagenet\u001B[0;34m(model, dataset, y, labels_in_layers, num_clusters, steps, batch_size, val_dataset_object, preprocess, num_samples, min_size, max_prop, shuffle, num_classes, savepath)\u001B[0m\n\u001B[1;32m    639\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    640\u001B[0m                     cluster_pred = np.argmax(lesion_model.predict(dataset, steps=steps,\n\u001B[0;32m--> 641\u001B[0;31m                                                                   batch_size=batch_size), axis=-1)\n\u001B[0m\u001B[1;32m    642\u001B[0m                     \u001B[0;32mif\u001B[0m \u001B[0;32mnot\u001B[0m \u001B[0misinstance\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdataset\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mnp\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mndarray\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    643\u001B[0m                         \u001B[0mdataset\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mimagenet_generator\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mval_dataset_object\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mpreprocess\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py\u001B[0m in \u001B[0;36mpredict\u001B[0;34m(self, x, batch_size, verbose, steps, callbacks, max_queue_size, workers, use_multiprocessing)\u001B[0m\n\u001B[1;32m    906\u001B[0m         \u001B[0mmax_queue_size\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mmax_queue_size\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    907\u001B[0m         \u001B[0mworkers\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mworkers\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 908\u001B[0;31m         use_multiprocessing=use_multiprocessing)\n\u001B[0m\u001B[1;32m    909\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    910\u001B[0m   \u001B[0;32mdef\u001B[0m \u001B[0mreset_metrics\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_arrays.py\u001B[0m in \u001B[0;36mpredict\u001B[0;34m(self, model, x, batch_size, verbose, steps, callbacks, **kwargs)\u001B[0m\n\u001B[1;32m    721\u001B[0m         \u001B[0mverbose\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mverbose\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    722\u001B[0m         \u001B[0msteps\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0msteps\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 723\u001B[0;31m         callbacks=callbacks)\n\u001B[0m",
      "\u001B[0;32m~/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_arrays.py\u001B[0m in \u001B[0;36mmodel_iteration\u001B[0;34m(model, inputs, targets, sample_weights, batch_size, epochs, verbose, callbacks, val_inputs, val_targets, val_sample_weights, shuffle, initial_epoch, steps_per_epoch, validation_steps, validation_freq, mode, validation_in_fit, prepared_feed_values_from_dataset, steps_name, **kwargs)\u001B[0m\n\u001B[1;32m    392\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    393\u001B[0m         \u001B[0;31m# Get outputs.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 394\u001B[0;31m         \u001B[0mbatch_outs\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mf\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mins_batch\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    395\u001B[0m         \u001B[0;32mif\u001B[0m \u001B[0;32mnot\u001B[0m \u001B[0misinstance\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mbatch_outs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mlist\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    396\u001B[0m           \u001B[0mbatch_outs\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0mbatch_outs\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/tensorflow_core/python/keras/backend.py\u001B[0m in \u001B[0;36m__call__\u001B[0;34m(self, inputs)\u001B[0m\n\u001B[1;32m   3474\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   3475\u001B[0m     fetched = self._callable_fn(*array_vals,\n\u001B[0;32m-> 3476\u001B[0;31m                                 run_metadata=self.run_metadata)\n\u001B[0m\u001B[1;32m   3477\u001B[0m     \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_call_fetch_callbacks\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mfetched\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m-\u001B[0m\u001B[0mlen\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_fetches\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   3478\u001B[0m     output_structure = nest.pack_sequence_as(\n",
      "\u001B[0;32m~/.local/share/virtualenvs/nn_clustering-Lo7V74L4/lib/python3.7/site-packages/tensorflow_core/python/client/session.py\u001B[0m in \u001B[0;36m__call__\u001B[0;34m(self, *args, **kwargs)\u001B[0m\n\u001B[1;32m   1470\u001B[0m         ret = tf_session.TF_SessionRunCallable(self._session._session,\n\u001B[1;32m   1471\u001B[0m                                                \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_handle\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 1472\u001B[0;31m                                                run_metadata_ptr)\n\u001B[0m\u001B[1;32m   1473\u001B[0m         \u001B[0;32mif\u001B[0m \u001B[0mrun_metadata\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1474\u001B[0m           \u001B[0mproto_data\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mtf_session\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mTF_GetBuffer\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mrun_metadata_ptr\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;31mResourceExhaustedError\u001B[0m: 2 root error(s) found.\n  (0) Resource exhausted: OOM when allocating tensor with shape[2048,114,114] and type float on /job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc\n\t [[{{node zero_padding2d_2_37/Pad}}]]\nHint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info.\n\n  (1) Resource exhausted: OOM when allocating tensor with shape[2048,114,114] and type float on /job:localhost/replica:0/task:0/device:GPU:0 by allocator GPU_0_bfc\n\t [[{{node zero_padding2d_2_37/Pad}}]]\nHint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info.\n\n\t [[softmax_38/Softmax/_23831]]\nHint: If you want to see a list of allocated tensors when OOM happens, add report_tensor_allocations_upon_oom to RunOptions for current allocation info.\n\n0 successful operations.\n0 derived errors ignored."
     ]
    }
   ],
   "source": [
    "with warnings.catch_warnings():\n",
    "    warnings.simplefilter('ignore')\n",
    "\n",
    "    for net in tqdm(nets):\n",
    "\n",
    "        _, results = perform_lesion_experiment_imagenet(net, num_clusters=n_clust,\n",
    "                                                        num_shuffles=n_shuffles,\n",
    "                                                        downsampled=True)\n",
    "        hypo_test_results = do_lesion_hypo_tests_imagenet(results, n_shuffles=n_shuffles)\n",
    "        chi2_p_means = hypo_test_results['chi2_p_means']\n",
    "        chi2_p_ranges= hypo_test_results['chi2_p_ranges']\n",
    "        combined_p_means = hypo_test_results['combined_p_means']\n",
    "        combined_p_ranges = hypo_test_results['combined_p_ranges']\n",
    "        effect_means = hypo_test_results['effect_factor_means']\n",
    "        effect_ranges = hypo_test_results['effect_factor_range']\n",
    "        print(net)\n",
    "        print(f'chi2_p_means: {chi2_p_means}')\n",
    "        print(f'chi2_p_ranges: {chi2_p_ranges}')\n",
    "        print(f'combined_p_means: {combined_p_means}')\n",
    "        print(f'combined_p_ranges: {combined_p_ranges}')\n",
    "        print(f'effect_factor_means: {effect_means}')\n",
    "        print(f'effect_factor_range: {effect_ranges}')\n",
    "        print()\n",
    "\n",
    "        model_results = {'model_tag': net, 'chi2_p_means': chi2_p_means,\n",
    "                         'chi2_p_ranges': chi2_p_ranges, 'combined_p_means': combined_p_means,\n",
    "                         'effect_ranges': effect_ranges, 'effect_means': effect_means,\n",
    "                         'combined_p_ranges': combined_p_ranges}\n",
    "        all_results.append(pd.Series(model_results))\n",
    "\n",
    "result_df = pd.DataFrame(all_results)\n",
    "savepath = '../results/lesion_results_imagenet.csv'\n",
    "result_df.to_csv(savepath)\n",
    "result_df"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "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.7.5"
  },
  "papermill": {
   "duration": 1042.577735,
   "end_time": "2020-09-05T07:52:19.470800",
   "environment_variables": {},
   "exception": true,
   "input_path": "./notebooks/imagenet_lesion_test.ipynb",
   "output_path": "./notebooks/imagenet_lesion_test.ipynb",
   "parameters": {},
   "start_time": "2020-09-05T07:34:56.893065",
   "version": "1.2.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}