{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import networkx as nx\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import scipy.stats as sts\n",
    "import seaborn as sns\n",
    "%matplotlib inline\n",
    "\n",
    "#import conviction files\n",
    "from conviction_helpers import *\n",
    "from conviction_system_logic import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This notebook uses the differential games framework developed by BlockScience. It is currently in private beta, and building towards a full open source release.\n",
    "\n",
    "**Description:**\n",
    "\n",
    "cadCAD is a Python library that assists in the processes of designing, testing and validating complex systems through simulation. At its core, cadCAD is a differential games engine that supports parameter sweeping and Monte Carlo analyses and can be easily integrated with other scientific computing Python modules and data science workflows.\n",
    "\n",
    "To learn more about cadCAD, follow our [tutorial series](https://github.com/BlockScience/cadCAD-Tutorials/tree/master/01%20Tutorials)\n",
    "\n",
    "**Installing cadCAD:**\n",
    "\n",
    "cadCAD is in private beta. Tokens are issued to participants. Replace `<TOKEN>` in the installation URL below\n",
    "```bash\n",
    "pip3 install cadCAD --extra-index-url https://<TOKEN>@repo.fury.io/blockscience/\n",
    "```\n",
    "\n",
    "If you'd like to participate in the beta program, contact cadcad [at] block [dot] science.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "#THIS policy is one of the main paramters of this system!\n",
    "\n",
    "#maximum share of funds a proposal can take\n",
    "default_beta = .25 #later we should set this to be param so we can sweep it\n",
    "# tuning param for the trigger function\n",
    "default_rho = .1*default_beta**2\n",
    "\n",
    "def trigger_threshold(requested, funds, supply, beta=default_beta , rho=default_rho):\n",
    "    \n",
    "    share = requested/funds\n",
    "    if share < beta:\n",
    "        return rho*supply/(beta-share)**2\n",
    "    else: \n",
    "        return np.inf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/Zargham/Documents/GitHub/conviction/conviction_helpers.py:158: RuntimeWarning: divide by zero encountered in log10\n",
      "  demo_data_Z0[sof_ind,ts_ind] = np.log10(trigger)\n",
      "/Users/Zargham/Documents/GitHub/conviction/conviction_helpers.py:160: RuntimeWarning: invalid value encountered in double_scalars\n",
      "  demo_data_Z2[sof_ind,ts_ind] = trigger/tc #share of maximum possible conviction\n",
      "/Users/Zargham/Documents/GitHub/conviction/conviction_helpers.py:161: RuntimeWarning: invalid value encountered in double_scalars\n",
      "  demo_data_Z3[sof_ind,ts_ind] = np.log10(trigger/tc)\n"
     ]
    }
   ],
   "source": [
    "dict1 = trigger_sweep('token_supply',trigger_threshold)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.15940000000000001, 1008990000.0, 'fixed alpha = 0.5')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 32924 characters]\n",
      "text/plain": [
       "<Figure size 720x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "trigger_plotter(dict1['share_of_funds'],\n",
    "                dict1['log10_trigger'], \n",
    "                'log10_trigger',\n",
    "                dict1['total_supply'],\n",
    "                'Token Supply')\n",
    "axis = plt.axis()\n",
    "plt.text(.2*axis[0]+.8*axis[1],axis[-1]*1.01, 'fixed alpha = 0.5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "dict2 = trigger_sweep('alpha',trigger_threshold)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_keys(['log10_trigger', 'trigger', 'share_of_max_conv', 'log10_share_of_max_conv', 'alpha', 'share_of_funds'])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dict2.keys()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 31192 characters]\n",
      "text/plain": [
       "<Figure size 720x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "trigger_plotter(dict2['share_of_funds'],\n",
    "                dict2['log10_share_of_max_conv'], \n",
    "                'Log10 Share of Conviction Required to Pass',\n",
    "                dict2['alpha'],\n",
    "                'alpha')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "n= 35 #initial participants\n",
    "m= 3 #initial proposals\n",
    "\n",
    "initial_sentiment = .7\n",
    "\n",
    "network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note from Kris, consider: substitutibility of proposals st when a substitute passes, affinity for the others goes away; this will make the process more realistic because proposals will end up never passing.\n",
    "\n",
    "implementation notes:\n",
    "- create substitutability matrix (proposal x proposal)\n",
    "- update accounting when thing pass: change affinities and should affect sentiments\n",
    "- define a new 'type' of proposals for tracking 'dead' ones (no longer candidates = zero tokens in support)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "proposals = get_nodes_by_type(network, 'proposal')\n",
    "participants = get_nodes_by_type(network, 'participant')\n",
    "\n",
    "supporters = get_edges_by_type(network, 'support')\n",
    "influencers = get_edges_by_type(network, 'influence')\n",
    "competitors = get_edges_by_type(network, 'conflict')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'type': 'proposal',\n",
       " 'conviction': 0,\n",
       " 'status': 'candidate',\n",
       " 'age': 0,\n",
       " 'funds_requested': 76004.9165822238,\n",
       " 'trigger': 1911.7540934361125}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#sample proposal\n",
    "network.nodes[proposals[0]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'type': 'participant',\n",
       " 'holdings': 899.7297436837233,\n",
       " 'sentiment': 0.7243294825269956}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#sample participant\n",
    "network.nodes[participants[0]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'affinity': 0.006885421446880757,\n",
       " 'tokens': 0,\n",
       " 'conviction': 0,\n",
       " 'type': 'support'}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#sample relationship participant to proposal\n",
    "network.edges[supporters[0]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'influence': 4.311864724268468, 'type': 'influence'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "network.edges[influencers[0]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'conflict': 0.9269205901765302, 'type': 'conflict'}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "network.edges[competitors[0]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Participants Social Network')"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 58416 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "nx.draw_kamada_kawai(network, nodelist = participants, edgelist=influencers)\n",
    "plt.title('Participants Social Network')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Proposals Conflict Network')"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 10196 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "nx.draw_kamada_kawai(network, nodelist = proposals, edgelist=competitors, node_color='b')\n",
    "plt.title('Proposals Conflict Network')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Histogram of Participants Token Holdings')"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 9336 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist([ network.nodes[i]['holdings'] for i in participants])\n",
    "plt.title('Histogram of Participants Token Holdings')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Histogram of Proposals Funds Requested')"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 8532 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist([ network.nodes[i]['funds_requested'] for i in proposals])\n",
    "plt.title('Histogram of Proposals Funds Requested')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "affinities = np.empty((n,m))\n",
    "for i_ind in range(n):\n",
    "    for j_ind in range(m):\n",
    "        i = participants[i_ind]\n",
    "        j = proposals[j_ind]\n",
    "        affinities[i_ind][j_ind] = network.edges[(i,j)]['affinity']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 121.63714285714283, 'participant_id')"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 17576 characters]\n",
      "text/plain": [
       "<Figure size 1440x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "dims = (20, 5)\n",
    "fig, ax = plt.subplots(figsize=dims)\n",
    "\n",
    "sns.heatmap(affinities.T,\n",
    "            xticklabels=participants,\n",
    "            yticklabels=proposals,\n",
    "            square=True,\n",
    "            cbar=True,\n",
    "            ax=ax)\n",
    "\n",
    "plt.title('affinities between participants and proposals')\n",
    "plt.ylabel('proposal_id')\n",
    "plt.xlabel('participant_id')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.9]\n"
     ]
    }
   ],
   "source": [
    "#power of 1 token forever\n",
    "conviction_capactity = [10]\n",
    "alpha = [1-1/cc for cc in conviction_capactity]\n",
    "print(alpha)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "params= {\n",
    "    'sensitivity': [.75],\n",
    "    'tmin': [7], #unit days; minimum periods passed before a proposal can pass\n",
    "    'sentiment_decay': [.001], #termed mu in the state update function\n",
    "    'alpha': alpha,\n",
    "    'base_completion_rate': [10],\n",
    "    'trigger_func': [trigger_threshold]\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "function"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(trigger_threshold)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
    "# Settings of general simulation parameters, unrelated to the system itself\n",
    "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
    "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
    "time_periods_per_run = 1000\n",
    "monte_carlo_runs = 1\n",
    "\n",
    "from cadCAD.configuration.utils import config_sim\n",
    "simulation_parameters = config_sim({\n",
    "    'T': range(time_periods_per_run),\n",
    "    'N': monte_carlo_runs,\n",
    "    'M': params\n",
    "})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'N': 1,\n",
       "  'T': range(0, 1000),\n",
       "  'M': {'sensitivity': 0.75,\n",
       "   'tmin': 7,\n",
       "   'sentiment_decay': 0.001,\n",
       "   'alpha': 0.9,\n",
       "   'base_completion_rate': 10,\n",
       "   'trigger_func': <function __main__.trigger_threshold(requested, funds, supply, beta=0.25, rho=0.00625)>}}]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "simulation_parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "initial_conditions = {'network':network,\n",
    "                      'supply': initial_supply,\n",
    "                      'funds':initial_funds,\n",
    "                      'sentiment': initial_sentiment}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'network': <networkx.classes.digraph.DiGraph at 0x1a22b49b38>,\n",
       " 'supply': 33548.84968598687,\n",
       " 'funds': 1125525.315252941,\n",
       " 'sentiment': 0.7}"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "initial_conditions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
    "# The Partial State Update Blocks\n",
    "partial_state_update_blocks = [\n",
    "    { \n",
    "        'policies': { \n",
    "            #new proposals or new participants\n",
    "            'random': driving_process\n",
    "        },\n",
    "        'variables': {\n",
    "            'network': update_network,\n",
    "            'funds':increment_funds,\n",
    "            'supply':increment_supply\n",
    "        }\n",
    "    },\n",
    "    {\n",
    "      'policies': {\n",
    "          'completion': check_progress #see if any of the funded proposals completes\n",
    "        },\n",
    "        'variables': { # The following state variables will be updated simultaneously\n",
    "            'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it\n",
    "            'network': complete_proposal #book-keeping\n",
    "        }\n",
    "    },\n",
    "        {\n",
    "      'policies': {\n",
    "          'release': trigger_function #check each proposal to see if it passes\n",
    "        },\n",
    "        'variables': { # The following state variables will be updated simultaneously\n",
    "            'funds': decrement_funds, #funds expended\n",
    "            'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment\n",
    "            'network': update_proposals #reset convictions, and participants sentiments\n",
    "                                        #update based on affinities\n",
    "        }\n",
    "    },\n",
    "    { \n",
    "        'policies': { \n",
    "            'participants_act': participants_decisions, #high sentiment, high affinity =>buy\n",
    "                                                        #low sentiment, low affinities => burn\n",
    "                                                        #assign tokens to top affinities\n",
    "        },\n",
    "        'variables': {\n",
    "            'supply': update_supply,\n",
    "            'network': update_tokens #update everyones holdings \n",
    "                                    #and their conviction for each proposal\n",
    "        }\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "from cadCAD.configuration import append_configs\n",
    "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n",
    "# The configurations above are then packaged into a `Configuration` object\n",
    "append_configs(\n",
    "    initial_state=initial_conditions, #dict containing variable names and initial values\n",
    "    partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
    "    sim_configs=simulation_parameters #dict containing simulation parameters\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tabulate import tabulate\n",
    "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
    "from cadCAD import configs\n",
    "import pandas as pd\n",
    "\n",
    "exec_mode = ExecutionMode()\n",
    "multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc)\n",
    "run = Executor(exec_context=multi_proc_ctx, configs=configs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "multi_proc: [<cadCAD.configuration.Configuration object at 0x1a233e3dd8>]\n",
      "[<cadCAD.configuration.Configuration object at 0x1a233e3dd8>]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/Zargham/Documents/GitHub/conviction/conviction_system_logic.py:238: RuntimeWarning: invalid value encountered in double_scalars\n",
      "  force = grants_completed/grants_outstanding\n",
      "/Users/Zargham/Documents/GitHub/conviction/conviction_system_logic.py:361: RuntimeWarning: divide by zero encountered in double_scalars\n",
      "  force = proposals_accepted/proposals_outstanding\n",
      "/Users/Zargham/Documents/GitHub/conviction/conviction_system_logic.py:104: RuntimeWarning: divide by zero encountered in double_scalars\n",
      "  new_proposal = bool(rv2<1/proposal_rate)\n",
      "/Users/Zargham/Documents/GitHub/conviction/conviction_system_logic.py:238: RuntimeWarning: divide by zero encountered in double_scalars\n",
      "  force = grants_completed/grants_outstanding\n"
     ]
    }
   ],
   "source": [
    "i = 0\n",
    "verbose = False\n",
    "results = {}\n",
    "for raw_result, tensor_field in run.execute():\n",
    "    result = pd.DataFrame(raw_result)\n",
    "    if verbose:\n",
    "        print()\n",
    "        print(f\"Tensor Field: {type(tensor_field)}\")\n",
    "        print(tabulate(tensor_field, headers='keys', tablefmt='psql'))\n",
    "        print(f\"Output: {type(result)}\")\n",
    "        print(tabulate(result, headers='keys', tablefmt='psql'))\n",
    "        print()\n",
    "    results[i] = {}\n",
    "    results[i]['result'] = result\n",
    "    results[i]['simulation_parameters'] = simulation_parameters[i]\n",
    "    i += 1\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = results[0]['result']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1a233e39b0>"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 14108 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "df.supply.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1ae5fbc550>"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 14344 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "df.sentiment.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1a25618390>"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 10832 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "df.plot(x='timestep', y='funds')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pad(vec, length,fill=True):\n",
    "    \n",
    "    if fill:\n",
    "        padded = np.zeros(length,)\n",
    "    else:\n",
    "        padded = np.empty(length,)\n",
    "        padded[:] = np.nan\n",
    "        \n",
    "    for i in range(len(vec)):\n",
    "        padded[i]= vec[i]\n",
    "        \n",
    "    return padded\n",
    "\n",
    "def make2D(key, data, fill=False):\n",
    "    maxL = data[key].apply(len).max()\n",
    "    newkey = 'padded_'+key\n",
    "    data[newkey] = data[key].apply(lambda x: pad(x,maxL,fill))\n",
    "    reshaped = np.array([a for a in data[newkey].values])\n",
    "    \n",
    "    return reshaped"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['conviction'] = df.network.apply(lambda g: np.array([g.nodes[j]['conviction'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n",
    "df['candidate_count'] = df.network.apply(lambda g: len([j for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n",
    "df['candidate_funds'] = df.network.apply(lambda g: np.sum([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n",
    "df['candidate_funds_requested'] = df.network.apply(lambda g: np.array([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate']))\n",
    "df['active_count'] = df.network.apply(lambda g: len([j for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='active']))\n",
    "df['active_funds'] = df.network.apply(lambda g: np.sum([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='active']))\n",
    "df['completed_count'] = df.network.apply(lambda g: len([j for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='completed']))\n",
    "df['completed_funds'] = df.network.apply(lambda g: np.sum([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='completed']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['funds_requested'] = df.network.apply(lambda g: np.array([g.nodes[j]['funds_requested'] for j in get_nodes_by_type(g, 'proposal')]))\n",
    "df['share_of_funds_requested'] = df.candidate_funds_requested/df.funds\n",
    "\n",
    "df['share_of_funds_requested_all'] = df.funds_requested/df.funds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['triggers'] = df.network.apply(lambda g: np.array([g.nodes[j]['trigger'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate' ]))\n",
    "df['conviction_share_of_trigger'] = df.conviction/df.triggers\n",
    "df['age'] = df.network.apply(lambda g: np.array([g.nodes[j]['age'] for j in get_nodes_by_type(g, 'proposal') if g.nodes[j]['status']=='candidate' ]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "df['age_all'] = df.network.apply(lambda g: np.array([g.nodes[j]['age'] for j in get_nodes_by_type(g, 'proposal') ]))\n",
    "df['conviction_all'] = df.network.apply(lambda g: np.array([g.nodes[j]['conviction'] for j in get_nodes_by_type(g, 'proposal') ]))\n",
    "df['triggers_all'] = df.network.apply(lambda g: np.array([g.nodes[j]['trigger'] for j in get_nodes_by_type(g, 'proposal')  ]))\n",
    "\n",
    "df['conviction_share_of_trigger_all'] = df.conviction_all/df.triggers_all"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "rdf= df[df.substep==4].copy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "last_net= df.network.values[-1]\n",
    "last_props=get_nodes_by_type(last_net, 'proposal')\n",
    "M = len(last_props)\n",
    "last_parts=get_nodes_by_type(last_net, 'participant')\n",
    "N = len(last_parts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "affinities = np.empty((N,M))\n",
    "for i_ind in range(N):\n",
    "    for j_ind in range(M):\n",
    "        i = last_parts[i_ind]\n",
    "        j = last_props[j_ind]\n",
    "        affinities[i_ind][j_ind] = last_net.edges[(i,j)]['affinity']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 487.1635514018691, 'participant_id')"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 89960 characters]\n",
      "text/plain": [
       "<Figure size 7200x1800 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "dims = (100, 25)\n",
    "fig, ax = plt.subplots(figsize=dims)\n",
    "\n",
    "sns.heatmap(affinities.T,\n",
    "            xticklabels=last_parts,\n",
    "            yticklabels=last_props,\n",
    "            square=True,\n",
    "            cbar=True,\n",
    "            ax=ax)\n",
    "\n",
    "plt.title('affinities between participants and proposals')\n",
    "plt.ylabel('proposal_id')\n",
    "plt.xlabel('participant_id')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "#working on deduplicating colors\n",
    "#\n",
    "#last_props=get_nodes_by_type(last_net, 'proposal')\n",
    "#M = len(last_props)\n",
    "\n",
    "#cm = plt.get_cmap('gist_rainbow')\n",
    "#c= [cm(1.*j/M) for j in range(M)] "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[   1.,    1.,    1., ...,   nan,   nan,   nan],\n",
       "       [   2.,    2.,    2., ...,   nan,   nan,   nan],\n",
       "       [   3.,    3.,    3., ...,   nan,   nan,   nan],\n",
       "       ...,\n",
       "       [ 998.,  998.,  998., ...,  492.,  457.,  300.],\n",
       "       [ 999.,  999.,  999., ...,  493.,  458.,  301.],\n",
       "       [1000., 1000., 1000., ...,  494.,  459.,  302.]])"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "make2D('age_all', rdf)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1aefcb8b38>"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 14952 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "rdf.plot(x='timestep',y=['candidate_count','active_count','completed_count'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x1ab2284470>"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 17948 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "rdf.plot(x='timestep',y=['candidate_funds','active_funds','completed_funds'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'share_of_funds_requested')"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 25412 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(rdf.timestep,make2D('share_of_funds_requested_all', rdf))\n",
    "plt.title('share_of_funds_requested by proposal')\n",
    "plt.xlabel('time $t$')\n",
    "plt.ylabel('share_of_funds_requested')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 0, 'share_of_funds_requested')"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 18092 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.loglog(make2D('share_of_funds_requested_all', rdf), make2D('conviction_all', rdf), '.')\n",
    "plt.ylabel('conviction')\n",
    "plt.xlabel('share_of_funds_requested')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 0, 'proposal_age')"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 11280 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(make2D('age_all', rdf), make2D('triggers_all', rdf))\n",
    "plt.ylabel('triggers')\n",
    "plt.xlabel('proposal_age')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'phase: Triggers & Conviction')"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 23524 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.loglog(make2D('conviction_all', rdf), make2D('triggers_all', rdf))\n",
    "a = plt.axis()\n",
    "plt.loglog(a[:2],a[2:], 'k',alpha=.5 )\n",
    "plt.ylabel('triggers')\n",
    "plt.xlabel('conviction')\n",
    "plt.title('phase: Triggers & Conviction')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.LineCollection at 0x1a2eca74a8>"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 22460 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "T = time_periods_per_run\n",
    "plt.loglog(rdf.timestep,make2D('conviction_share_of_trigger_all', rdf))\n",
    "plt.title('conviction_share_of_trigger')\n",
    "plt.xlabel('time $t$')\n",
    "plt.ylabel('conviction_share_of_trigger')\n",
    "plt.hlines(1,0,T, linestyle='--')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.LineCollection at 0x1a3a1e6630>"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 28416 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.loglog(make2D('age_all', rdf), make2D('conviction_share_of_trigger_all', rdf))\n",
    "plt.ylabel('triggers')\n",
    "plt.xlabel('proposal_age')\n",
    "plt.hlines(1,0,T, linestyle='--')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "nets = rdf.network.values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 142228 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "snap_plot(nets[-1:], ani=False, savefigs=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
