{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "papermill": {
     "duration": 0.038348,
     "end_time": "2019-11-22T16:04:58.576972",
     "exception": false,
     "start_time": "2019-11-22T16:04:58.538624",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "# MLP Double lesion Test - Stability!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "MODEL_TAG = 'FASHION+DROPOUT'\n",
    "MODE = 'STORE'\n",
    "WITH_ONE_WAY_RANDOM_DEMO = False\n",
    "MODEL_NUMBER = 1\n",
    "FILTER = 'all'\n",
    "MODEL_BASE_PATH = '../models/'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert MODE in ('STORE', 'LOAD')\n",
    "assert MODEL_NUMBER > 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.053341,
     "end_time": "2019-11-22T16:04:58.646996",
     "exception": false,
     "start_time": "2019-11-22T16:04:58.593655",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 2.751473,
     "end_time": "2019-11-22T16:05:01.499408",
     "exception": false,
     "start_time": "2019-11-22T16:04:58.747935",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ['CUDA_VISIBLE_DEVICES'] = '1'\n",
    "\n",
    "import sys\n",
    "sys.path.append('..')\n",
    "\n",
    "import pickle\n",
    "\n",
    "from IPython import display\n",
    "from tqdm import tqdm\n",
    "import numpy as np\n",
    "import matplotlib.pylab as plt\n",
    "import seaborn as sns\n",
    "import pandas as pd\n",
    "\n",
    "from src.pointers import DATA_PATHS\n",
    "from src.experiment_tagging import get_model_path\n",
    "from src.lesion import (perform_lesion_experiment,\n",
    "                              report_lesion_test)\n",
    "from src.visualization import (run_double_spectral_cluster, draw_mlp_clustering_report,\n",
    "                               build_weighted_dist_mat, plot_weighted_dist_mat,\n",
    "                               draw_ow_weight_dependency_graph)\n",
    "\n",
    "sns.set(style='whitegrid')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_path = get_model_path(MODEL_TAG, filter_=FILTER, model_base_path=MODEL_BASE_PATH)[-MODEL_NUMBER]\n",
    "\n",
    "\n",
    "DATASET_NAME, *_ = MODEL_TAG.split('+')\n",
    "DATASET_NAME = DATASET_NAME.lower()\n",
    "\n",
    "PICKLE_PATH = f'../results/double-lesion-{MODEL_TAG}-{MODEL_NUMBER}-stability.pickle'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.080934,
     "end_time": "2019-11-22T16:05:01.720440",
     "exception": false,
     "start_time": "2019-11-22T16:05:01.639506",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "if MODE == 'LOAD':\n",
    "    with open(PICKLE_PATH, 'rb') as f:\n",
    "        (N_CLUSTERS, single_df,\n",
    "         TWO_WAY_N_SHUFFLES, TWO_WAY_PVALUE_THRESHOLD,\n",
    "         double_joint_df, joint_metadata, \n",
    "         double_conditional_df, conditional_metadata) = pickle.load(f)\n",
    "        \n",
    "if MODE == 'STORE':\n",
    "    N_CLUSTERS = 10\n",
    "    TWO_WAY_N_SHUFFLES = 50\n",
    "    TWO_WAY_PVALUE_THRESHOLD = 1 / TWO_WAY_N_SHUFFLES"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Clustering"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 23.575014,
     "end_time": "2019-11-22T16:05:25.323304",
     "exception": false,
     "start_time": "2019-11-22T16:05:01.748290",
     "status": "completed"
    },
    "scrolled": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "one_way_clustering_result = run_double_spectral_cluster(model_path, n_clusters=N_CLUSTERS, with_shuffle=False)\n",
    "\n",
    "draw_mlp_clustering_report(model_path,\n",
    "                           one_way_clustering_result,\n",
    "                           n_cluster=N_CLUSTERS, title=f'{MODEL_TAG}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "papermill": {
     "duration": 0.031096,
     "end_time": "2019-11-22T16:05:26.469742",
     "exception": false,
     "start_time": "2019-11-22T16:05:26.438646",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Single: Taxonomy of layer-clusters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 1354.988489,
     "end_time": "2019-11-22T16:28:01.486921",
     "exception": false,
     "start_time": "2019-11-22T16:05:26.498432",
     "status": "completed"
    },
    "scrolled": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "if MODE == 'STORE':\n",
    "    single_df = report_lesion_test(MODEL_TAG,\n",
    "                                     '.' + DATA_PATHS[DATASET_NAME],\n",
    "                                     model_path,\n",
    "                                     n_clusters=N_CLUSTERS, n_shuffles=100, n_way=1,\n",
    "                                     verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.206508,
     "end_time": "2019-11-22T16:28:01.819154",
     "exception": false,
     "start_time": "2019-11-22T16:28:01.612646",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "single_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.lesion.output import plot_cluster_scatter\n",
    "\n",
    "plot_cluster_scatter(single_df);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_cluster_scatter(single_df, y='z_score');"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_cluster_scatter(single_df, x='diff', y='z_score');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Let's look on a random clustering"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if WITH_ONE_WAY_RANDOM_DEMO:\n",
    "    ax = plot_cluster_scatter(report_lesion_test(MODEL_TAG,\n",
    "                                             '.' + DATA_PATHS[DATASET_NAME],\n",
    "                                             model_path,\n",
    "                                             n_clusters=N_CLUSTERS, n_shuffles=100, n_way=1,\n",
    "                                             with_overall_plot=False, with_accuracy_profile=False,\n",
    "                                             true_as_random=True,\n",
    "                                             verbose=False),\n",
    "                        vmin=-13)\n",
    "\n",
    "    ax.set_ylim(-13, 1);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "papermill": {
     "duration": 0.119255,
     "end_time": "2019-11-22T16:28:02.070919",
     "exception": false,
     "start_time": "2019-11-22T16:28:01.951664",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## Double: Exploring Dependencies between layer-clusters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.192162,
     "end_time": "2019-11-22T16:28:02.617775",
     "exception": false,
     "start_time": "2019-11-22T16:28:02.425613",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from src.lesion.output import (build_double_mat,\n",
    "                                     build_double_joint_interaction_mat,\n",
    "                                     build_conditional_double_df,\n",
    "                                     plot_double_heatmap,\n",
    "                                     compute_damaged_cluster_stats,\n",
    "                                     enrich_score_double_conditional_df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 4227.496925,
     "end_time": "2019-11-22T17:38:30.261484",
     "exception": false,
     "start_time": "2019-11-22T16:28:02.764559",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "if MODE == 'STORE':\n",
    "\n",
    "    (joint_true_results,\n",
    "     joint_all_random_results,\n",
    "     joint_metadata,\n",
    "     joint_evaluation) = perform_lesion_experiment('.' + DATA_PATHS[DATASET_NAME],\n",
    "                                                   model_path,\n",
    "                                                   n_clusters=N_CLUSTERS,\n",
    "                                                   n_shuffles=2, #!!!!!!! IT IS NOT IMPORTANT #TWO_WAY_N_SHUFFLES,\n",
    "                                                   n_way=2,\n",
    "                                                   n_way_type='joint',\n",
    "                                                   with_random=True,\n",
    "                                                   verbose=True)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.lesion.output import compute_damaged_cluster_stats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.409635,
     "end_time": "2019-11-22T17:38:30.833071",
     "exception": false,
     "start_time": "2019-11-22T17:38:30.423436",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "if MODE == 'STORE':\n",
    "    double_joint_df = compute_damaged_cluster_stats(joint_true_results, joint_all_random_results,\n",
    "                                                     joint_metadata, joint_evaluation)\n",
    "\n",
    "double_joint_df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### $Diff_{ij}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "double_joint_diff_mat = -build_double_mat(double_joint_df, single_df, col='diff')\n",
    "\n",
    "plot_double_heatmap(double_joint_diff_mat, double_joint_df,\n",
    "                     metadata=joint_metadata, pvalue_threshod=TWO_WAY_PVALUE_THRESHOLD,\n",
    "                     vmax=50);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Joint\n",
    "\n",
    "#### What can we learn from the joint TWBD?\n",
    "\n",
    "Using the single TWBD prior, with created a taxonomy of clusters, we get the primary division is to important (diff > 1 and significant) and not-important. Let's use this dichotomy to analyze the joint TWBD.\n",
    "\n",
    "Let's look at layer-cluster X and Y. If we assume that they are both important (as single), we won't be surprised that the joint damaged XY is also important(diff > 1 and significant). Therefore, we should focus on XY if it is not important.\n",
    "\n",
    "If we assume that X and Y are both *not* important, it would be interesting if the joint damage XY is important. It means that only their combined formation together is impactful.\n",
    "\n",
    "Finally, considering the case for X is important, and Y is not (without loss of generality), our prior would be that the joint damaged XY will also be important, because of the contribution of X. So not important XY matters in that situation.\n",
    "\n",
    "To summary up, these are the cases we care about, because they convey information that changes our single TWBD prior:\n",
    "\n",
    "| X | Y | XY |\n",
    "|---|---|----|\n",
    "| ✔️ | ✔️ | ❌  |\n",
    "| ✔️ | ❌ | ❌  |\n",
    "| ❌ | ❌ | ✔️  |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.lesion.output import build_double_joint_imp_grouped_df\n",
    "\n",
    "double_joint_imp_df, double_joint_imp_grouped_df = build_double_joint_imp_grouped_df(double_joint_df, single_df)\n",
    "\n",
    "double_joint_imp_grouped_df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Let's iterate on the interesting cases:\n",
    "\n",
    "We should be REALLY CAREFUL when we look at double damage when both of the clusters come from the same layer. The space of possible shuffles behaves different, so I won't try to come up with observation in these cases."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Counter\n",
    "\n",
    "double_joint_interesting_cases = [('✔️', '✔️', '❌'),\n",
    "                              ('✔️', '❌', '❌'),\n",
    "                              ('❌', '✔️', '❌'),\n",
    "                              ('❌', '❌', '✔️')]\n",
    "\n",
    "is_important_fields = ['first_is_important', 'second_is_important', 'is_important'] "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for interesting_case in double_joint_interesting_cases:\n",
    "    \n",
    "    intresting_double_joint_imp_df = (double_joint_imp_df[(double_joint_imp_df[is_important_fields]\n",
    "                                                   == interesting_case)\n",
    "                                              .all(axis=1)])\n",
    "\n",
    "    # same cluster-layer is an anomaly that we should remove (we do so with the heatmap plotting)\n",
    "    intresting_double_joint_imp_df = intresting_double_joint_imp_df[intresting_double_joint_imp_df['first']\n",
    "                                                            != intresting_double_joint_imp_df['second']]\n",
    "\n",
    "    if not intresting_double_joint_imp_df.empty:\n",
    "        \n",
    "        print(interesting_case)\n",
    "\n",
    "        display.display(intresting_double_joint_imp_df.sort_values('diff'))\n",
    "        \n",
    "        display.display(Counter(tuple(sorted(v))\n",
    "                        for v in (intresting_double_joint_imp_df[['first_taxonomy', 'second_taxonomy']]).values))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is hard to come up with a firm conclusions, but we can clearly see that in that pattern, \"almost\" important layer-cluster (diff-but-sig or sig-but-diff) is hitting the important criterion with the help of another layer-cluster.\n",
    "\n",
    "One tempting interpretation, if we continue our previous line of reasoning until now, is that we found a higher-level clustering. These two layer-clusters should be grouped together. However, while the single BDT and its taxonomy are based on spectral clustering, and it is justified by the static structure (stronger weights inside a cluster compared to outside), this is not the case here.\n",
    "\n",
    "An alternative explanation could be that we grouped with joint TWBDT two unrelated layer-clusters, so we did damage in two different \"functions\"; therefore, it a sign for a separate grouping. \n",
    "\n",
    "\n",
    "By the joint TWBDt experiment, we cannot distinguish the two possible explanations; therefore, we'll turn to another method to perform double BDT, which helps us to discover dependencies (in the sense of information flow) between layer-clusters)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "papermill": {
     "duration": 0.141558,
     "end_time": "2019-11-22T17:38:38.073578",
     "exception": false,
     "start_time": "2019-11-22T17:38:37.932020",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "### Conditional - $first|second$\n",
    "\n",
    "* Difference - As in joint $Diff_{first,second}$\n",
    "* P-value - Fix the *second* layer-cluster and shuffle the *first*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 7830.365643,
     "end_time": "2019-11-22T19:49:08.576735",
     "exception": false,
     "start_time": "2019-11-22T17:38:38.211092",
     "status": "completed"
    },
    "scrolled": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "if MODE == 'STORE':\n",
    "    (conditional_true_results,\n",
    "     conditional_all_random_results,\n",
    "     conditional_metadata,\n",
    "     conditional_evaluation) = perform_lesion_experiment('.' + DATA_PATHS[DATASET_NAME],\n",
    "                                                   model_path,\n",
    "                                                   n_clusters=N_CLUSTERS,\n",
    "                                                   n_shuffles=TWO_WAY_N_SHUFFLES,\n",
    "                                                   n_way=2,\n",
    "                                                   n_way_type='conditional',\n",
    "                                                   with_random=True,\n",
    "                                                   verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.534141,
     "end_time": "2019-11-22T19:49:09.306824",
     "exception": false,
     "start_time": "2019-11-22T19:49:08.772683",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "if MODE == 'STORE':\n",
    "    double_conditional_df = compute_damaged_cluster_stats(conditional_true_results,\n",
    "                                                           conditional_all_random_results,\n",
    "                                                           conditional_metadata,\n",
    "                                                           conditional_evaluation,\n",
    "                                                           double_joint_df=double_joint_df,\n",
    "                                                           single_df=single_df,\n",
    "                                                           diff_field='s_i|j')\n",
    "\n",
    "    double_conditional_df = enrich_score_double_conditional_df(double_conditional_df, single_df)\n",
    "\n",
    "\n",
    "double_conditional_df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's plot on heatmap the results:\n",
    "* Rows: First (\"shuffled\") layer cluster\n",
    "* Columns: Second (\"fixed\"/\"conditioned\") layer-cluster\n",
    "* Cells: Difference of true layter-cluster (number) as well as significance (★)\n",
    "\n",
    "Of course, the matrix will be symmetric concerning the difference; however, the significance won't be necessary symmetric.\n",
    "\n",
    "The diagonal represents the single BDT. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 2.489359,
     "end_time": "2019-11-22T19:49:11.952506",
     "exception": false,
     "start_time": "2019-11-22T19:49:09.463147",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "double_conditional_true_mat = -build_double_mat(double_conditional_df, single_df, col='diff')\n",
    "\n",
    "plot_double_heatmap(double_conditional_true_mat, double_conditional_df, is_trig=False,\n",
    "                     metadata=conditional_metadata, pvalue_threshod=TWO_WAY_PVALUE_THRESHOLD,\n",
    "                     vmax=50);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### We can look also into the $s_{i|j}$ - which was used to define the taxonomy (see remark later):\n",
    "\n",
    "## $s_{i|j} = Diff_{ij} - Diff_{j} = (Acc - Acc_{ij}) - (Acc - Acc_{j}) = Acc_{j} - Acc_{ij}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tw_cond_sij_mat = -build_double_mat(double_conditional_df, single_df, col='s_i|j')\n",
    "\n",
    "plot_double_heatmap(tw_cond_sij_mat, double_conditional_df, is_trig=False,\n",
    "                     metadata=conditional_metadata, pvalue_threshod=TWO_WAY_PVALUE_THRESHOLD,\n",
    "                     vmax=50);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Remark & TODO: Integrate into notebook, not as contra to `diff`\n",
    "\n",
    "Let's look at the case of `X -> Y`, where both X and Y are *important* single layer-cluster.\n",
    "If we damage Y, then the output of X won't have any impact.\n",
    "So `d_Y ≈ d_XY` (or `s_X|Y ≈ 0`).\n",
    "The opposite argument gives us `d_X ≈ d_XY` (or `s_Y|X ≈ 0`).\n",
    "\n",
    "I defined an *important conditional* damage X|Y by these two necessary conditions:\n",
    "1. `d_XY > 1%`\n",
    "2. The p-value (damaging X while fixing Y) is minimal, i.e., `1 / (#shuffles + 1)`. In other words, `d_XY` is bigger than `d_ZY` for all (sampled) random shuffles of X (here Z).\n",
    "\n",
    "Now, I'd argue that condition (1) indeed doesn't make sense, but (2) is totally fine.\n",
    "\n",
    "First, the \"importance\" of a conditional damage `X|Y` should be defined in terms of `s_X|Y` and not `d_XY`. The latter is dependent on the impact of Y, which we would like to remove, while `s_X|Y` takes it into account. So a better first condition would be `s_X|Y > 1%`, i.e., the additional damage of X on top of Y is at least 1% of accuracy.\n",
    "\n",
    "Second, recall that we are focusing on the case of `X -> Y`. Assume we damage Y. Damaging X won't increase the damage of Y ( `s_X|Y ≈ 0`), while any other random shuffle Z might contain other neurons that influence other neurons in the layer of Y.\n",
    "**So, they will have a bigger difference, and we would have p-value equals to 1!**\n",
    "However, all the four types of influence (such as `X -> Y`) are *idealized*, and X probably influences on other neurons in the layer of Y, so we introduce some noise to the calculated accuracy. Therefore, we won't get precisely one for p-value.\n",
    "A prediction we can make here is that the standard deviation would be relatively small, and **we should check it**.\n",
    "\n",
    "This line of argumentation works as well for the `Y|X` damage, and other types of `(X|Y, Y|X)` combinations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### We can also explore the relationship between `s_i|j` and `diff`. Keep in mind the taxonomy is based on `s_i|j`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.scatterplot(x='diff', y='s_i|j', hue='taxonomy', data=double_conditional_df);\n",
    "plt.plot([-1, 0], [-1, 0], ls='-', c='.3');\n",
    "plt.plot([-0.01, -0.01], [-1, 0.1], ls='--', c='.3');\n",
    "plt.plot([-1, 0.1], [-0.01, -0.01], ls='--', c='.3');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we could expect, there is a linear relationship between the two metrics. The different \"parallel\" lines are probably coming from different layer-cluster we conditioned on them because it set the offset (because $diff_j$ is fixed.\n",
    "\n",
    "#### Let's see if we get $s_{i|j}$ > 1% with p-value = 1 (called `least_important` in the taxonomy):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "double_conditional_df['taxonomy'].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "double_conditional_df[double_conditional_df['taxonomy'] == 'least_important']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How can we use conditional TWBDT? Discovering Dependencies!\n",
    "\n",
    "Suppose we have two layer-cluster $X$ and $Y$, where $X$ is in an earlier layer then $Y$. We will assume that both of them are *important* by the single BDT (diff. and sig.).\n",
    "\n",
    "Let's focus only on *important* layer-clusters, because we know by the single BDT that they are important to the functioning of the network.\n",
    "\n",
    "We will say that a layter-cluster $Y$ is *depended* on layer-cluster $X$ if there is a *flow of information* from $X$ to $Y$, i.e. the values of $X$ are required for computing $Y$. We will show it as:\n",
    "\n",
    "$$X \\rightarrow Y$$\n",
    "\n",
    "(⚠️ We need a better formalization!)\n",
    "\n",
    "#### There are four kind of possible (idealistic) dependencies, whether there are additional involved layer-clusters ($Z$ and $Z'$):\n",
    "\n",
    "![](imgs/ctwbd-dependencies.jpg)\n",
    "\n",
    "#### How will conditional TWBDT behave?\n",
    "(keep in mind, both $X$ and $Y$ are important)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| Type | X\\|Y | Y\\|X | Why? |\n",
    "|------|-----|-----|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n",
    "| I | ❌ | ❌ | All the (information) output of X goes to Y, and all of the (information) input of Y comes from X. So they are tied together, and damage in one for them will be equivalent to damage the other one. |\n",
    "| II | ✔️ | ❌ | Damaging X will block already all the input of Y, so no additional impact for damaging Y afterwords. However, Z gets input from X independently, so only conditioning on Y won't block all the information from X. |\n",
    "| III | ❌ | ✔️ | The opposite argument from the previous row holds. |\n",
    "| IV | ✔️ | ✔️ | Both of the two previous arguments hold. |\n",
    "\n",
    "**We cannot learn anything from the last type IV**, because it will have the same behavior as no flow of information between $X$ and $Y$. Therefore, we care only about the first three types. It does make sense because, by our prior (single BDT), we should be \"surprised\" if a layer-cluster stops to be important."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "from src.lesion.output import build_tw_cond_imp_merged_df\n",
    "\n",
    "tw_cond_imp_df = build_tw_cond_imp_merged_df(double_conditional_df, single_df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tw_cond_imp_df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This a count for every type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tw_cond_imp_df.groupby(['X|Y', 'Y|X']).size()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how $s_{i|j}$ is influnced by the type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tw_cond_imp_type = (tw_cond_imp_df['X|Y'].astype(str) + '-' + tw_cond_imp_df['Y|X'].astype(str)).values\n",
    "\n",
    "ax = sns.scatterplot(tw_cond_imp_df['s_X|Y']*100,\n",
    "                     tw_cond_imp_df['s_Y|X']*100,\n",
    "                     hue=tw_cond_imp_type,\n",
    "                     s=100)\n",
    "\n",
    "sij_min, sij_max = double_conditional_df['s_i|j'].min() * 100 - 5, double_conditional_df['s_i|j'].max() * 110\n",
    "\n",
    "ax.axis([sij_min, sij_max, sij_min, sij_max]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take only Type I, II and II: There is at least one False for X|Y and Y|X:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from src.lesion.output import draw_tw_cond_dependency_graph\n",
    "\n",
    "if tw_cond_imp_df.shape[0] > 0:\n",
    "    draw_tw_cond_dependency_graph(tw_cond_imp_df, single_df);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### For comparison, let's see the \"dependency\" graph by the strongest weight (absolute sum) path between two layers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "one_way_weighted_dist_mat = build_weighted_dist_mat(model_path, one_way_clustering_result)\n",
    "\n",
    "draw_ow_weight_dependency_graph(one_way_weighted_dist_mat);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Although the two graphs have shared edges, they are not the same, and they capture different stuff."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_weighted_dist_mat(one_way_weighted_dist_mat);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### IGNORE ME - IT IS MORE COMPLEX THAN IT SEEMS\n",
    "### Validation: Transitivity\n",
    "\n",
    "We can try to valdidate our dependency graph, by checking whether it is transitive:\n",
    "\n",
    "#### If\n",
    "$$\n",
    "X \\rightarrow Z \\rightarrow Y\n",
    "$$\n",
    "\n",
    "### Then\n",
    "\n",
    "$$\n",
    "X \\rightarrow Y\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "papermill": {
     "duration": 0.144045,
     "end_time": "2019-11-22T19:49:16.470397",
     "exception": false,
     "start_time": "2019-11-22T19:49:16.326352",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "### Store Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "papermill": {
     "duration": 0.212036,
     "end_time": "2019-11-22T19:49:16.826485",
     "exception": false,
     "start_time": "2019-11-22T19:49:16.614449",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "if MODE == 'STORE':\n",
    "    with open(PICKLE_PATH, 'wb') as f:\n",
    "        pickle.dump((N_CLUSTERS, single_df,\n",
    "                     TWO_WAY_N_SHUFFLES, TWO_WAY_PVALUE_THRESHOLD,\n",
    "                     double_joint_df, joint_metadata,\n",
    "                     double_conditional_df, conditional_metadata),\n",
    "                   f)"
   ]
  }
 ],
 "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.3"
  },
  "papermill": {
   "duration": 13462.717078,
   "end_time": "2019-11-22T19:49:20.536312",
   "environment_variables": {},
   "exception": null,
   "input_path": "./notebooks/mlp-double-lesion-test.ipynb",
   "output_path": "./notebooks/mlp-double-lesion-test.ipynb",
   "parameters": {},
   "start_time": "2019-11-22T16:04:57.819234",
   "version": "1.1.0"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "source": [],
    "metadata": {
     "collapsed": false
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}