├── .ipynb_checkpoints ├── ABM - discounted integral voting-checkpoint.ipynb ├── abc_sim-Copy1-checkpoint.ipynb ├── abc_sim-Copy2-checkpoint.ipynb ├── abc_sim-checkpoint.ipynb ├── abc_sim-simpler-checkpoint.ipynb ├── abc_sim-simpler-martingale_paramsweep-checkpoint.ipynb ├── cic_initialization-checkpoint.ipynb ├── conviction_cadCAD-checkpoint.ipynb ├── conviction_cadCAD2-checkpoint.ipynb ├── conviction_cadCAD3-checkpoint.ipynb ├── funding campaign demo-checkpoint.ipynb └── hatch_sim-checkpoint.ipynb ├── CICecosubsystem.jpeg ├── CICecosystem.jpeg ├── CICinvariant.jpeg ├── GJMMEqSVef ├── Conviction Demo 1.mov ├── GJMMEqSVef_fig0.png ├── GJMMEqSVef_fig1.png ├── GJMMEqSVef_fig10.png ├── GJMMEqSVef_fig100.png ├── GJMMEqSVef_fig101.png ├── GJMMEqSVef_fig102.png ├── GJMMEqSVef_fig103.png ├── GJMMEqSVef_fig104.png ├── GJMMEqSVef_fig105.png ├── GJMMEqSVef_fig106.png ├── GJMMEqSVef_fig107.png ├── GJMMEqSVef_fig108.png ├── GJMMEqSVef_fig109.png ├── GJMMEqSVef_fig11.png ├── GJMMEqSVef_fig110.png ├── GJMMEqSVef_fig111.png ├── GJMMEqSVef_fig112.png ├── GJMMEqSVef_fig113.png ├── GJMMEqSVef_fig114.png ├── GJMMEqSVef_fig115.png ├── GJMMEqSVef_fig116.png ├── GJMMEqSVef_fig117.png ├── GJMMEqSVef_fig118.png ├── GJMMEqSVef_fig119.png ├── GJMMEqSVef_fig12.png ├── GJMMEqSVef_fig120.png ├── GJMMEqSVef_fig121.png ├── GJMMEqSVef_fig122.png ├── GJMMEqSVef_fig123.png ├── GJMMEqSVef_fig124.png ├── GJMMEqSVef_fig125.png ├── GJMMEqSVef_fig126.png ├── GJMMEqSVef_fig127.png ├── GJMMEqSVef_fig128.png ├── GJMMEqSVef_fig129.png ├── GJMMEqSVef_fig13.png ├── GJMMEqSVef_fig130.png ├── GJMMEqSVef_fig131.png ├── GJMMEqSVef_fig132.png ├── GJMMEqSVef_fig133.png ├── GJMMEqSVef_fig134.png ├── GJMMEqSVef_fig135.png ├── GJMMEqSVef_fig136.png ├── GJMMEqSVef_fig137.png ├── GJMMEqSVef_fig138.png ├── GJMMEqSVef_fig139.png ├── GJMMEqSVef_fig14.png ├── GJMMEqSVef_fig140.png ├── GJMMEqSVef_fig141.png ├── GJMMEqSVef_fig142.png ├── GJMMEqSVef_fig143.png ├── GJMMEqSVef_fig144.png ├── GJMMEqSVef_fig145.png ├── GJMMEqSVef_fig146.png ├── GJMMEqSVef_fig147.png ├── GJMMEqSVef_fig148.png ├── GJMMEqSVef_fig149.png ├── GJMMEqSVef_fig15.png ├── GJMMEqSVef_fig150.png ├── GJMMEqSVef_fig151.png ├── GJMMEqSVef_fig152.png ├── GJMMEqSVef_fig153.png ├── GJMMEqSVef_fig154.png ├── GJMMEqSVef_fig155.png ├── GJMMEqSVef_fig156.png ├── GJMMEqSVef_fig157.png ├── GJMMEqSVef_fig158.png ├── GJMMEqSVef_fig159.png ├── GJMMEqSVef_fig16.png ├── GJMMEqSVef_fig160.png ├── GJMMEqSVef_fig161.png ├── GJMMEqSVef_fig162.png ├── GJMMEqSVef_fig163.png ├── GJMMEqSVef_fig164.png ├── GJMMEqSVef_fig165.png ├── GJMMEqSVef_fig166.png ├── GJMMEqSVef_fig167.png ├── GJMMEqSVef_fig168.png ├── GJMMEqSVef_fig169.png ├── GJMMEqSVef_fig17.png ├── GJMMEqSVef_fig170.png ├── GJMMEqSVef_fig171.png ├── GJMMEqSVef_fig172.png ├── GJMMEqSVef_fig173.png ├── GJMMEqSVef_fig174.png ├── GJMMEqSVef_fig175.png ├── GJMMEqSVef_fig176.png ├── GJMMEqSVef_fig177.png ├── GJMMEqSVef_fig178.png ├── GJMMEqSVef_fig179.png ├── GJMMEqSVef_fig18.png ├── GJMMEqSVef_fig180.png ├── GJMMEqSVef_fig181.png ├── GJMMEqSVef_fig182.png ├── GJMMEqSVef_fig183.png ├── GJMMEqSVef_fig184.png ├── GJMMEqSVef_fig185.png ├── GJMMEqSVef_fig186.png ├── GJMMEqSVef_fig187.png ├── GJMMEqSVef_fig188.png ├── GJMMEqSVef_fig189.png ├── GJMMEqSVef_fig19.png ├── GJMMEqSVef_fig190.png ├── GJMMEqSVef_fig191.png ├── GJMMEqSVef_fig192.png ├── GJMMEqSVef_fig193.png ├── GJMMEqSVef_fig194.png ├── GJMMEqSVef_fig195.png ├── GJMMEqSVef_fig196.png ├── GJMMEqSVef_fig197.png ├── GJMMEqSVef_fig198.png ├── GJMMEqSVef_fig199.png ├── GJMMEqSVef_fig2.png ├── GJMMEqSVef_fig20.png ├── GJMMEqSVef_fig21.png ├── GJMMEqSVef_fig22.png ├── GJMMEqSVef_fig23.png ├── GJMMEqSVef_fig24.png ├── GJMMEqSVef_fig25.png ├── GJMMEqSVef_fig26.png ├── GJMMEqSVef_fig27.png ├── GJMMEqSVef_fig28.png ├── GJMMEqSVef_fig29.png ├── GJMMEqSVef_fig3.png ├── GJMMEqSVef_fig30.png ├── GJMMEqSVef_fig31.png ├── GJMMEqSVef_fig32.png ├── GJMMEqSVef_fig33.png ├── GJMMEqSVef_fig34.png ├── GJMMEqSVef_fig35.png ├── GJMMEqSVef_fig36.png ├── GJMMEqSVef_fig37.png ├── GJMMEqSVef_fig38.png ├── GJMMEqSVef_fig39.png ├── GJMMEqSVef_fig4.png ├── GJMMEqSVef_fig40.png ├── GJMMEqSVef_fig41.png ├── GJMMEqSVef_fig42.png ├── GJMMEqSVef_fig43.png ├── GJMMEqSVef_fig44.png ├── GJMMEqSVef_fig45.png ├── GJMMEqSVef_fig46.png ├── GJMMEqSVef_fig47.png ├── GJMMEqSVef_fig48.png ├── GJMMEqSVef_fig49.png ├── GJMMEqSVef_fig5.png ├── GJMMEqSVef_fig50.png ├── GJMMEqSVef_fig51.png ├── GJMMEqSVef_fig52.png ├── GJMMEqSVef_fig53.png ├── GJMMEqSVef_fig54.png ├── GJMMEqSVef_fig55.png ├── GJMMEqSVef_fig56.png ├── GJMMEqSVef_fig57.png ├── GJMMEqSVef_fig58.png ├── GJMMEqSVef_fig59.png ├── GJMMEqSVef_fig6.png ├── GJMMEqSVef_fig60.png ├── GJMMEqSVef_fig61.png ├── GJMMEqSVef_fig62.png ├── GJMMEqSVef_fig63.png ├── GJMMEqSVef_fig64.png ├── GJMMEqSVef_fig65.png ├── GJMMEqSVef_fig66.png ├── GJMMEqSVef_fig67.png ├── GJMMEqSVef_fig68.png ├── GJMMEqSVef_fig69.png ├── GJMMEqSVef_fig7.png ├── GJMMEqSVef_fig70.png ├── GJMMEqSVef_fig71.png ├── GJMMEqSVef_fig72.png ├── GJMMEqSVef_fig73.png ├── GJMMEqSVef_fig74.png ├── GJMMEqSVef_fig75.png ├── GJMMEqSVef_fig76.png ├── GJMMEqSVef_fig77.png ├── GJMMEqSVef_fig78.png ├── GJMMEqSVef_fig79.png ├── GJMMEqSVef_fig8.png ├── GJMMEqSVef_fig80.png ├── GJMMEqSVef_fig81.png ├── GJMMEqSVef_fig82.png ├── GJMMEqSVef_fig83.png ├── GJMMEqSVef_fig84.png ├── GJMMEqSVef_fig85.png ├── GJMMEqSVef_fig86.png ├── GJMMEqSVef_fig87.png ├── GJMMEqSVef_fig88.png ├── GJMMEqSVef_fig89.png ├── GJMMEqSVef_fig9.png ├── GJMMEqSVef_fig90.png ├── GJMMEqSVef_fig91.png ├── GJMMEqSVef_fig92.png ├── GJMMEqSVef_fig93.png ├── GJMMEqSVef_fig94.png ├── GJMMEqSVef_fig95.png ├── GJMMEqSVef_fig96.png ├── GJMMEqSVef_fig97.png ├── GJMMEqSVef_fig98.png └── GJMMEqSVef_fig99.png ├── GrassrootsEconomicsCICcontractconservation.jpeg ├── README.md ├── __pycache__ ├── bonding_curve_eq.cpython-36.pyc ├── conviction_helpers.cpython-36.pyc ├── conviction_system_logic.cpython-36.pyc ├── conviction_system_logic2.cpython-36.pyc └── conviction_system_logic3.cpython-36.pyc ├── abc_sim-Copy1.ipynb ├── abc_sim-Copy2.ipynb ├── abc_sim-simpler-martingale_paramsweep.ipynb ├── abc_sim-simpler.ipynb ├── abc_sim.ipynb ├── bonding_curve_eq.py ├── c_2014_zargham_etal.pdf ├── camp_logics.png ├── cic_initialization.ipynb ├── conviction_cadCAD.ipynb ├── conviction_cadCAD2.ipynb ├── conviction_cadCAD3.ipynb ├── conviction_helpers.py ├── conviction_system_logic.py ├── conviction_system_logic2.py ├── conviction_system_logic3.py ├── funding campaign demo.ipynb ├── hatch_sim.ipynb ├── requirements.txt ├── scratchwork_old ├── ABM - discounted integral voting.ipynb ├── passive_voting.isdb └── passive_voting.stmx ├── scratwork_cadcad ├── conviction_cadCAD.py ├── conviction_system_logic_sim-josh.py ├── conviction_system_logic_sim.py └── conviction_test_sim.py └── social-sensorfusion.pdf /.ipynb_checkpoints/ABM - discounted integral voting-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import numpy as np\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "import seaborn as sns\n", 13 | "import random\n", 14 | "\n", 15 | "from scipy.stats import gamma\n", 16 | "\n", 17 | "%matplotlib inline" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "# Discounted integral 'Voting'" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "Simple model proposed for implementing and testing a voting scheme\n", 32 | "\n", 33 | "- Assume a dynamic supply of governance tokens accessed by a bonding ETH (linear bonding curve)\n", 34 | "- Assume this tokens also represent a stake in a revenue generating process\n", 35 | "- The revenue generating process is has one parameter which is \"governed\" \n", 36 | "- The revenue generated is random and there is a true \"best parameter\" unknown to the voters which may change\n", 37 | "- The goal of the 'voting' system is for the selected parameter to trend toward the \"best parameter\" (even if it changes)\n", 38 | "- In this set up, voting is completely passive, \"votes\" are automatically determined by each agents belief state and counted according to their balance of the 'Tokens' that representing their voting capacity\n", 39 | "- An agent has the right to change their belief or preference at any time but the effect of their prior beliefs or prefences continues to influence the system, decaying in time according to the forgetfulness parameter\n", 40 | "- These tokens also represent their stake in the pool of Ether being generated by the revenue process\n", 41 | "\n", 42 | "This is a sensor fusion problem -- coordination problem. The environment, the pool of agents, the process, the actions and the system updates have been made mind-numbingly noisy in order to show the effect of the di" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "#Define the Revenue generating process\n", 52 | "def revenue(true_best_param,current_voted_param ):\n", 53 | " #use an concave function with unique maximum as true_best_param = current_voted_param\n", 54 | " base_scale = 1\n", 55 | " scale = base_scale*np.exp(-(true_best_param-current_voted_param)**2)\n", 56 | " shape = .5\n", 57 | " return gamma(scale, shape).rvs()" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 3, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "data": { 67 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYAAAAD8CAYAAAB+UHOxAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAFdxJREFUeJzt3X2QVfWd5/H3d0DtEc3gQ5tSmtDEBwJaNYBdBkdjbVQ0yKxIlUatbBTDFqn4MBkzqYkZaipmWcpYccfomDFFiS7sWkNSigquhVBGa7N/KA8xZdAeA6uuXGS0xQeciYAk3/2jD9hKP9/b9zZ93q+qrj7nd3/nnu9t6Pvp8zvn/G5kJpKk8vmTRhcgSWoMA0CSSsoAkKSSMgAkqaQMAEkqKQNAkkrKAJCkkjIAJKmkDABJKqnRjS6gN8cff3y2trY2ugxJOqRs2rTp7cxs7qvfsA6A1tZWNm7c2OgyJOmQEhH/rz/9HAKSpJIyACSppAwASSqpYX0OQJIG4qOPPqJSqbB79+5Gl1IXTU1NtLS0cNhhhw1qewNA0ohRqVQ4+uijaW1tJSIaXc6Qykx27txJpVJh4sSJg3qOPoeAIuL+iHgrIjZ3aTs2ItZFxJbi+zFFe0TE3RGxNSJeiIjpXba5tui/JSKuHVS1ktSL3bt3c9xxx434N3+AiOC4446r6minP+cA/jvwlU+13QI8lZmnAk8V6wCzgFOLrwXAvUWhxwI/AL4InAX8YH9oSFItleHNf79qX2ufAZCZ/xt451PNc4BlxfIy4LIu7cuz07PA2Ig4EbgYWJeZ72Tmu8A6Dg4VSVIdDfYcwGczcwdAZu6IiBOK9nHAti79KkVbT+2SNGTuXPe7mj7fzTNPG1D/W2+9laOOOooJEyZw66230t7ezvr162lrawNg586dXH755WzYsIF58+Zxzz331LTevtT6JHB3xyPZS/vBTxCxgM7hIz73uc/VrrJBePrvf35g+cuLrmxgJZIOZWeccQYrV67km9/85ifam5qaWLRoEZs3b2bz5s09bD10BnsfwJvF0A7F97eK9gowvku/FuCNXtoPkplLMrMtM9uam/ucykKShpXFixczadIkLrzwQl5++WUAJk+ezKRJkw7qO2bMGM4991yamprqXSYw+ABYBey/kuda4LEu7dcUVwPNAN4vhoqeBC6KiGOKk78XFW2SNGJs2rSJFStW8Pzzz7Ny5Uo2bNjQ6JJ61ecQUET8M/AfgOMjokLn1Tw/An4REfOB14Eriu5PAJcAW4HfA9cBZOY7EbEI2P/T+C+Z+ekTy5J0SPvVr37F3LlzOfLIIwG49NJLG1xR7/oMgMy8uoeHLuimbwI39PA89wP3D6g6STrEHEqXoToXkCTVyHnnnccjjzzChx9+yAcffMDq1asbXVKvnApC0og10Ms2qzV9+nSuvPJKpk6dyoQJE/jSl74EwCOPPMJNN91ER0cHs2fPZurUqTz5ZOdp0NbWVnbt2sXevXt59NFHWbt2LVOmTKlLvQaAJNXQwoULWbhw4UHtc+fO7bb/a6+9NsQV9cwhIEkqKQNAkkrKAJCkkjIAJKmkDABJKikDQJJKystAJY1cT99W2+f78vcH1L2v6aDXrVvHLbfcwt69ezn88MP58Y9/zPnnn1/bmnthAEjSEOtpOujjjz+e1atXc9JJJ7F582Yuvvhitm/fXre6DABJqqHFixezfPlyxo8fT3NzM2eeeSaTJ0/utu+0adMOLJ9++uns3r2bPXv2cMQRR9SlVgNAkmqk63TQ+/btY/r06Zx55pn92vbhhx9m2rRpdXvzBwNAkmpmsNNBv/jii3zve99j7dq1Q1neQbwKSJJqaKDTQVcqFebOncvy5cs5+eSTh6iq7hkAklQjA50O+r333mP27NncdtttnHPOOXWq8mMOAUkauQZ42Wa1Bjod9D333MPWrVtZtGgRixYtAmDt2rWccMIJdak3Oj/Ea3hqa2vLjRs3Nmz/T//9zw8sf3nRlQ2rQ1L/tLe393jFzUjV3WuOiE2Z2dbXtg4BSVJJGQCSVFIGgCSVlAEgSSVlAEhSSRkAklRS3gfQizf+7eNZ+f7pN/80JPu4fur1Q/K8kmr/ezvQ39f900G/+eabrF69msMPP5yTTz6ZBx54gLFjx7Jz504uv/xyNmzYwLx587jnnntqWm9fPAKQpCE2c+ZMNm/ezAsvvMBpp53Gbbd1fk5BU1MTixYt4o477mhIXQaAJNXQ4sWLmTRpEhdeeCEvv/wyABdddBGjR3cOuMyYMYNKpQLAmDFjOPfcc2lqampIrQ4BSVKN9Gc66Pvvv58rrxweMwsYAJJUI31NB7148WJGjx7N1772tUaUdxADQJJqqKfpoJctW8bjjz/OU089NeApo4eK5wAkqUZ6mg56zZo13H777axaterA0cFw4BGApBGr3pdZ9zQd9I033siePXuYOXMm0Hki+Gc/+xkAra2t7Nq1i7179/Loo4+ydu1apkyZUpd6qwqAiLgZ+M9AAr8FrgNOBFYAxwK/Br6emXsj4ghgOXAmsBO4MjNfq2b/kjTcLFy4kIULF36i7bvf/W6P/V977bUhrqhngx4CiohxwF8BbZl5BjAKuAq4HbgzM08F3gXmF5vMB97NzFOAO4t+kqQGqfYcwGjgTyNiNHAksAM4H3ioeHwZcFmxPKdYp3j8ghguZ0IkqYQGHQCZuR24A3idzjf+94FNwHuZua/oVgHGFcvjgG3FtvuK/scNdv+SpOpUMwR0DJ1/1U8ETgLGALO66br/Mye7+2v/oM+jjIgFEbExIjZ2dHQMtjxJUh+qGQK6EHg1Mzsy8yNgJfAXwNhiSAigBXijWK4A4wGKx/8MeOfTT5qZSzKzLTPbmpubqyhPktSbagLgdWBGRBxZjOVfALwEPA1cXvS5FnisWF5VrFM8/ssczp9IL0kj3KAvA83M5yLiITov9dwHPA8sAf4XsCIi/mvRtrTYZCnwPyJiK51/+V9VTeGS1JeOf6zt9MrNN904oP77p4OeMGECt956K+3t7axfv562tjaAXqeD3rRpE/PmzePDDz/kkksu4a677qr5HcRVXQWUmT/IzC9k5hmZ+fXM3JOZr2TmWZl5SmZekZl7ir67i/VTisdfqc1LkKTh7YwzzmDlypWcd955n2jvbTrob33rWyxZsoQtW7awZcsW1qxZU/O6nApCkmqou+mgJ0+ezKRJkw7q29N00Dt27GDXrl2cffbZRATXXHMNjz76aM1rdSoISaqR/kwH3R/bt2+npaXlwHpLSwvbt2/vZYvBMQAkqUb6mg66v7q7PmYo7pt1CEiSaqgWb9QtLS0HPjUMoFKpcNJJJ1X9vJ9mAEhSjfQ0HfRAnXjiiRx99NE8++yzZCbLly9nzpw5Na7WISBJI9hAL9usVk/TQT/yyCPcdNNNdHR0MHv2bKZOncqTTz4J9Dwd9L333nvgMtBZs2Yxa1Z3Ey1UxwCQpBrqbjpogLlz53bbv6fpoNva2ti8eXMtSzuIQ0CSVFIGgCSVlAEgSSVlAEhSSXkSuDe73/94+dUN/d9u4pdqX4sk1ZhHAJJUUh4BSBqx1q+u7aTDZ/3Hzw+of1/TQQPcdtttLF26lFGjRnH33Xdz8cUXA/CNb3yDxx9/nBNOOGHILgf1CECShlhP00G/9NJLrFixghdffJE1a9Zw/fXX84c//AGAefPmDckU0F0ZAJJUQwOZDvqxxx7jqquu4ogjjmDixImccsoprF+/HuicVuLYY48d0lodApKkGhnodNDbt29nxowZB9aHatrnnhgAklQjA50Oul7TPvfEISBJqqGBvIG3tLSwbdu2A+tDNe1zTwwASaqRgU4Hfemll7JixQr27NnDq6++ypYtWzjrrLPqVK1DQJJGsIFetlmtgU4Hffrpp/PVr36VKVOmMHr0aH76058yatQoAK6++mqeeeYZ3n77bVpaWvjhD3/I/Pnza1pvdDcGNVy0tbXlxo0bG7b/B7/1gwPL71/0u/5vOIA7ga+fev1ASpLUi/b2diZPntzoMuqqu9ccEZsys62HTQ5wCEiSSsoAkKSSMgAkjSjDeVi71qp9rQaApBGjqamJnTt3liIEMpOdO3fS1NQ06OfwKiBJI0ZLSwuVSoWOjo5Gl1IXTU1NtLS0DHp7A0DSiHHYYYcxceLERpdxyHAISJJKygCQpJIyACSppAwASSopA0CSSqqqAIiIsRHxUET8S0S0R8TZEXFsRKyLiC3F92OKvhERd0fE1oh4ISKm1+YlSJIGo9ojgLuANZn5BeDPgXbgFuCpzDwVeKpYB5gFnFp8LQDurXLfkqQqDDoAIuIzwHnAUoDM3JuZ7wFzgGVFt2XAZcXyHGB5dnoWGBsRJw66cklSVao5Avg80AE8EBHPR8R9ETEG+Gxm7gAovp9Q9B8HbOuyfaVokyQ1QDUBMBqYDtybmdOAf+fj4Z7udPc5aQdN2BERCyJiY0RsLMvt3JLUCNUEQAWoZOZzxfpDdAbCm/uHdorvb3XpP77L9i3AG59+0sxckpltmdnW3NxcRXmSpN4Mei6gzPzXiNgWEZMy82XgAuCl4uta4EfF98eKTVYBN0bECuCLwPv7h4qGqz37/nhgedt7H/Z7u8r/3dn/fXR8/EljN888rd/bSVK1qp0M7ibgwYg4HHgFuI7Oo4pfRMR84HXgiqLvE8AlwFbg90VfSVKDVBUAmfkboLvPnbygm74J3FDN/iRJteOdwJJUUgaAJJWUASBJJWUASFJJGQCSVFIGgCSVlAEgSSVlAEhSSRkAklRSBoAklZQBIEklZQBIUkkZAJJUUgaAJJWUASBJJWUASFJJGQCSVFIGgCSVlAEgSSVlAEhSSRkAklRSBoAklZQBIEklZQBIUkkZAJJUUgaAJJWUASBJJWUASFJJGQCSVFIGgCSVlAEgSSVlAEhSSVUdABExKiKej4jHi/WJEfFcRGyJiJ9HxOFF+xHF+tbi8dZq9y1JGrxaHAF8G2jvsn47cGdmngq8C8wv2ucD72bmKcCdRT9JUoNUFQAR0QLMBu4r1gM4H3io6LIMuKxYnlOsUzx+QdFfktQA1R4B/AT4W+CPxfpxwHuZua9YrwDjiuVxwDaA4vH3i/6SpAYYdABExF8Cb2Xmpq7N3XTNfjzW9XkXRMTGiNjY0dEx2PIkSX2o5gjgHODSiHgNWEHn0M9PgLERMbro0wK8USxXgPEAxeN/Brzz6SfNzCWZ2ZaZbc3NzVWUJ0nqzaADIDO/n5ktmdkKXAX8MjO/BjwNXF50uxZ4rFheVaxTPP7LzDzoCECSVB9DcR/A94DvRMRWOsf4lxbtS4HjivbvALcMwb4lSf00uu8ufcvMZ4BniuVXgLO66bMbuKIW+5MkVc87gSWppAwASSopA0CSSsoAkKSSMgAkqaQMAEkqKQNAkkrKAJCkkjIAJKmkDABJKikDQJJKygCQpJIyACSppAwASSopA0CSSsoAkKSSMgAkqaQMAEkqKQNAkkrKAJCkkjIAJKmkDABJKikDQJJKygCQpJIyACSppAwASSopA0CSSmp0owvQx+5c97u67/PmmafVfZ+ShgePACSppAwASSopA0CSSsoAkKSSMgAkqaQGHQARMT4ino6I9oh4MSK+XbQfGxHrImJL8f2Yoj0i4u6I2BoRL0TE9Fq9CEnSwFVzBLAP+JvMnAzMAG6IiCnALcBTmXkq8FSxDjALOLX4WgDcW8W+JUlVGnQAZOaOzPx1sfwB0A6MA+YAy4puy4DLiuU5wPLs9CwwNiJOHHTlkqSq1OQcQES0AtOA54DPZuYO6AwJ4ISi2zhgW5fNKkXbp59rQURsjIiNHR0dtShPktSNqgMgIo4CHgb+OjN39da1m7Y8qCFzSWa2ZWZbc3NzteVJknpQVQBExGF0vvk/mJkri+Y39w/tFN/fKtorwPgum7cAb1Szf0nS4FVzFVAAS4H2zPyHLg+tAq4tlq8FHuvSfk1xNdAM4P39Q0WSpPqrZjK4c4CvA7+NiN8UbX8H/Aj4RUTMB14HrigeewK4BNgK/B64rop9S5KqNOgAyMz/Q/fj+gAXdNM/gRsGuz9JUm15J7AklZQBIEklZQBIUkkZAJJUUgaAJJWUASBJJWUASFJJVXMjmHrQsmtTv/vOeO/9T6w/+7kFtS5HkrrlEYAklZQBIEklZQBIUkkZAJJUUgaAJJWUASBJJeVloCV357rf1X2fN888re77lHQwjwAkqaQMAEkqKYeAGmzVn2z9xHpl18+HZD/TP3PlkDyvpEOXRwCSVFIGgCSVlAEgSSVlAEhSSRkAklRSBoAklZSXgQ4zA/kwma4qnzmzxpVIGuk8ApCkkjIAJKmkDABJKinPAajunIFUGh4MgBGir5PHb/XweK1PHjvnkHTocAhIkkrKAJCkkqr7EFBEfAW4CxgF3JeZP6p3DSofzztIB6trAETEKOCnwEygAmyIiFWZ+VI969DHBnvjWU96OtfQVbXnHTzPINVGvYeAzgK2ZuYrmbkXWAHMqXMNkiTqPwQ0DtjWZb0CfLHONajBqj3q6M9RRi1c+sdTqtr+znULalRJ/znspIGodwBEN235iQ4RC4D9vzn/FhEvV7G/44G3q9j+Y/fV5FnqrXav/9BU1euv/uTUf6v6GQbqO59cLfu/P5T3ZzChP53qHQAVYHyX9Rbgja4dMnMJsKQWO4uIjZnZVovnOhT5+n39ZX794M+gL/U+B7ABODUiJkbE4cBVwKo61yBJos5HAJm5LyJuBJ6k8zLQ+zPzxXrWIEnqVPf7ADLzCeCJOu2uJkNJhzBff7mV/fWDP4NeRWb23UuSNOI4FYQkldSIDICI+EpEvBwRWyPilkbXU08RMT4ino6I9oh4MSK+3eiaGiEiRkXE8xHxeKNraYSIGBsRD0XEvxT/F85udE31FBE3F///N0fEP0dEU6NrGo5GXAB0mW5iFjAFuDoipjS2qrraB/xNZk4GZgA3lOz17/dtoL3RRTTQXcCazPwC8OeU6GcREeOAvwLaMvMMOi84uaqxVQ1PIy4AKPl0E5m5IzN/XSx/QOcv/rjGVlVfEdECzOZQvX2vShHxGeA8YClAZu7NzPcaW1XdjQb+NCJGA0fyqfuN1GkkBkB3002U6g1wv4hoBaYBzzW2krr7CfC3wB8bXUiDfB7oAB4ohsHui4gxjS6qXjJzO3AH8DqwA3g/M9c2tqrhaSQGQJ/TTZRBRBwFPAz8dWbuanQ99RIRfwm8lZn1mTBoeBoNTAfuzcxpwL8DpTkXFhHH0HnUPxE4CRgTEf+psVUNTyMxAPqcbmKki4jD6HzzfzAzVza6njo7B7g0Il6jc/jv/Ij4n40tqe4qQCUz9x/5PURnIJTFhcCrmdmRmR8BK4G/aHBNw9JIDIBSTzcREUHn2G97Zv5Do+upt8z8fma2ZGYrnf/2v8zMUv31l5n/CmyLiElF0wVAmT5z43VgRkQcWfw+XECJToIPxIj7UHinm+Ac4OvAbyPiN0Xb3xV3YKs8bgIeLP4IegW4rsH11E1mPhcRDwG/pvOquOfxjuBueSewJJXUSBwCkiT1gwEgSSVlAEhSSRkAklRSBoAklZQBIEklZQBIUkkZAJJUUv8f5VqZpg3CyroAAAAASUVORK5CYII=\n", 68 | "text/plain": [ 69 | "" 70 | ] 71 | }, 72 | "metadata": {}, 73 | "output_type": "display_data" 74 | } 75 | ], 76 | "source": [ 77 | "#test the revenue random variable as a function of how right the param is\n", 78 | "N = 1000\n", 79 | "bins = 10\n", 80 | "d11=np.zeros(N)\n", 81 | "d12=np.zeros(N)\n", 82 | "d21=np.zeros(N)\n", 83 | "d110=np.zeros(N)\n", 84 | "d101=np.zeros(N)\n", 85 | "for i in range(N):\n", 86 | " d11[i] = revenue(1,1)\n", 87 | " d12[i] = revenue(1,2)\n", 88 | " d21[i] = revenue(2,1)\n", 89 | " d110[i] = revenue(1,10)\n", 90 | " d101[i] = revenue(10,1)\n", 91 | "\n", 92 | "plt.hist(d11, bins, alpha=0.5, label='d11')\n", 93 | "plt.hist(d12, bins, alpha=0.5, label='d12')\n", 94 | "plt.hist(d21, bins, alpha=0.5, label='d21')\n", 95 | "plt.hist(d110, bins, alpha=0.5, label='d110')\n", 96 | "plt.hist(d101, bins, alpha=0.5, label='d101')\n", 97 | "\n", 98 | "plt.legend(loc='upper right')\n", 99 | "plt.show()" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 4, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "#Lets assuming that all tokens in existence is given by T\n", 109 | "\n", 110 | "def bond_mint(eth, Eth, Tokens):\n", 111 | " return eth*Tokens/Eth\n", 112 | "\n", 113 | "def burn_withdraw(tokens, Eth, Tokens):\n", 114 | " return tokens*Eth/Tokens\n" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "#define a set of agents\n", 124 | "agents = {}\n", 125 | "\n", 126 | "#in this passive voting model agents maintain a belief or a preference as part of their state with respect\n", 127 | "#to the contract the 'token' came from\n", 128 | "#their belief or preference may be updated by them at will\n", 129 | "def add_agent(eth, tbparam, Eth, Tokens):\n", 130 | " agents[str(len(agents))] = {\"eth\":eth,\n", 131 | " \"tokens\":0,\n", 132 | " \"param_belief\": 2*tbparam*np.random.uniform(),\n", 133 | " \"value_belief\":2*Tokens/Eth*np.random.uniform()}\n", 134 | " \n", 135 | "\n", 136 | "def agent_acts(a, Eth, Tokens, r, p):\n", 137 | " #update value belief\n", 138 | " agents[a][\"value_belief\"] = 2*Tokens/Eth*np.random.uniform()\n", 139 | " \n", 140 | " #update tokens held -- buy or sell -- pretty naive with random belief and random amount\n", 141 | " if agents[a][\"value_belief\"]0:\n", 214 | " param[k] = votes[k]/count[k]\n", 215 | " else:\n", 216 | " param[k] = 1\n", 217 | " rev[k] = revenue(true_best_param[k],param[k])\n", 218 | " Eth = Eth+rev[k]\n", 219 | " \n", 220 | " #new agents join\n", 221 | " if np.random.uniform()< np.log10(K/k):\n", 222 | " new = int(2*np.random.uniform())\n", 223 | " for i in range(new):\n", 224 | " add_agent(gamma(5,1).rvs(), true_best_param[k], Eth, Tokens)\n", 225 | " n[k]=len(agents)\n", 226 | " \n", 227 | " #pick some to update on the order of log of agents\n", 228 | " active = random.sample(list(agents.keys()), int(np.log2(len(agents))))\n", 229 | " for a in active:\n", 230 | " Eth, Tokens, val = agent_acts(a, Eth, Tokens, rev[:k+1], param[:k+1])\n", 231 | " agents[a] = val\n", 232 | " \n", 233 | " A[k] = len(active)\n", 234 | " E[k] = Eth\n", 235 | " T[k] = Tokens\n", 236 | " " 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [ 245 | "k" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "plt.plot(range(K),n)" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": null, 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "plt.hist(rev)" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "plt.hist((true_best_param-param)/true_best_param, bins=(np.arange(-1,1,.1)), density=True, alpha=.5)\n", 273 | "plt.hist((true_best_param-param)/true_best_param, bins=(np.arange(-1,1,.1)), density=True, weights = rev, alpha=.25)" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": {}, 280 | "outputs": [], 281 | "source": [ 282 | "plt.semilogy(n, np.abs(true_best_param - param)/true_best_param, n, .1)\n", 283 | "plt.title(\"Logscale absolute percent error\")\n", 284 | "plt.labels([\"error\", \"10% reference\"])\n", 285 | "plt.xlabel(\"Number of Agents\")" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "metadata": {}, 292 | "outputs": [], 293 | "source": [ 294 | "plt.plot(range(K), true_best_param, range(K), param )" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [ 303 | "plt.plot(range(K), E/T)" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": {}, 310 | "outputs": [], 311 | "source": [ 312 | "plt.plot(range(K), E, range(K), T)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "plt.plot(E, T)" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "metadata": {}, 328 | "outputs": [], 329 | "source": [ 330 | "plt.scatter(param,rev)" 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "execution_count": null, 336 | "metadata": {}, 337 | "outputs": [], 338 | "source": [ 339 | "sns.kdeplot(param,rev)\n", 340 | "plt.axis([4,6,0,4])" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": null, 346 | "metadata": {}, 347 | "outputs": [], 348 | "source": [ 349 | "sns.jointplot(param, rev, kind=\"kde\", size=7, space=0, xlim=[4,6],ylim=[0,5])" 350 | ] 351 | }, 352 | { 353 | "cell_type": "code", 354 | "execution_count": null, 355 | "metadata": {}, 356 | "outputs": [], 357 | "source": [] 358 | } 359 | ], 360 | "metadata": { 361 | "kernelspec": { 362 | "display_name": "Python 3", 363 | "language": "python", 364 | "name": "python3" 365 | }, 366 | "language_info": { 367 | "codemirror_mode": { 368 | "name": "ipython", 369 | "version": 3 370 | }, 371 | "file_extension": ".py", 372 | "mimetype": "text/x-python", 373 | "name": "python", 374 | "nbconvert_exporter": "python", 375 | "pygments_lexer": "ipython3", 376 | "version": "3.6.4" 377 | } 378 | }, 379 | "nbformat": 4, 380 | "nbformat_minor": 2 381 | } 382 | -------------------------------------------------------------------------------- /.ipynb_checkpoints/abc_sim-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 2 6 | } 7 | -------------------------------------------------------------------------------- /.ipynb_checkpoints/funding campaign demo-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 2 6 | } 7 | -------------------------------------------------------------------------------- /CICecosubsystem.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/CICecosubsystem.jpeg -------------------------------------------------------------------------------- /CICecosystem.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/CICecosystem.jpeg -------------------------------------------------------------------------------- /CICinvariant.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/CICinvariant.jpeg -------------------------------------------------------------------------------- /GJMMEqSVef/Conviction Demo 1.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/Conviction Demo 1.mov -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig0.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig1.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig10.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig100.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig101.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig102.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig103.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig104.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig105.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig106.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig107.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig108.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig109.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig11.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig110.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig111.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig112.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig113.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig113.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig114.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig115.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig116.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig117.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig118.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig118.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig119.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig119.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig12.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig120.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig121.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig122.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig123.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig124.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig125.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig126.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig127.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig127.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig128.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig129.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig129.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig13.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig130.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig131.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig132.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig133.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig133.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig134.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig134.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig135.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig136.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig137.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig137.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig138.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig139.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig139.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig14.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig140.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig141.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig141.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig142.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig142.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig143.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig143.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig144.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig145.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig145.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig146.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig146.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig147.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig148.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig148.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig149.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig149.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig15.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig150.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig151.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig152.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig153.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig153.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig154.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig154.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig155.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig155.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig156.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig156.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig157.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig158.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig158.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig159.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig159.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig16.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig160.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig161.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig161.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig162.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig162.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig163.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig163.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig164.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig164.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig165.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig165.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig166.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig166.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig167.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig168.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig168.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig169.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig169.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig17.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig170.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig170.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig171.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig171.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig172.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig173.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig173.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig174.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig174.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig175.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig175.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig176.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig176.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig177.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig177.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig178.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig178.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig179.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig18.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig180.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig181.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig181.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig182.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig182.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig183.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig183.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig184.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig184.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig185.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig185.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig186.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig186.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig187.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig187.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig188.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig188.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig189.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig189.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig19.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig190.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig190.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig191.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig191.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig192.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig193.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig193.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig194.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig195.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig195.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig196.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig197.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig197.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig198.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig199.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig199.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig2.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig20.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig21.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig22.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig23.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig24.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig25.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig26.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig27.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig28.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig29.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig3.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig30.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig31.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig32.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig33.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig34.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig35.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig36.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig37.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig38.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig39.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig4.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig40.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig41.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig42.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig43.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig44.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig45.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig46.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig47.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig48.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig49.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig5.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig50.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig51.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig52.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig53.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig54.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig55.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig56.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig57.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig58.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig59.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig6.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig60.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig61.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig62.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig63.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig64.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig65.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig66.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig67.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig68.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig68.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig69.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig69.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig7.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig70.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig71.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig71.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig72.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig73.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig73.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig74.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig74.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig75.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig76.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig77.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig78.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig78.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig79.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig79.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig8.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig80.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig81.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig81.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig82.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig82.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig83.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig83.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig84.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig84.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig85.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig86.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig87.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig88.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig89.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig89.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig9.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig90.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig91.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig91.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig92.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig93.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig93.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig94.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig94.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig95.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig95.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig96.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig97.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig97.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig98.png -------------------------------------------------------------------------------- /GJMMEqSVef/GJMMEqSVef_fig99.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GJMMEqSVef/GJMMEqSVef_fig99.png -------------------------------------------------------------------------------- /GrassrootsEconomicsCICcontractconservation.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/GrassrootsEconomicsCICcontractconservation.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a repository for Research on Conviction voting schemes. 2 | 3 | ## What is Conviction Voting? 4 | Conviction Voting is a novel decision making process used to estimate real-time collective preference in a distributed work proposal system. Voters continuously express their preference by staking tokens in support of proposals they would like to see approved, with the conviction (i.e. weight) of their vote growing over time. Collective conviction accumulates until it reaches an algorithmically set threshold based on the proportion of funds requested by a proposal. When conviction accumulates past the threshold, the proposal passes and funds are released so work may begin. 5 | 6 | Conviction voting improves on traditional discrete voting processes by allowing participants to vote at any time, and eliminates the need for group consensus on every proposal. 7 | 8 | # Learn more 9 | [TL;DR Tweet Thread](https://twitter.com/commonsstack/status/1309211776425365510) 10 | [Conviction Voting: A Novel Continuous Decision Making Alternative to Governance](https://medium.com/commonsstack/conviction-voting-a-novel-continuous-decision-making-alternative-to-governance-62e215ad2b3d) 11 | [Understanding Real-Time ‘Vote Streaming’: Announcing the Conviction Voting cadCAD Model Release](https://medium.com/commonsstack/announcing-the-conviction-voting-cadcad-model-release-8e907ce67e4e) 12 | 13 | [cadCAD Model Walk through](https://www.youtube.com/watch?v=ZtVtBjcIlRw&t=77s) About [cadCAD](http://cadcad.org/) 14 | [Presentation](https://www.youtube.com/watch?v=r4-G18YNogw&list=PLy6j39s17_jQfcxhI-64Dt9ttyu1HGv4q) by CV Developer [Sem Brestels](https://github.com/sembrestels) 15 | Academic Paper [Social Sensor Fusion](https://github.com/BlockScience/conviction/blob/master/social-sensorfusion.pdf) 16 | 17 | ## About BlockScience 18 | BlockScience® is a complex systems engineering, R&D, and analytics firm. Our goal is to combine academic-grade research with advanced mathematical and computational engineering to design safe and resilient socio-technical systems. We provide engineering, design, and analytics services to a wide range of clients, including for-profit, non-profit, academic, and government organizations, and contribute to open-source research and software development. 19 | 20 | ## Follow & Subscribe 21 | [🐦Twitter](https://twitter.com/block_science) | [📚 Medium](https://medium.com/block-science) | [👻Blog](https://blockscience.ghost.io/) | [🎥 YouTube](https://www.youtube.com/c/BlockScience) | [👥 Linkedin](https://www.linkedin.com/company/blockscience/) 22 | 23 | ## Contact 24 | 25 | The above model was designed to be configurable and customizable for multiple use cases. If you are interested in using the model, supporting further development or have any other questions, reach out to us at **info@block.science**. 26 | 27 |
28 | 29 | ![img](https://i.imgur.com/ypZlPg9.png) 30 | -------------------------------------------------------------------------------- /__pycache__/bonding_curve_eq.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/__pycache__/bonding_curve_eq.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/conviction_helpers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/__pycache__/conviction_helpers.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/conviction_system_logic.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/__pycache__/conviction_system_logic.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/conviction_system_logic2.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/__pycache__/conviction_system_logic2.cpython-36.pyc -------------------------------------------------------------------------------- /__pycache__/conviction_system_logic3.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/__pycache__/conviction_system_logic3.cpython-36.pyc -------------------------------------------------------------------------------- /bonding_curve_eq.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | default_kappa= 2 4 | default_exit_tax = .02 5 | 6 | #value function for a given state (R,S) 7 | def invariant(R,S,kappa=default_kappa): 8 | 9 | return (S**kappa)/R 10 | 11 | #given a value function (parameterized by kappa) 12 | #and an invariant coeficient V0 13 | #return Supply S as a function of reserve R 14 | def reserve(S, V0, kappa=default_kappa): 15 | 16 | return (S**kappa)/V0 17 | 18 | #given a value function (parameterized by kappa) 19 | #and an invariant coeficient V0 20 | #return Supply S as a function of reserve R 21 | def supply(R, V0, kappa=default_kappa): 22 | return (V0*R)**(1/kappa) 23 | 24 | #given a value function (parameterized by kappa) 25 | #and an invariant coeficient V0 26 | #return a spot price P as a function of reserve R 27 | def spot_price(R, V0, kappa=default_kappa): 28 | return kappa*R**((kappa-1)/kappa)/V0**(1/kappa) 29 | 30 | #for a given state (R,S) 31 | #given a value function (parameterized by kappa) 32 | #and an invariant coeficient V0 33 | #deposit deltaR to Mint deltaS 34 | #with realized price deltaR/deltaS 35 | def mint(deltaR, R,S, V0, kappa=default_kappa): 36 | deltaS = (V0*(R+deltaR))**(1/kappa)-S 37 | if deltaS ==0: 38 | realized_price = spot_price(R+deltaR, V0, kappa) 39 | else: 40 | realized_price = deltaR/deltaS 41 | return deltaS, realized_price 42 | 43 | #for a given state (R,S) 44 | #given a value function (parameterized by kappa) 45 | #and an invariant coeficient V0 46 | #burn deltaS to Withdraw deltaR 47 | #with realized price deltaR/deltaS 48 | def withdraw(deltaS, R,S, V0, kappa=default_kappa): 49 | deltaR = R-((S-deltaS)**kappa)/V0 50 | if deltaS ==0: 51 | realized_price = spot_price(R+deltaR, V0, kappa) 52 | else: 53 | realized_price = deltaR/deltaS 54 | return deltaR, realized_price 55 | 56 | def withdraw_with_tax(deltaS, R,S, V0, exit_tax = default_exit_tax, kappa=default_kappa): 57 | deltaR = R-((S-deltaS)**kappa)/V0 58 | #print(deltaR) 59 | quantity_taxed = exit_tax*deltaR 60 | quantity_recieved = (1-exit_tax)*deltaR 61 | 62 | realized_price = quantity_recieved/deltaS 63 | 64 | return quantity_recieved, quantity_taxed, realized_price 65 | 66 | -------------------------------------------------------------------------------- /c_2014_zargham_etal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/c_2014_zargham_etal.pdf -------------------------------------------------------------------------------- /camp_logics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/camp_logics.png -------------------------------------------------------------------------------- /conviction_helpers.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from scipy.stats import expon, gamma 3 | import numpy as np 4 | from bonding_curve_eq import invariant,spot_price 5 | import matplotlib.pyplot as plt 6 | import matplotlib.colors as colors 7 | import matplotlib.cm as cmx 8 | 9 | #helper functions 10 | def get_nodes_by_type(g, node_type_selection): 11 | return [node for node in g.nodes if g.nodes[node]['type']== node_type_selection ] 12 | 13 | def get_edges_by_type(g, edge_type_selection): 14 | return [edge for edge in g.edges if g.edges[edge]['type']== edge_type_selection ] 15 | 16 | default_theta = .25 17 | default_initial_price = .1 18 | default_kappa = 2 19 | 20 | def conviction_order(network, proposals): 21 | 22 | ordered = sorted(proposals, key=lambda j:network.nodes[j]['conviction'] , reverse=True) 23 | 24 | return ordered 25 | 26 | 27 | def total_funds_given_total_supply(initial_supply, theta = default_theta, initial_price = default_initial_price): 28 | 29 | total_raise = initial_price*initial_supply 30 | 31 | total_funds = theta*total_raise 32 | 33 | return total_funds 34 | 35 | def initialize_bonding_curve(initial_supply, initial_price = default_initial_price, kappa =default_kappa, theta = default_theta): 36 | 37 | S = initial_supply 38 | total_raise = initial_price*S 39 | 40 | R = (1-theta)*total_raise 41 | 42 | V0 = invariant(R,S,kappa) 43 | 44 | initial_reserve = R 45 | 46 | hatch_price = spot_price(R, V0, kappa) 47 | 48 | return initial_reserve, V0, hatch_price 49 | 50 | #maximum share of funds a proposal can take 51 | default_beta = .2 #later we should set this to be param so we can sweep it 52 | # tuning param for the trigger function 53 | default_rho = .001 54 | 55 | def trigger_threshold(requested, funds, supply, beta = default_beta, rho = default_rho): 56 | 57 | share = requested/funds 58 | if share < beta: 59 | return rho*supply/(beta-share)**2 60 | else: 61 | return np.inf 62 | 63 | def initialize_network(n,m, funds_func=total_funds_given_total_supply, trigger_func =trigger_threshold, expected_supply = 10**6 ): 64 | network = nx.DiGraph() 65 | for i in range(n): 66 | network.add_node(i) 67 | network.nodes[i]['type']="participant" 68 | 69 | h_rv = expon.rvs(loc=0.0, scale= expected_supply/n) 70 | network.nodes[i]['holdings'] = h_rv 71 | 72 | s_rv = np.random.rand() 73 | network.nodes[i]['sentiment'] = s_rv 74 | 75 | participants = get_nodes_by_type(network, 'participant') 76 | initial_supply = np.sum([ network.nodes[i]['holdings'] for i in participants]) 77 | 78 | initial_funds = funds_func(initial_supply) 79 | 80 | #generate initial proposals 81 | for ind in range(m): 82 | j = n+ind 83 | network.add_node(j) 84 | network.nodes[j]['type']="proposal" 85 | network.nodes[j]['conviction']=0 86 | network.nodes[j]['status']='candidate' 87 | network.nodes[j]['age']=0 88 | 89 | r_rv = gamma.rvs(3,loc=0.001, scale=10000) 90 | network.nodes[j]['funds_requested'] = r_rv 91 | 92 | network.nodes[j]['trigger']= trigger_threshold(r_rv, initial_funds, initial_supply) 93 | 94 | for i in range(n): 95 | network.add_edge(i, j) 96 | 97 | rv = np.random.rand() 98 | a_rv = 1-4*(1-rv)*rv #polarized distribution 99 | network.edges[(i, j)]['affinity'] = a_rv 100 | network.edges[(i, j)]['tokens'] = 0 101 | network.edges[(i, j)]['conviction'] = 0 102 | network.edges[(i, j)]['type'] = 'support' 103 | 104 | proposals = get_nodes_by_type(network, 'proposal') 105 | total_requested = np.sum([ network.nodes[i]['funds_requested'] for i in proposals]) 106 | 107 | network = initial_conflict_network(network, rate = .25) 108 | network = initial_social_network(network, scale = 1) 109 | 110 | return network, initial_funds, initial_supply, total_requested 111 | 112 | def initial_social_network(network, scale = 1, sigmas=3): 113 | 114 | participants = get_nodes_by_type(network, 'participant') 115 | 116 | for i in participants: 117 | for j in participants: 118 | if not(j==i): 119 | influence_rv = expon.rvs(loc=0.0, scale=scale) 120 | if influence_rv > scale+sigmas*scale**2: 121 | network.add_edge(i,j) 122 | network.edges[(i,j)]['influence'] = influence_rv 123 | network.edges[(i,j)]['type'] = 'influence' 124 | return network 125 | 126 | def initial_conflict_network(network, rate = .25): 127 | 128 | proposals = get_nodes_by_type(network, 'proposal') 129 | 130 | for i in proposals: 131 | for j in proposals: 132 | if not(j==i): 133 | conflict_rv = np.random.rand() 134 | if conflict_rv < rate : 135 | network.add_edge(i,j) 136 | network.edges[(i,j)]['conflict'] = 1-conflict_rv 137 | network.edges[(i,j)]['type'] = 'conflict' 138 | return network 139 | 140 | def social_links(network, participant, scale = 1): 141 | 142 | participants = get_nodes_by_type(network, 'participant') 143 | 144 | i = participant 145 | for j in participants: 146 | if not(j==i): 147 | influence_rv = expon.rvs(loc=0.0, scale=scale) 148 | if influence_rv > scale+scale**2: 149 | network.add_edge(i,j) 150 | network.edges[(i,j)]['influence'] = influence_rv 151 | network.edges[(i,j)]['type'] = 'influence' 152 | return network 153 | 154 | def conflict_links(network,proposal ,rate = .25): 155 | 156 | proposals = get_nodes_by_type(network, 'proposal') 157 | 158 | i = proposal 159 | for j in proposals: 160 | if not(j==i): 161 | conflict_rv = np.random.rand() 162 | if conflict_rv < rate : 163 | network.add_edge(i,j) 164 | network.edges[(i,j)]['conflict'] = 1-conflict_rv 165 | network.edges[(i,j)]['type'] = 'conflict' 166 | return network 167 | 168 | def social_affinity_booster(network, proposal, participant): 169 | 170 | participants = get_nodes_by_type(network, 'participant') 171 | influencers = get_edges_by_type(network, 'influence') 172 | 173 | j=proposal 174 | i=participant 175 | 176 | i_tokens = network.nodes[i]['holdings'] 177 | 178 | influence = np.array([network.edges[(i,node)]['influence'] for node in participants if (i, node) in influencers ]) 179 | #print(influence) 180 | tokens = np.array([network.edges[(node,j)]['tokens'] for node in participants if (i, node) in influencers ]) 181 | #print(tokens) 182 | 183 | 184 | influence_sum = np.sum(influence) 185 | 186 | if influence_sum>0: 187 | boosts = np.sum(tokens*influence)/(influence_sum*i_tokens) 188 | else: 189 | boosts = 0 190 | 191 | return np.sum(boosts) 192 | 193 | 194 | def trigger_sweep(field, trigger_func,xmax=.2,default_alpha=.5): 195 | 196 | if field == 'token_supply': 197 | alpha = default_alpha 198 | share_of_funds = np.arange(.001,xmax,.001) 199 | total_supply = np.arange(0,10**9, 10**6) 200 | demo_data_XY = np.outer(share_of_funds,total_supply) 201 | 202 | demo_data_Z0=np.empty(demo_data_XY.shape) 203 | demo_data_Z1=np.empty(demo_data_XY.shape) 204 | demo_data_Z2=np.empty(demo_data_XY.shape) 205 | demo_data_Z3=np.empty(demo_data_XY.shape) 206 | for sof_ind in range(len(share_of_funds)): 207 | sof = share_of_funds[sof_ind] 208 | for ts_ind in range(len(total_supply)): 209 | ts = total_supply[ts_ind] 210 | tc = ts /(1-alpha) 211 | trigger = trigger_func(sof, 1, ts) 212 | demo_data_Z0[sof_ind,ts_ind] = np.log10(trigger) 213 | demo_data_Z1[sof_ind,ts_ind] = trigger 214 | demo_data_Z2[sof_ind,ts_ind] = trigger/tc #share of maximum possible conviction 215 | demo_data_Z3[sof_ind,ts_ind] = np.log10(trigger/tc) 216 | return {'log10_trigger':demo_data_Z0, 217 | 'trigger':demo_data_Z1, 218 | 'share_of_max_conv': demo_data_Z2, 219 | 'log10_share_of_max_conv':demo_data_Z3, 220 | 'total_supply':total_supply, 221 | 'share_of_funds':share_of_funds} 222 | elif field == 'alpha': 223 | alpha = np.arange(.5,1,.01) 224 | share_of_funds = np.arange(.001,xmax,.001) 225 | total_supply = 10**9 226 | demo_data_XY = np.outer(share_of_funds,alpha) 227 | 228 | demo_data_Z4=np.empty(demo_data_XY.shape) 229 | demo_data_Z5=np.empty(demo_data_XY.shape) 230 | demo_data_Z6=np.empty(demo_data_XY.shape) 231 | demo_data_Z7=np.empty(demo_data_XY.shape) 232 | for sof_ind in range(len(share_of_funds)): 233 | sof = share_of_funds[sof_ind] 234 | for a_ind in range(len(alpha)): 235 | ts = total_supply 236 | a = alpha[a_ind] 237 | tc = ts /(1-a) 238 | trigger = trigger_func(sof, 1, ts) 239 | demo_data_Z4[sof_ind,a_ind] = np.log10(trigger) 240 | demo_data_Z5[sof_ind,a_ind] = trigger 241 | demo_data_Z6[sof_ind,a_ind] = trigger/tc #share of maximum possible conviction 242 | demo_data_Z7[sof_ind,a_ind] = np.log10(trigger/tc) 243 | 244 | return {'log10_trigger':demo_data_Z4, 245 | 'trigger':demo_data_Z5, 246 | 'share_of_max_conv': demo_data_Z6, 247 | 'log10_share_of_max_conv':demo_data_Z7, 248 | 'alpha':alpha, 249 | 'share_of_funds':share_of_funds} 250 | 251 | else: 252 | return "invalid field" 253 | 254 | def trigger_plotter(share_of_funds,Z, color_label,y, ylabel,cmap='jet'): 255 | dims = (10, 5) 256 | fig, ax = plt.subplots(figsize=dims) 257 | 258 | cf = plt.contourf(share_of_funds, y, Z.T, 100, cmap=cmap) 259 | cbar=plt.colorbar(cf) 260 | plt.axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]]) 261 | #ax.set_xscale('log') 262 | plt.ylabel(ylabel) 263 | plt.xlabel('Share of Funds Requested') 264 | plt.title('Trigger Function Map') 265 | 266 | cbar.ax.set_ylabel(color_label) 267 | 268 | 269 | def snap_plot(nets, size_scale = 1/500, ani = False, dims = (20,20), savefigs=False): 270 | 271 | 272 | last_net = nets[-1] 273 | 274 | last_props=get_nodes_by_type(last_net, 'proposal') 275 | M = len(last_props) 276 | last_parts=get_nodes_by_type(last_net, 'participant') 277 | N = len(last_parts) 278 | pos = {} 279 | 280 | for ind in range(N): 281 | i = last_parts[ind] 282 | pos[i] = np.array([0, 2*ind-N]) 283 | 284 | for ind in range(M): 285 | j = last_props[ind] 286 | pos[j] = np.array([1, 2*N/M *ind-N]) 287 | 288 | if ani: 289 | figs = [] 290 | fig, ax = plt.subplots(figsize=dims) 291 | 292 | if savefigs: 293 | counter = 0 294 | length = 10 295 | import string 296 | unique_id = ''.join([np.random.choice(list(string.ascii_letters + string.digits)) for _ in range(length)]) 297 | for net in nets: 298 | edges = get_edges_by_type(net, 'support') 299 | max_tok = np.max([net.edges[e]['tokens'] for e in edges]) 300 | 301 | E = len(edges) 302 | 303 | net_props = get_nodes_by_type(net, 'proposal') 304 | net_parts = get_nodes_by_type(net, 'participant') 305 | net_node_label ={} 306 | 307 | num_nodes = len([node for node in net.nodes]) 308 | 309 | node_color = np.empty((num_nodes,4)) 310 | node_size = np.empty(num_nodes) 311 | 312 | edge_color = np.empty((E,4)) 313 | cm = plt.get_cmap('Reds') 314 | 315 | cNorm = colors.Normalize(vmin=0, vmax=max_tok) 316 | scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm) 317 | 318 | net_cand = [j for j in net_props if net.nodes[j]['status']=='candidate'] 319 | 320 | for j in net_props: 321 | node_size[j] = net.nodes[j]['funds_requested']*size_scale 322 | if net.nodes[j]['status']=="candidate": 323 | node_color[j] = colors.to_rgba('blue') 324 | trigger = net.nodes[j]['trigger'] 325 | conviction = net.nodes[j]['conviction'] 326 | percent_of_trigger = " "+str(int(100*conviction/trigger))+'%' 327 | net_node_label[j] = str(percent_of_trigger) 328 | elif net.nodes[j]['status']=="active": 329 | node_color[j] = colors.to_rgba('orange') 330 | net_node_label[j] = '' 331 | elif net.nodes[j]['status']=="completed": 332 | node_color[j] = colors.to_rgba('green') 333 | net_node_label[j] = '' 334 | elif net.nodes[j]['status']=="failed": 335 | node_color[j] = colors.to_rgba('gray') 336 | net_node_label[j] = '' 337 | elif net.nodes[j]['status']=="killed": 338 | node_color[j] = colors.to_rgba('black') 339 | net_node_label[j] = '' 340 | 341 | for i in net_parts: 342 | node_size[i] = net.nodes[i]['holdings']*size_scale/10 343 | node_color[i] = colors.to_rgba('red') 344 | net_node_label[i] = '' 345 | 346 | included_edges = [] 347 | for ind in range(E): 348 | e = edges[ind] 349 | tokens = net.edges[e]['tokens'] 350 | edge_color[ind] = scalarMap.to_rgba(tokens) 351 | if e[1] in net_cand: 352 | included_edges.append(e) 353 | 354 | 355 | iE = len(included_edges) 356 | included_edge_color = np.empty((iE,4)) 357 | for ind in range(iE): 358 | e = included_edges[ind] 359 | tokens = net.edges[e]['tokens'] 360 | included_edge_color[ind] = scalarMap.to_rgba(tokens) 361 | 362 | # nx.draw(net, 363 | # pos=pos, 364 | # node_size = node_size, 365 | # node_color = node_color, 366 | # edge_color = included_edge_color, 367 | # edgelist=included_edges, 368 | # labels = net_node_label) 369 | # plt.title('Tokens Staked by Partipants to Proposals') 370 | 371 | if ani: 372 | nx.draw(net, 373 | pos=pos, 374 | node_size = node_size, 375 | node_color = node_color, 376 | edge_color = included_edge_color, 377 | edgelist=included_edges, 378 | labels = net_node_label, ax=ax) 379 | figs.append(fig) 380 | 381 | else: 382 | nx.draw(net, 383 | pos=pos, 384 | node_size = node_size, 385 | node_color = node_color, 386 | edge_color = included_edge_color, 387 | edgelist=included_edges, 388 | labels = net_node_label) 389 | plt.title('Tokens Staked by Partipants to Proposals') 390 | if savefigs: 391 | plt.savefig(unique_id+'_fig'+str(counter)+'.png') 392 | counter = counter+1 393 | plt.show() 394 | 395 | if ani: 396 | False 397 | #anim = animation.ArtistAnimation(fig, , interval=50, blit=True, repeat_delay=1000) 398 | #plt.show() 399 | 400 | def pad(vec, length,fill=True): 401 | 402 | if fill: 403 | padded = np.zeros(length,) 404 | else: 405 | padded = np.empty(length,) 406 | padded[:] = np.nan 407 | 408 | for i in range(len(vec)): 409 | padded[i]= vec[i] 410 | 411 | return padded 412 | 413 | def make2D(key, data, fill=False): 414 | maxL = data[key].apply(len).max() 415 | newkey = 'padded_'+key 416 | data[newkey] = data[key].apply(lambda x: pad(x,maxL,fill)) 417 | reshaped = np.array([a for a in data[newkey].values]) 418 | 419 | return reshaped -------------------------------------------------------------------------------- /conviction_system_logic.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from conviction_helpers import get_nodes_by_type,get_edges_by_type, conflict_links, social_links 3 | #import networkx as nx 4 | from scipy.stats import expon, gamma 5 | 6 | 7 | #functions for partial state update block 1 8 | 9 | #Driving processes: arrival of participants, proposals and funds 10 | ##----------------------------------------- 11 | def gen_new_participant(network, new_participant_holdings): 12 | 13 | i = len([node for node in network.nodes]) 14 | 15 | network.add_node(i) 16 | network.nodes[i]['type']="participant" 17 | 18 | s_rv = np.random.rand() 19 | network.nodes[i]['sentiment'] = s_rv 20 | network.nodes[i]['holdings']=new_participant_holdings 21 | 22 | for j in get_nodes_by_type(network, 'proposal'): 23 | network.add_edge(i, j) 24 | 25 | rv = np.random.rand() 26 | a_rv = 1-4*(1-rv)*rv #polarized distribution 27 | network.edges[(i, j)]['affinity'] = a_rv 28 | network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] 29 | network.edges[(i, j)]['conviction'] = 0 30 | network.edges[(i,j)]['type'] = 'support' 31 | 32 | social_links(network, i) 33 | 34 | return network 35 | 36 | 37 | 38 | 39 | def gen_new_proposal(network, funds, supply, trigger_func, scale_factor = 1.0/10): 40 | 41 | 42 | 43 | j = len([node for node in network.nodes]) 44 | network.add_node(j) 45 | network.nodes[j]['type']="proposal" 46 | 47 | network.nodes[j]['conviction']=0 48 | network.nodes[j]['status']='candidate' 49 | network.nodes[j]['age']=0 50 | 51 | rescale = funds*scale_factor 52 | r_rv = gamma.rvs(3,loc=0.001, scale=rescale) 53 | network.node[j]['funds_requested'] = r_rv 54 | 55 | network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) 56 | 57 | participants = get_nodes_by_type(network, 'participant') 58 | proposing_participant = np.random.choice(participants) 59 | 60 | for i in participants: 61 | network.add_edge(i, j) 62 | if i==proposing_participant: 63 | network.edges[(i, j)]['affinity']=1 64 | else: 65 | rv = np.random.rand() 66 | a_rv = 1-4*(1-rv)*rv #polarized distribution 67 | network.edges[(i, j)]['affinity'] = a_rv 68 | 69 | network.edges[(i, j)]['conviction'] = 0 70 | network.edges[(i,j)]['tokens'] = 0 71 | network.edges[(i,j)]['type'] = 'support' 72 | 73 | network = conflict_links(network,j) 74 | 75 | return network 76 | 77 | 78 | 79 | def driving_process(params, step, sL, s): 80 | 81 | #placeholder plumbing for random processes 82 | arrival_rate = 10/s['sentiment'] 83 | rv1 = np.random.rand() 84 | new_participant = bool(rv1<1/arrival_rate) 85 | if new_participant: 86 | h_rv = expon.rvs(loc=0.0, scale=1000) 87 | new_participant_holdings = h_rv 88 | else: 89 | new_participant_holdings = 0 90 | 91 | network = s['network'] 92 | supporters = get_edges_by_type(network, 'support') 93 | affinities = [network.edges[e]['affinity'] for e in supporters ] 94 | median_affinity = np.median(affinities) 95 | 96 | proposals = get_nodes_by_type(network, 'proposal') 97 | fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] 98 | 99 | funds = s['funds'] 100 | total_funds_requested = np.sum(fund_requests) 101 | 102 | proposal_rate = 10/median_affinity * total_funds_requested/funds 103 | rv2 = np.random.rand() 104 | new_proposal = bool(rv2<1/proposal_rate) 105 | 106 | sentiment = s['sentiment'] 107 | funds = s['funds'] 108 | scale_factor = funds*sentiment**2/10000 109 | 110 | #this shouldn't happen but expon is throwing domain errors 111 | if sentiment>.4: 112 | funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) 113 | else: 114 | funds_arrival = 0 115 | 116 | return({'new_participant':new_participant, 117 | 'new_participant_holdings':new_participant_holdings, 118 | 'new_proposal':new_proposal, 119 | 'funds_arrival':funds_arrival}) 120 | 121 | 122 | #Mechanisms for updating the state based on driving processes 123 | ##--- 124 | def update_network(params, step, sL, s, _input): 125 | 126 | network = s['network'] 127 | funds = s['funds'] 128 | supply = s['supply'] 129 | trigger_func = params['trigger_func'] 130 | #print(trigger_func) 131 | 132 | new_participant = _input['new_participant'] #T/F 133 | new_proposal = _input['new_proposal'] #T/F 134 | 135 | if new_participant: 136 | new_participant_holdings = _input['new_participant_holdings'] 137 | network = gen_new_participant(network, new_participant_holdings) 138 | 139 | if new_proposal: 140 | network= gen_new_proposal(network,funds,supply,trigger_func ) 141 | 142 | #update age of the existing proposals 143 | proposals = get_nodes_by_type(network, 'proposal') 144 | 145 | for j in proposals: 146 | network.nodes[j]['age'] = network.nodes[j]['age']+1 147 | if network.nodes[j]['status'] == 'candidate': 148 | requested = network.nodes[j]['funds_requested'] 149 | network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) 150 | else: 151 | network.nodes[j]['trigger'] = np.nan 152 | 153 | key = 'network' 154 | value = network 155 | 156 | return (key, value) 157 | 158 | def increment_funds(params, step, sL, s, _input): 159 | 160 | funds = s['funds'] 161 | funds_arrival = _input['funds_arrival'] 162 | 163 | #increment funds 164 | funds = funds + funds_arrival 165 | 166 | key = 'funds' 167 | value = funds 168 | 169 | return (key, value) 170 | 171 | def increment_supply(params, step, sL, s, _input): 172 | 173 | supply = s['supply'] 174 | supply_arrival = _input['new_participant_holdings'] 175 | 176 | #increment funds 177 | supply = supply + supply_arrival 178 | 179 | key = 'supply' 180 | value = supply 181 | 182 | return (key, value) 183 | 184 | #functions for partial state update block 2 185 | 186 | #Driving processes: completion of previously funded proposals 187 | ##----------------------------------------- 188 | 189 | def check_progress(params, step, sL, s): 190 | 191 | network = s['network'] 192 | proposals = get_nodes_by_type(network, 'proposal') 193 | 194 | completed = [] 195 | for j in proposals: 196 | if network.nodes[j]['status'] == 'active': 197 | grant_size = network.nodes[j]['funds_requested'] 198 | base_completion_rate=params['base_completion_rate'] 199 | likelihood = 1.0/(base_completion_rate+np.log(grant_size)) 200 | if np.random.rand() < likelihood: 201 | completed.append(j) 202 | 203 | return({'completed':completed}) 204 | 205 | 206 | #Mechanisms for updating the state based on check progress 207 | ##--- 208 | def complete_proposal(params, step, sL, s, _input): 209 | 210 | network = s['network'] 211 | participants = get_nodes_by_type(network, 'participant') 212 | 213 | completed = _input['completed'] 214 | for j in completed: 215 | network.nodes[j]['status']='completed' 216 | for i in participants: 217 | force = network.edges[(i,j)]['affinity'] 218 | sentiment = network.node[i]['sentiment'] 219 | network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) 220 | 221 | key = 'network' 222 | value = network 223 | 224 | return (key, value) 225 | 226 | def update_sentiment_on_completion(params, step, sL, s, _input): 227 | 228 | network = s['network'] 229 | proposals = get_nodes_by_type(network, 'proposal') 230 | completed = _input['completed'] 231 | 232 | grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) 233 | 234 | grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) 235 | 236 | sentiment = s['sentiment'] 237 | 238 | force = grants_completed/grants_outstanding 239 | mu = params['sentiment_decay'] 240 | if (force >=0) and (force <=1): 241 | sentiment = get_sentimental(sentiment, force, mu) 242 | else: 243 | sentiment = get_sentimental(sentiment, 0, mu) 244 | 245 | 246 | key = 'sentiment' 247 | value = sentiment 248 | 249 | return (key, value) 250 | 251 | def get_sentimental(sentiment, force, decay=0): 252 | mu = decay 253 | sentiment = sentiment*(1-mu) + force 254 | 255 | if sentiment > 1: 256 | sentiment = 1 257 | 258 | return sentiment 259 | 260 | #functions for partial state update block 3 261 | 262 | #Decision processes: trigger function policy 263 | ##----------------------------------------- 264 | 265 | def trigger_function(params, step, sL, s): 266 | 267 | network = s['network'] 268 | funds = s['funds'] 269 | supply = s['supply'] 270 | proposals = get_nodes_by_type(network, 'proposal') 271 | tmin = params['tmin'] 272 | trigger_func = params['trigger_func'] 273 | 274 | accepted = [] 275 | triggers = {} 276 | for j in proposals: 277 | if network.nodes[j]['status'] == 'candidate': 278 | requested = network.nodes[j]['funds_requested'] 279 | age = network.nodes[j]['age'] 280 | threshold = trigger_func(requested, funds, supply) 281 | if age > tmin: 282 | conviction = network.nodes[j]['conviction'] 283 | if conviction >threshold: 284 | accepted.append(j) 285 | else: 286 | threshold = np.nan 287 | 288 | triggers[j] = threshold 289 | 290 | 291 | 292 | return({'accepted':accepted, 'triggers':triggers}) 293 | 294 | def decrement_funds(params, step, sL, s, _input): 295 | 296 | funds = s['funds'] 297 | network = s['network'] 298 | accepted = _input['accepted'] 299 | 300 | #decrement funds 301 | for j in accepted: 302 | funds = funds - network.nodes[j]['funds_requested'] 303 | 304 | key = 'funds' 305 | value = funds 306 | 307 | return (key, value) 308 | 309 | def update_proposals(params, step, sL, s, _input): 310 | 311 | network = s['network'] 312 | accepted = _input['accepted'] 313 | triggers = _input['triggers'] 314 | participants = get_nodes_by_type(network, 'participant') 315 | proposals = get_nodes_by_type(network, 'proposals') 316 | sensitivity = params['sensitivity'] 317 | 318 | for j in proposals: 319 | network.nodes[j]['trigger'] = triggers[j] 320 | 321 | #bookkeeping conviction and participant sentiment 322 | for j in accepted: 323 | network.nodes[j]['status']='active' 324 | network.nodes[j]['conviction']=np.nan 325 | #change status to active 326 | for i in participants: 327 | 328 | #operating on edge = (i,j) 329 | #reset tokens assigned to other candidates 330 | network.edges[(i,j)]['tokens']=0 331 | network.edges[(i,j)]['conviction'] = np.nan 332 | 333 | #update participants sentiments (positive or negative) 334 | affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] 335 | if len(affinities)>1: 336 | max_affinity = np.max(affinities) 337 | force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity 338 | else: 339 | force = 0 340 | 341 | #based on what their affinities to the accepted proposals 342 | network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) 343 | 344 | 345 | key = 'network' 346 | value = network 347 | 348 | return (key, value) 349 | 350 | def update_sentiment_on_release(params, step, sL, s, _input): 351 | 352 | network = s['network'] 353 | proposals = get_nodes_by_type(network, 'proposal') 354 | accepted = _input['accepted'] 355 | 356 | proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) 357 | 358 | proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) 359 | 360 | sentiment = s['sentiment'] 361 | force = proposals_accepted/proposals_outstanding 362 | if (force >=0) and (force <=1): 363 | sentiment = get_sentimental(sentiment, force, False) 364 | else: 365 | sentiment = get_sentimental(sentiment, 0, False) 366 | 367 | key = 'sentiment' 368 | value = sentiment 369 | 370 | return (key, value) 371 | 372 | def participants_decisions(params, step, sL, s): 373 | 374 | network = s['network'] 375 | participants = get_nodes_by_type(network, 'participant') 376 | proposals = get_nodes_by_type(network, 'proposal') 377 | candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] 378 | sensitivity = params['sensitivity'] 379 | 380 | gain = .01 381 | delta_holdings={} 382 | proposals_supported ={} 383 | for i in participants: 384 | force = network.nodes[i]['sentiment']-sensitivity 385 | delta_holdings[i] = network.nodes[i]['holdings']*gain*force 386 | 387 | support = [] 388 | for j in candidates: 389 | affinity = network.edges[(i, j)]['affinity'] 390 | cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates]) 391 | if cutoff <.5: 392 | cutoff = .5 393 | 394 | if affinity > cutoff: 395 | support.append(j) 396 | 397 | proposals_supported[i] = support 398 | 399 | return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) 400 | 401 | def update_tokens(params, step, sL, s, _input): 402 | 403 | network = s['network'] 404 | delta_holdings = _input['delta_holdings'] 405 | proposals = get_nodes_by_type(network, 'proposal') 406 | proposals_supported = _input['proposals_supported'] 407 | participants = get_nodes_by_type(network, 'participant') 408 | alpha = params['alpha'] 409 | 410 | for i in participants: 411 | network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] 412 | supported = proposals_supported[i] 413 | total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) 414 | for j in proposals: 415 | if j in supported: 416 | normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity 417 | network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] 418 | else: 419 | network.edges[(i, j)]['tokens'] = 0 420 | 421 | prior_conviction = network.edges[(i, j)]['conviction'] 422 | current_tokens = network.edges[(i, j)]['tokens'] 423 | network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction 424 | 425 | for j in proposals: 426 | network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) 427 | 428 | key = 'network' 429 | value = network 430 | 431 | return (key, value) 432 | 433 | def update_supply(params, step, sL, s, _input): 434 | 435 | supply = s['supply'] 436 | delta_holdings = _input['delta_holdings'] 437 | delta_supply = np.sum([v for v in delta_holdings.values()]) 438 | 439 | supply = supply + delta_supply 440 | 441 | key = 'supply' 442 | value = supply 443 | 444 | return (key, value) -------------------------------------------------------------------------------- /conviction_system_logic2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from conviction_helpers import get_nodes_by_type, get_edges_by_type, social_affinity_booster 3 | #import networkx as nx 4 | from scipy.stats import expon, gamma 5 | 6 | 7 | #functions for partial state update block 1 8 | 9 | #Driving processes: arrival of participants, proposals and funds 10 | ##----------------------------------------- 11 | def gen_new_participant(network, new_participant_holdings): 12 | 13 | i = len([node for node in network.nodes]) 14 | 15 | network.add_node(i) 16 | network.nodes[i]['type']="participant" 17 | 18 | s_rv = np.random.rand() 19 | network.nodes[i]['sentiment'] = s_rv 20 | network.nodes[i]['holdings']=new_participant_holdings 21 | 22 | for j in get_nodes_by_type(network, 'proposal'): 23 | network.add_edge(i, j) 24 | 25 | rv = np.random.rand() 26 | a_rv = 1-4*(1-rv)*rv #polarized distribution 27 | network.edges[(i, j)]['affinity'] = a_rv 28 | network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] 29 | network.edges[(i, j)]['conviction'] = 0 30 | network.edges[(i,j)]['type'] = 'support' 31 | 32 | return network 33 | 34 | 35 | 36 | 37 | def gen_new_proposal(network, funds, supply, trigger_func, scale_factor = 1.0/100): 38 | 39 | 40 | 41 | j = len([node for node in network.nodes]) 42 | network.add_node(j) 43 | network.nodes[j]['type']="proposal" 44 | 45 | network.nodes[j]['conviction']=0 46 | network.nodes[j]['status']='candidate' 47 | network.nodes[j]['age']=0 48 | 49 | rescale = funds*scale_factor 50 | r_rv = gamma.rvs(3,loc=0.001, scale=rescale) 51 | network.node[j]['funds_requested'] = r_rv 52 | 53 | network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) 54 | 55 | participants = get_nodes_by_type(network, 'participant') 56 | proposing_participant = np.random.choice(participants) 57 | 58 | for i in participants: 59 | network.add_edge(i, j) 60 | if i==proposing_participant: 61 | network.edges[(i, j)]['affinity']=1 62 | else: 63 | rv = np.random.rand() 64 | a_rv = 1-4*(1-rv)*rv #polarized distribution 65 | network.edges[(i, j)]['affinity'] = a_rv 66 | 67 | network.edges[(i, j)]['conviction'] = 0 68 | network.edges[(i,j)]['tokens'] = 0 69 | network.edges[(i,j)]['type'] = 'support' 70 | 71 | return network 72 | 73 | 74 | 75 | def driving_process(params, step, sL, s): 76 | 77 | #placeholder plumbing for random processes 78 | arrival_rate = 10/s['sentiment'] 79 | rv1 = np.random.rand() 80 | new_participant = bool(rv1<1/arrival_rate) 81 | supporters = get_edges_by_type(s['network'], 'support') 82 | if new_participant: 83 | h_rv = expon.rvs(loc=0.0, scale=1000) 84 | new_participant_holdings = h_rv 85 | else: 86 | new_participant_holdings = 0 87 | 88 | network = s['network'] 89 | affinities = [network.edges[e]['affinity'] for e in supporters ] 90 | median_affinity = np.median(affinities) 91 | 92 | proposals = get_nodes_by_type(network, 'proposal') 93 | fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] 94 | 95 | funds = s['funds'] 96 | total_funds_requested = np.sum(fund_requests) 97 | 98 | proposal_rate = 10/median_affinity * total_funds_requested/funds 99 | rv2 = np.random.rand() 100 | new_proposal = bool(rv2<1/proposal_rate) 101 | 102 | sentiment = s['sentiment'] 103 | funds = s['funds'] 104 | scale_factor = funds*sentiment**2/10000 105 | 106 | #this shouldn't happen but expon is throwing domain errors 107 | if sentiment>.4: 108 | funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) 109 | else: 110 | funds_arrival = 0 111 | 112 | return({'new_participant':new_participant, 113 | 'new_participant_holdings':new_participant_holdings, 114 | 'new_proposal':new_proposal, 115 | 'funds_arrival':funds_arrival}) 116 | 117 | 118 | #Mechanisms for updating the state based on driving processes 119 | ##--- 120 | def update_network(params, step, sL, s, _input): 121 | 122 | network = s['network'] 123 | funds = s['funds'] 124 | supply = s['supply'] 125 | trigger_func = params['trigger_func'] 126 | #print(trigger_func) 127 | 128 | new_participant = _input['new_participant'] #T/F 129 | new_proposal = _input['new_proposal'] #T/F 130 | 131 | if new_participant: 132 | new_participant_holdings = _input['new_participant_holdings'] 133 | network = gen_new_participant(network, new_participant_holdings) 134 | 135 | if new_proposal: 136 | network= gen_new_proposal(network,funds,supply,trigger_func ) 137 | 138 | #update age of the existing proposals 139 | proposals = get_nodes_by_type(network, 'proposal') 140 | 141 | for j in proposals: 142 | network.nodes[j]['age'] = network.nodes[j]['age']+1 143 | if network.nodes[j]['status'] == 'candidate': 144 | requested = network.nodes[j]['funds_requested'] 145 | network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) 146 | else: 147 | network.nodes[j]['trigger'] = np.nan 148 | 149 | key = 'network' 150 | value = network 151 | 152 | return (key, value) 153 | 154 | def increment_funds(params, step, sL, s, _input): 155 | 156 | funds = s['funds'] 157 | funds_arrival = _input['funds_arrival'] 158 | 159 | #increment funds 160 | funds = funds + funds_arrival 161 | 162 | key = 'funds' 163 | value = funds 164 | 165 | return (key, value) 166 | 167 | def increment_supply(params, step, sL, s, _input): 168 | 169 | supply = s['supply'] 170 | supply_arrival = _input['new_participant_holdings'] 171 | 172 | #increment funds 173 | supply = supply + supply_arrival 174 | 175 | key = 'supply' 176 | value = supply 177 | 178 | return (key, value) 179 | 180 | #functions for partial state update block 2 181 | 182 | #Driving processes: completion of previously funded proposals 183 | ##----------------------------------------- 184 | 185 | def check_progress(params, step, sL, s): 186 | 187 | network = s['network'] 188 | proposals = get_nodes_by_type(network, 'proposal') 189 | 190 | completed = [] 191 | failed = [] 192 | for j in proposals: 193 | if network.nodes[j]['status'] == 'active': 194 | grant_size = network.nodes[j]['funds_requested'] 195 | base_completion_rate=params['base_completion_rate'] 196 | likelihood = 1.0/(base_completion_rate+np.log(grant_size)) 197 | 198 | base_failure_rate = params['base_failure_rate'] 199 | failure_rate = 1.0/(base_failure_rate+np.log(grant_size)) 200 | if np.random.rand() < likelihood: 201 | completed.append(j) 202 | elif np.random.rand() < failure_rate: 203 | failed.append(j) 204 | 205 | return({'completed':completed, 'failed':failed}) 206 | 207 | 208 | #Mechanisms for updating the state based on check progress 209 | ##--- 210 | def complete_proposal(params, step, sL, s, _input): 211 | 212 | network = s['network'] 213 | participants = get_nodes_by_type(network, 'participant') 214 | proposals = get_nodes_by_type(network, 'proposal') 215 | competitors = get_edges_by_type(network, 'conflict') 216 | 217 | completed = _input['completed'] 218 | for j in completed: 219 | network.nodes[j]['status']='completed' 220 | 221 | for c in proposals: 222 | if (j,c) in competitors: 223 | conflict = network.edges[(j,c)]['conflict'] 224 | for i in participants: 225 | network.edges[(i,c)]['affinity'] = network.edges[(i,c)]['affinity'] *(1-conflict) 226 | 227 | for i in participants: 228 | force = network.edges[(i,j)]['affinity'] 229 | sentiment = network.node[i]['sentiment'] 230 | network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) 231 | 232 | 233 | 234 | failed = _input['failed'] 235 | for j in failed: 236 | network.nodes[j]['status']='failed' 237 | for i in participants: 238 | force = -network.edges[(i,j)]['affinity'] 239 | sentiment = network.node[i]['sentiment'] 240 | network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) 241 | 242 | key = 'network' 243 | value = network 244 | 245 | return (key, value) 246 | 247 | def update_sentiment_on_completion(params, step, sL, s, _input): 248 | 249 | network = s['network'] 250 | proposals = get_nodes_by_type(network, 'proposal') 251 | completed = _input['completed'] 252 | failed = _input['failed'] 253 | 254 | grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) 255 | 256 | grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) 257 | grants_failed = np.sum([network.nodes[j]['funds_requested'] for j in failed]) 258 | 259 | sentiment = s['sentiment'] 260 | 261 | if grants_outstanding>0: 262 | force = (grants_completed-grants_failed)/grants_outstanding 263 | else: 264 | force=1 265 | mu = params['sentiment_decay'] 266 | if (force >=0) and (force <=1): 267 | sentiment = get_sentimental(sentiment, force, mu) 268 | else: 269 | sentiment = get_sentimental(sentiment, 0, mu) 270 | 271 | 272 | key = 'sentiment' 273 | value = sentiment 274 | 275 | return (key, value) 276 | 277 | def get_sentimental(sentiment, force, decay=0): 278 | mu = decay 279 | sentiment = sentiment*(1-mu) + force 280 | 281 | if sentiment > 1: 282 | sentiment = 1 283 | 284 | return sentiment 285 | 286 | #functions for partial state update block 3 287 | 288 | #Decision processes: trigger function policy 289 | ##----------------------------------------- 290 | 291 | def trigger_function(params, step, sL, s): 292 | 293 | network = s['network'] 294 | funds = s['funds'] 295 | supply = s['supply'] 296 | proposals = get_nodes_by_type(network, 'proposal') 297 | tmin = params['tmin'] 298 | trigger_func = params['trigger_func'] 299 | 300 | accepted = [] 301 | triggers = {} 302 | for j in proposals: 303 | if network.nodes[j]['status'] == 'candidate': 304 | requested = network.nodes[j]['funds_requested'] 305 | age = network.nodes[j]['age'] 306 | threshold = trigger_func(requested, funds, supply) 307 | if age > tmin: 308 | conviction = network.nodes[j]['conviction'] 309 | if conviction >threshold: 310 | accepted.append(j) 311 | else: 312 | threshold = np.nan 313 | 314 | triggers[j] = threshold 315 | 316 | return({'accepted':accepted, 'triggers':triggers}) 317 | 318 | #functions for partial state update block 3 319 | 320 | #state updates 321 | ##--- 322 | 323 | def decrement_funds(params, step, sL, s, _input): 324 | 325 | funds = s['funds'] 326 | network = s['network'] 327 | accepted = _input['accepted'] 328 | 329 | #decrement funds 330 | for j in accepted: 331 | funds = funds - network.nodes[j]['funds_requested'] 332 | 333 | key = 'funds' 334 | value = funds 335 | 336 | return (key, value) 337 | 338 | def update_proposals(params, step, sL, s, _input): 339 | 340 | network = s['network'] 341 | accepted = _input['accepted'] 342 | triggers = _input['triggers'] 343 | participants = get_nodes_by_type(network, 'participant') 344 | proposals = get_nodes_by_type(network, 'proposals') 345 | sensitivity = params['sensitivity'] 346 | 347 | for j in proposals: 348 | network.nodes[j]['trigger'] = triggers[j] 349 | 350 | #bookkeeping conviction and participant sentiment 351 | for j in accepted: 352 | network.nodes[j]['status']='active' 353 | network.nodes[j]['conviction']=np.nan 354 | #change status to active 355 | for i in participants: 356 | 357 | #operating on edge = (i,j) 358 | #reset tokens assigned to other candidates 359 | network.edges[(i,j)]['tokens']=0 360 | network.edges[(i,j)]['conviction'] = np.nan 361 | 362 | #update participants sentiments (positive or negative) 363 | affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] 364 | if len(affinities)>1: 365 | max_affinity = np.max(affinities) 366 | force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity 367 | else: 368 | force = 0 369 | 370 | #based on what their affinities to the accepted proposals 371 | network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) 372 | 373 | 374 | key = 'network' 375 | value = network 376 | 377 | return (key, value) 378 | 379 | def update_sentiment_on_release(params, step, sL, s, _input): 380 | 381 | network = s['network'] 382 | proposals = get_nodes_by_type(network, 'proposal') 383 | accepted = _input['accepted'] 384 | 385 | proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) 386 | 387 | proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) 388 | 389 | sentiment = s['sentiment'] 390 | force = proposals_accepted/proposals_outstanding 391 | if (force >=0) and (force <=1): 392 | sentiment = get_sentimental(sentiment, force, False) 393 | else: 394 | sentiment = get_sentimental(sentiment, 0, False) 395 | 396 | key = 'sentiment' 397 | value = sentiment 398 | 399 | return (key, value) 400 | 401 | 402 | #functions for partial state update block 4 403 | 404 | #Decision processes: trigger function policy 405 | ##--- 406 | def participants_decisions(params, step, sL, s): 407 | 408 | network = s['network'] 409 | participants = get_nodes_by_type(network, 'participant') 410 | proposals = get_nodes_by_type(network, 'proposal') 411 | candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] 412 | sensitivity = params['sensitivity'] 413 | 414 | gain = .01 415 | delta_holdings={} 416 | proposals_supported ={} 417 | for i in participants: 418 | 419 | engagement_rate = .3*network.nodes[i]['sentiment'] 420 | if np.random.rand() cutoff: 433 | support.append(j) 434 | 435 | proposals_supported[i] = support 436 | else: 437 | delta_holdings[i] = 0 438 | proposals_supported[i] = [j for j in candidates if network.edges[(i,j)]['tokens']>0 ] 439 | 440 | return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) 441 | 442 | #functions for partial state update block 4 443 | 444 | #state updates 445 | ##--- 446 | 447 | def update_tokens(params, step, sL, s, _input): 448 | 449 | network = s['network'] 450 | delta_holdings = _input['delta_holdings'] 451 | proposals = get_nodes_by_type(network, 'proposal') 452 | candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] 453 | proposals_supported = _input['proposals_supported'] 454 | participants = get_nodes_by_type(network, 'participant') 455 | alpha = params['alpha'] 456 | min_support = params['min_supp'] 457 | 458 | for i in participants: 459 | network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] 460 | supported = proposals_supported[i] 461 | total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) 462 | for j in candidates: 463 | if j in supported: 464 | normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity 465 | network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] 466 | else: 467 | network.edges[(i, j)]['tokens'] = 0 468 | 469 | prior_conviction = network.edges[(i, j)]['conviction'] 470 | current_tokens = network.edges[(i, j)]['tokens'] 471 | network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction 472 | 473 | for j in candidates: 474 | network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) 475 | total_tokens = np.sum([network.edges[(i, j)]['tokens'] for i in participants ]) 476 | if total_tokens < min_support: 477 | network.nodes[j]['status'] = 'killed' 478 | 479 | key = 'network' 480 | value = network 481 | 482 | return (key, value) 483 | 484 | def update_supply(params, step, sL, s, _input): 485 | 486 | supply = s['supply'] 487 | delta_holdings = _input['delta_holdings'] 488 | delta_supply = np.sum([v for v in delta_holdings.values()]) 489 | 490 | supply = supply + delta_supply 491 | 492 | key = 'supply' 493 | value = supply 494 | 495 | return (key, value) -------------------------------------------------------------------------------- /funding campaign demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 18, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import numpy as np\n", 11 | "from datetime import datetime, timedelta" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 19, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "#campaigns\n", 21 | "init_date = datetime.now()\n", 22 | "\n", 23 | "campaigns = {1:{'name': 'ABC field test',\n", 24 | " 'multiplier': 2.5,\n", 25 | " 'min_raise': 984.0, \n", 26 | " 'max_raise': 1260.0, \n", 27 | " 'cutoff_date': init_date+timedelta(days=365*2/12.0),\n", 28 | " 'expected_time': timedelta(days=365*8/12.0)},\n", 29 | " 2:{'name': 'Giveth field test',\n", 30 | " 'multiplier': 2.0,\n", 31 | " 'min_raise': 726.0, \n", 32 | " 'max_raise': 940.0, \n", 33 | " 'cutoff_date': init_date+timedelta(days=365*4/12),\n", 34 | " 'expected_time': timedelta(days=365*6/12.0)},\n", 35 | " 3:{'name': 'CV field test',\n", 36 | " 'multiplier': 1.5,\n", 37 | " 'min_raise': 1140.0, \n", 38 | " 'max_raise': 1460.0, \n", 39 | " 'cutoff_date': init_date+timedelta(days=365*6/12),\n", 40 | " 'expected_time': timedelta(days=365*12/12)},\n", 41 | " 4:{'name': 'Analytics',\n", 42 | " 'multiplier': 1.25,\n", 43 | " 'min_raise': 780.0, \n", 44 | " 'max_raise': 975.0, \n", 45 | " 'cutoff_date': init_date+timedelta(days=365*4/12.0),\n", 46 | " 'expected_time': timedelta(days=365*16/12.0)},\n", 47 | " 5:{'name': 'Mobile first',\n", 48 | " 'multiplier': 1.125,\n", 49 | " 'min_raise': 1610.0, \n", 50 | " 'max_raise': 2010.0, \n", 51 | " 'cutoff_date': init_date+timedelta(days=365*8/12.0),\n", 52 | " 'expected_time': timedelta(days=365*24/12.0)},\n", 53 | " 6:{'name': 'Easy Deploy',\n", 54 | " 'multiplier': 1.0,\n", 55 | " 'min_raise': 1500.0, \n", 56 | " 'max_raise': 1875.0, \n", 57 | " 'cutoff_date': init_date+timedelta(days=365*6/12.0),\n", 58 | " 'expected_time': timedelta(days=365*30/12.0)},\n", 59 | " \n", 60 | "}" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 20, 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "data": { 70 | "text/plain": [ 71 | "1875.0" 72 | ] 73 | }, 74 | "execution_count": 20, 75 | "metadata": {}, 76 | "output_type": "execute_result" 77 | } 78 | ], 79 | "source": [ 80 | "1500*1.25" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 28, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "total_min = sum([campaigns[i]['min_raise'] for i in campaigns.keys()])\n", 90 | "total_max = sum([campaigns[i]['max_raise'] for i in campaigns.keys()])\n", 91 | "\n", 92 | "total_first3_at_max = sum([campaigns[i]['max_raise'] for i in campaigns.keys()if i<4] )\n", 93 | "total_first3_at_min = sum([campaigns[i]['min_raise'] for i in campaigns.keys()if i<4] )\n", 94 | "total_first4_at_min = sum([campaigns[i]['min_raise'] for i in campaigns.keys()if i<5] )" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 22, 100 | "metadata": {}, 101 | "outputs": [ 102 | { 103 | "name": "stdout", 104 | "output_type": "stream", 105 | "text": [ 106 | "smallest outcome: 6740.0\n" 107 | ] 108 | } 109 | ], 110 | "source": [ 111 | "print(\"smallest outcome: \"+str(total_min))" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 23, 117 | "metadata": {}, 118 | "outputs": [ 119 | { 120 | "name": "stdout", 121 | "output_type": "stream", 122 | "text": [ 123 | "largest outcome: 8520.0\n" 124 | ] 125 | } 126 | ], 127 | "source": [ 128 | "print(\"largest outcome: \"+str(total_max))" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 24, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "name": "stdout", 138 | "output_type": "stream", 139 | "text": [ 140 | "outcome at first 3 full amount: 3660.0\n" 141 | ] 142 | } 143 | ], 144 | "source": [ 145 | "print(\"outcome at first 3 full amount: \"+str(total_first3_at_max))" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 29, 151 | "metadata": {}, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "buffer for first 3 given first 3 full amount: 1.2842105263157895\n" 158 | ] 159 | } 160 | ], 161 | "source": [ 162 | "print(\"buffer for first 3 given first 3 full amount: \"+str(total_first3_at_max/total_first3_at_min))" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 25, 168 | "metadata": {}, 169 | "outputs": [ 170 | { 171 | "name": "stdout", 172 | "output_type": "stream", 173 | "text": [ 174 | "share of total budget covered by capping first 3: 0.543026706231454\n" 175 | ] 176 | } 177 | ], 178 | "source": [ 179 | "print(\"share of total budget covered by capping first 3: \"+str(total_first3_at_max/total_min))" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 26, 185 | "metadata": {}, 186 | "outputs": [ 187 | { 188 | "name": "stdout", 189 | "output_type": "stream", 190 | "text": [ 191 | "share of 4 iteration budget covered by capping first 3: 1.0082644628099173\n" 192 | ] 193 | } 194 | ], 195 | "source": [ 196 | "print(\"share of 4 iteration budget covered by capping first 3: \"+str(total_first3_at_max/total_first4_at_min))" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 30, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "multi_mins = [campaigns[i]['multiplier']*campaigns[i]['min_raise'] for i in campaigns.keys()]\n", 206 | "multi_maxs = [campaigns[i]['multiplier']*campaigns[i]['max_raise'] for i in campaigns.keys()]" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 36, 212 | "metadata": {}, 213 | "outputs": [ 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "[2460.0, 1452.0, 1710.0, 975.0, 1811.25, 1500.0]" 218 | ] 219 | }, 220 | "execution_count": 36, 221 | "metadata": {}, 222 | "output_type": "execute_result" 223 | } 224 | ], 225 | "source": [ 226 | "multi_mins" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": 38, 232 | "metadata": {}, 233 | "outputs": [ 234 | { 235 | "name": "stdout", 236 | "output_type": "stream", 237 | "text": [ 238 | "hatch allocations after first 3 at min:4286.25\n", 239 | "hatch allocations after first 6 at min:9908.25\n", 240 | "hatch allocations after first 3 at max:5355.0\n", 241 | "hatch allocations after first 6 at max:12575.0\n" 242 | ] 243 | } 244 | ], 245 | "source": [ 246 | "#hatch allocations after first 3 at min\n", 247 | "print(\"hatch allocations after first 3 at min:\"+str(sum(multi_mins[3:])) )\n", 248 | "\n", 249 | "#hatch allocations after all 6 at min\n", 250 | "print(\"hatch allocations after first 6 at min:\"+str(sum(multi_mins)) )\n", 251 | "\n", 252 | "#hatch allocations after first 3 at max\n", 253 | "print(\"hatch allocations after first 3 at max:\"+str(sum(multi_maxs[3:])) )\n", 254 | "\n", 255 | "#hatch allocations after all 6 at max\n", 256 | "print(\"hatch allocations after first 6 at max:\"+str(sum(multi_maxs)) )" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 8, 262 | "metadata": {}, 263 | "outputs": [ 264 | { 265 | "data": { 266 | "text/plain": [ 267 | "{1: {'cutoff_date': datetime.datetime(2019, 9, 24, 7, 21, 10, 221483),\n", 268 | " 'expected_time': datetime.timedelta(243, 28800),\n", 269 | " 'max_raise': 1500.0,\n", 270 | " 'min_raise': 984.0,\n", 271 | " 'multiplier': 2.5,\n", 272 | " 'name': 'ABC field test'},\n", 273 | " 2: {'cutoff_date': datetime.datetime(2019, 11, 24, 3, 21, 10, 221483),\n", 274 | " 'expected_time': datetime.timedelta(182, 43200),\n", 275 | " 'max_raise': 1500.0,\n", 276 | " 'min_raise': 726.0,\n", 277 | " 'multiplier': 2.0,\n", 278 | " 'name': 'Giveth field test'},\n", 279 | " 3: {'cutoff_date': datetime.datetime(2020, 1, 23, 23, 21, 10, 221483),\n", 280 | " 'expected_time': datetime.timedelta(365),\n", 281 | " 'max_raise': 2000.0,\n", 282 | " 'min_raise': 1140.0,\n", 283 | " 'multiplier': 1.5,\n", 284 | " 'name': 'CV field test'},\n", 285 | " 4: {'cutoff_date': datetime.datetime(2019, 11, 24, 3, 21, 10, 221483),\n", 286 | " 'expected_time': datetime.timedelta(486, 57600),\n", 287 | " 'max_raise': 1500.0,\n", 288 | " 'min_raise': 780.0,\n", 289 | " 'multiplier': 1.25,\n", 290 | " 'name': 'Analytics'},\n", 291 | " 5: {'cutoff_date': datetime.datetime(2020, 3, 24, 19, 21, 10, 221483),\n", 292 | " 'expected_time': datetime.timedelta(730),\n", 293 | " 'max_raise': 2500.0,\n", 294 | " 'min_raise': 1610.0,\n", 295 | " 'multiplier': 1.125,\n", 296 | " 'name': 'Mobile first'},\n", 297 | " 6: {'cutoff_date': datetime.datetime(2020, 1, 23, 23, 21, 10, 221483),\n", 298 | " 'expected_time': datetime.timedelta(912, 43200),\n", 299 | " 'max_raise': 3000.0,\n", 300 | " 'min_raise': 1500.0,\n", 301 | " 'multiplier': 1.0,\n", 302 | " 'name': 'Easy Deploy'}}" 303 | ] 304 | }, 305 | "execution_count": 8, 306 | "metadata": {}, 307 | "output_type": "execute_result" 308 | } 309 | ], 310 | "source": [ 311 | "campaigns" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": 9, 317 | "metadata": {}, 318 | "outputs": [], 319 | "source": [ 320 | "tranches = { tr:{'raised':0, 'status':'not_started'} for tr in range(2,7)}\n", 321 | "tranches[1] = {'raised':0, 'status':'active'}\n", 322 | "state = {'new_raised':0,'total_raised': 0, 'active_tranche':1,'tranches': tranches}" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": 10, 328 | "metadata": {}, 329 | "outputs": [ 330 | { 331 | "data": { 332 | "text/plain": [ 333 | "{'active_tranche': 1,\n", 334 | " 'new_raised': 0,\n", 335 | " 'total_raised': 0,\n", 336 | " 'tranches': {1: {'raised': 0, 'status': 'active'},\n", 337 | " 2: {'raised': 0, 'status': 'not_started'},\n", 338 | " 3: {'raised': 0, 'status': 'not_started'},\n", 339 | " 4: {'raised': 0, 'status': 'not_started'},\n", 340 | " 5: {'raised': 0, 'status': 'not_started'},\n", 341 | " 6: {'raised': 0, 'status': 'not_started'}}}" 342 | ] 343 | }, 344 | "execution_count": 10, 345 | "metadata": {}, 346 | "output_type": "execute_result" 347 | } 348 | ], 349 | "source": [ 350 | "state" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 11, 356 | "metadata": {}, 357 | "outputs": [], 358 | "source": [ 359 | "T = 52*3\n", 360 | "dt = timedelta(weeks =1)\n", 361 | "\n", 362 | "def gen_raise(thresh = .3, scale = 10):\n", 363 | " rv = np.random.rand()\n", 364 | " if rv < thresh:\n", 365 | " return 0\n", 366 | " else :\n", 367 | " return rv*scale" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": {}, 373 | "source": [ 374 | "" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": 12, 380 | "metadata": {}, 381 | "outputs": [ 382 | { 383 | "ename": "IndentationError", 384 | "evalue": "expected an indented block (, line 12)", 385 | "output_type": "error", 386 | "traceback": [ 387 | "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m12\u001b[0m\n\u001b[0;31m elif week > campaigns[ct]['cutoff_date']:\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" 388 | ] 389 | } 390 | ], 391 | "source": [ 392 | "#to be written to match the logic in the image above \n", 393 | "def tranche_iter_logic(week, state):\n", 394 | " \n", 395 | " ct = state['active_tranche']\n", 396 | " \n", 397 | " ct_data = state['tranches'][ct]\n", 398 | " \n", 399 | " #is the deadline passed:\n", 400 | " if ct_data['raised'] > ct_data['max_raise']:\n", 401 | " \n", 402 | " \n", 403 | " elif week > campaigns[ct]['cutoff_date']:\n", 404 | " \n", 405 | " #is the cap reached\n", 406 | " elif :\n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " return " 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": null, 416 | "metadata": {}, 417 | "outputs": [], 418 | "source": [ 419 | "tranch_status" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": null, 425 | "metadata": {}, 426 | "outputs": [], 427 | "source": [ 428 | "# generate a simulation so what happens!\n", 429 | "states = [state]\n", 430 | "for t in range(T):\n", 431 | " new_raise = gen_raise()\n", 432 | " state['new_raised'] = new_raise\n", 433 | " state['total_raised'] = state['total_raised'] + new_raise\n", 434 | " \n", 435 | " #\n", 436 | " " 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": null, 442 | "metadata": {}, 443 | "outputs": [], 444 | "source": [] 445 | } 446 | ], 447 | "metadata": { 448 | "kernelspec": { 449 | "display_name": "Python 3", 450 | "language": "python", 451 | "name": "python3" 452 | }, 453 | "language_info": { 454 | "codemirror_mode": { 455 | "name": "ipython", 456 | "version": 3 457 | }, 458 | "file_extension": ".py", 459 | "mimetype": "text/x-python", 460 | "name": "python", 461 | "nbconvert_exporter": "python", 462 | "pygments_lexer": "ipython3", 463 | "version": "3.6.4" 464 | } 465 | }, 466 | "nbformat": 4, 467 | "nbformat_minor": 2 468 | } 469 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | numpy 3 | scipy 4 | funcy 5 | cadCAD 6 | jupyter 7 | matplotlib 8 | seaborn 9 | networkx 10 | -------------------------------------------------------------------------------- /scratchwork_old/passive_voting.isdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/scratchwork_old/passive_voting.isdb -------------------------------------------------------------------------------- /scratwork_cadcad/conviction_system_logic_sim-josh.py: -------------------------------------------------------------------------------- 1 | #from pprint import pprint 2 | 3 | import numpy as np 4 | from tabulate import tabulate 5 | 6 | from cadCAD.configuration.utils import config_sim 7 | from conviction_helpers import get_nodes_by_type,initialize_network,total_funds_given_total_supply,trigger_threshold 8 | #import networkx as nx 9 | from scipy.stats import expon, gamma 10 | 11 | 12 | #functions for partial state update block 1 13 | 14 | #Driving processes: arrival of participants, proposals and funds 15 | ##----------------------------------------- 16 | 17 | 18 | def gen_new_participant(network, new_participant_holdings): 19 | 20 | i = len([node for node in network.nodes]) 21 | 22 | network.add_node(i) 23 | network.nodes[i]['type']="participant" 24 | 25 | s_rv = np.random.rand() 26 | network.nodes[i]['sentiment'] = s_rv 27 | network.nodes[i]['holdings']=new_participant_holdings 28 | 29 | for j in get_nodes_by_type(network, 'proposal'): 30 | network.add_edge(i, j) 31 | 32 | rv = np.random.rand() 33 | a_rv = 1-4*(1-rv)*rv #polarized distribution 34 | network.edges[(i, j)]['affinity'] = a_rv 35 | network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] 36 | network.edges[(i, j)]['conviction'] = 0 37 | 38 | return network 39 | 40 | 41 | scale_factor = 1000 42 | 43 | def gen_new_proposal(network, funds, supply, trigger_func): 44 | j = len([node for node in network.nodes]) 45 | network.add_node(j) 46 | network.nodes[j]['type']="proposal" 47 | 48 | network.nodes[j]['conviction']=0 49 | network.nodes[j]['status']='candidate' 50 | network.nodes[j]['age']=0 51 | 52 | rescale = scale_factor*funds 53 | r_rv = gamma.rvs(3,loc=0.001, scale=rescale) 54 | network.node[j]['funds_requested'] = r_rv 55 | 56 | network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) 57 | 58 | participants = get_nodes_by_type(network, 'participant') 59 | proposing_participant = np.random.choice(participants) 60 | 61 | for i in participants: 62 | network.add_edge(i, j) 63 | if i==proposing_participant: 64 | network.edges[(i, j)]['affinity']=1 65 | else: 66 | rv = np.random.rand() 67 | a_rv = 1-4*(1-rv)*rv #polarized distribution 68 | network.edges[(i, j)]['affinity'] = a_rv 69 | 70 | network.edges[(i, j)]['conviction'] = 0 71 | network.edges[(i,j)]['tokens'] = 0 72 | return network 73 | 74 | 75 | 76 | def driving_process(params, step, sL, s): 77 | 78 | #placeholder plumbing for random processes 79 | arrival_rate = 10/s['sentiment'] 80 | rv1 = np.random.rand() 81 | new_participant = bool(rv1<1/arrival_rate) 82 | if new_participant: 83 | h_rv = expon.rvs(loc=0.0, scale=1000) 84 | new_participant_holdings = h_rv 85 | else: 86 | new_participant_holdings = 0 87 | 88 | network = s['network'] 89 | affinities = [network.edges[e]['affinity'] for e in network.edges ] 90 | median_affinity = np.median(affinities) 91 | 92 | proposals = get_nodes_by_type(network, 'proposal') 93 | fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] 94 | 95 | funds = s['funds'] 96 | total_funds_requested = np.sum(fund_requests) 97 | 98 | proposal_rate = 10/median_affinity * total_funds_requested/funds 99 | rv2 = np.random.rand() 100 | new_proposal = bool(rv2<1/proposal_rate) 101 | 102 | sentiment = s['sentiment'] 103 | funds = s['funds'] 104 | scale_factor = 1+4000*sentiment**2 105 | 106 | #this shouldn't happen but expon is throwing domain errors 107 | if scale_factor > 1: 108 | funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) 109 | else: 110 | funds_arrival = 0 111 | 112 | return({'new_participant':new_participant, 113 | 'new_participant_holdings':new_participant_holdings, 114 | 'new_proposal':new_proposal, 115 | 'funds_arrival':funds_arrival}) 116 | 117 | 118 | #Mechanisms for updating the state based on driving processes 119 | ##--- 120 | def update_network(params, step, sL, s, _input): 121 | 122 | network = s['network'] 123 | funds = s['funds'] 124 | supply = s['supply'] 125 | trigger_func = params['trigger_func'] 126 | #print(trigger_func) 127 | 128 | new_participant = _input['new_participant'] #T/F 129 | new_proposal = _input['new_proposal'] #T/F 130 | 131 | if new_participant: 132 | new_participant_holdings = _input['new_participant_holdings'] 133 | network = gen_new_participant(network, new_participant_holdings) 134 | 135 | if new_proposal: 136 | network= gen_new_proposal(network,funds,supply,trigger_func ) 137 | 138 | #update age of the existing proposals 139 | proposals = get_nodes_by_type(network, 'proposal') 140 | 141 | for j in proposals: 142 | network.nodes[j]['age'] = network.nodes[j]['age']+1 143 | if network.nodes[j]['status'] == 'candidate': 144 | requested = network.nodes[j]['funds_requested'] 145 | network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) 146 | else: 147 | network.nodes[j]['trigger'] = np.nan 148 | 149 | key = 'network' 150 | value = network 151 | 152 | return (key, value) 153 | 154 | def increment_funds(params, step, sL, s, _input): 155 | 156 | funds = s['funds'] 157 | funds_arrival = _input['funds_arrival'] 158 | 159 | #increment funds 160 | funds = funds + funds_arrival 161 | 162 | key = 'funds' 163 | value = funds 164 | 165 | return (key, value) 166 | 167 | def increment_supply(params, step, sL, s, _input): 168 | 169 | supply = s['supply'] 170 | supply_arrival = _input['new_participant_holdings'] 171 | 172 | #increment funds 173 | supply = supply + supply_arrival 174 | 175 | key = 'supply' 176 | value = supply 177 | 178 | return (key, value) 179 | 180 | #functions for partial state update block 2 181 | 182 | #Driving processes: completion of previously funded proposals 183 | ##----------------------------------------- 184 | 185 | def check_progress(params, step, sL, s): 186 | 187 | network = s['network'] 188 | proposals = get_nodes_by_type(network, 'proposal') 189 | 190 | completed = [] 191 | for j in proposals: 192 | if network.nodes[j]['status'] == 'active': 193 | grant_size = network.nodes[j]['funds_requested'] 194 | base_completion_rate=params['base_completion_rate'] 195 | likelihood = 1.0/(base_completion_rate+np.log(grant_size)) 196 | if np.random.rand() < likelihood: 197 | completed.append(j) 198 | 199 | return({'completed':completed}) 200 | 201 | 202 | #Mechanisms for updating the state based on check progress 203 | ##--- 204 | def complete_proposal(params, step, sL, s, _input): 205 | 206 | network = s['network'] 207 | participants = get_nodes_by_type(network, 'participant') 208 | 209 | completed = _input['completed'] 210 | for j in completed: 211 | network.nodes[j]['status']='completed' 212 | for i in participants: 213 | force = network.edges[(i,j)]['affinity'] 214 | sentiment = network.node[i]['sentiment'] 215 | network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) 216 | 217 | key = 'network' 218 | value = network 219 | 220 | return (key, value) 221 | 222 | def update_sentiment_on_completion(params, step, sL, s, _input): 223 | 224 | network = s['network'] 225 | proposals = get_nodes_by_type(network, 'proposal') 226 | completed = _input['completed'] 227 | 228 | grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) 229 | 230 | grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) 231 | 232 | sentiment = s['sentiment'] 233 | 234 | force = grants_completed/grants_outstanding 235 | mu = params['sentiment_decay'] 236 | if (force >=0) and (force <=1): 237 | sentiment = get_sentimental(sentiment, force, mu) 238 | else: 239 | sentiment = get_sentimental(sentiment, 0, mu) 240 | 241 | 242 | key = 'sentiment' 243 | value = sentiment 244 | 245 | return (key, value) 246 | 247 | def get_sentimental(sentiment, force, decay=0): 248 | mu = decay 249 | sentiment = sentiment*(1-mu) + force 250 | 251 | if sentiment > 1: 252 | sentiment = 1 253 | 254 | return sentiment 255 | 256 | #functions for partial state update block 3 257 | 258 | #Decision processes: trigger function policy 259 | ##----------------------------------------- 260 | 261 | def trigger_function(params, step, sL, s): 262 | 263 | network = s['network'] 264 | funds = s['funds'] 265 | supply = s['supply'] 266 | proposals = get_nodes_by_type(network, 'proposal') 267 | tmin = params['tmin'] 268 | 269 | accepted = [] 270 | triggers = {} 271 | for j in proposals: 272 | if network.nodes[j]['status'] == 'candidate': 273 | requested = network.nodes[j]['funds_requested'] 274 | age = network.nodes[j]['age'] 275 | threshold = trigger_threshold(requested, funds, supply) 276 | if age > tmin: 277 | conviction = network.nodes[j]['conviction'] 278 | if conviction >threshold: 279 | accepted.append(j) 280 | else: 281 | threshold = np.nan 282 | 283 | triggers[j] = threshold 284 | 285 | 286 | 287 | return({'accepted':accepted, 'triggers':triggers}) 288 | 289 | def decrement_funds(params, step, sL, s, _input): 290 | 291 | funds = s['funds'] 292 | network = s['network'] 293 | accepted = _input['accepted'] 294 | 295 | #decrement funds 296 | for j in accepted: 297 | funds = funds - network.nodes[j]['funds_requested'] 298 | 299 | key = 'funds' 300 | value = funds 301 | 302 | return (key, value) 303 | 304 | def update_proposals(params, step, sL, s, _input): 305 | 306 | network = s['network'] 307 | accepted = _input['accepted'] 308 | triggers = _input['triggers'] 309 | participants = get_nodes_by_type(network, 'participant') 310 | proposals = get_nodes_by_type(network, 'proposals') 311 | sensitivity = params['sensitivity'] 312 | 313 | for j in proposals: 314 | network.nodes[j]['trigger'] = triggers[j] 315 | 316 | #bookkeeping conviction and participant sentiment 317 | for j in accepted: 318 | network.nodes[j]['status']='active' 319 | network.nodes[j]['conviction']=np.nan 320 | #change status to active 321 | for i in participants: 322 | 323 | #operating on edge = (i,j) 324 | #reset tokens assigned to other candidates 325 | network.edges[(i,j)]['tokens']=0 326 | network.edges[(i,j)]['conviction'] = np.nan 327 | 328 | #update participants sentiments (positive or negative) 329 | affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] 330 | if len(affinities)>1: 331 | max_affinity = np.max(affinities) 332 | force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity 333 | else: 334 | force = 0 335 | 336 | #based on what their affinities to the accepted proposals 337 | network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) 338 | 339 | 340 | key = 'network' 341 | value = network 342 | 343 | return (key, value) 344 | 345 | def update_sentiment_on_release(params, step, sL, s, _input): 346 | 347 | network = s['network'] 348 | proposals = get_nodes_by_type(network, 'proposal') 349 | accepted = _input['accepted'] 350 | 351 | proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) 352 | 353 | proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) 354 | 355 | sentiment = s['sentiment'] 356 | force = proposals_accepted/proposals_outstanding 357 | if (force >=0) and (force <=1): 358 | sentiment = get_sentimental(sentiment, force, False) 359 | else: 360 | sentiment = get_sentimental(sentiment, 0, False) 361 | 362 | key = 'sentiment' 363 | value = sentiment 364 | 365 | return (key, value) 366 | 367 | def participants_decisions(params, step, sL, s): 368 | network = s['network'] 369 | participants = get_nodes_by_type(network, 'participant') 370 | proposals = get_nodes_by_type(network, 'proposal') 371 | candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] 372 | sensitivity = params['sensitivity'] 373 | 374 | gain = .01 375 | delta_holdings={} 376 | proposals_supported ={} 377 | for i in participants: 378 | force = network.nodes[i]['sentiment']-sensitivity 379 | delta_holdings[i] = network.nodes[i]['holdings']*gain*force 380 | 381 | support = [] 382 | for j in candidates: 383 | affinity = network.edges[(i, j)]['affinity'] 384 | cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates]) 385 | if cutoff <.5: 386 | cutoff = .5 387 | 388 | if affinity > cutoff: 389 | support.append(j) 390 | 391 | proposals_supported[i] = support 392 | 393 | return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) 394 | 395 | def update_tokens(params, step, sL, s, _input): 396 | 397 | network = s['network'] 398 | delta_holdings = _input['delta_holdings'] 399 | proposals = get_nodes_by_type(network, 'proposal') 400 | proposals_supported = _input['proposals_supported'] 401 | participants = get_nodes_by_type(network, 'participant') 402 | alpha = params['alpha'] 403 | 404 | for i in participants: 405 | network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] 406 | supported = proposals_supported[i] 407 | total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) 408 | for j in proposals: 409 | if j in supported: 410 | normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity 411 | network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] 412 | else: 413 | network.edges[(i, j)]['tokens'] = 0 414 | 415 | prior_conviction = network.edges[(i, j)]['conviction'] 416 | current_tokens = network.edges[(i, j)]['tokens'] 417 | network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction 418 | 419 | for j in proposals: 420 | network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) 421 | 422 | key = 'network' 423 | value = network 424 | 425 | return (key, value) 426 | 427 | def update_supply(params, step, sL, s, _input): 428 | 429 | supply = s['supply'] 430 | delta_holdings = _input['delta_holdings'] 431 | delta_supply = np.sum([v for v in delta_holdings.values()]) 432 | 433 | supply = supply + delta_supply 434 | 435 | key = 'supply' 436 | value = supply 437 | 438 | return (key, value) 439 | 440 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 441 | # The Partial State Update Blocks 442 | partial_state_update_blocks = [ 443 | { 444 | 'policies': { 445 | #new proposals or new participants 446 | 'random': driving_process 447 | }, 448 | 'variables': { 449 | 'network': update_network, 450 | 'funds':increment_funds, 451 | 'supply':increment_supply 452 | } 453 | }, 454 | { 455 | 'policies': { 456 | 'completion': check_progress #see if any of the funded proposals completes 457 | }, 458 | 'variables': { # The following state variables will be updated simultaneously 459 | 'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it 460 | 'network': complete_proposal #book-keeping 461 | } 462 | }, 463 | { 464 | 'policies': { 465 | 'release': trigger_function #check each proposal to see if it passes 466 | }, 467 | 'variables': { # The following state variables will be updated simultaneously 468 | 'funds': decrement_funds, #funds expended 469 | 'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment 470 | 'network': update_proposals #reset convictions, and participants sentiments 471 | #update based on affinities 472 | } 473 | }, 474 | { 475 | 'policies': { 476 | 'participants_act': participants_decisions, #high sentiment, high affinity =>buy 477 | #low sentiment, low affinities => burn 478 | #assign tokens to top affinities 479 | }, 480 | 'variables': { 481 | 'supply': update_supply, 482 | 'network': update_tokens #update everyones holdings 483 | #and their conviction for each proposal 484 | } 485 | } 486 | ] 487 | 488 | n= 25 #initial participants 489 | m= 3 #initial proposals 490 | 491 | initial_sentiment = .5 492 | 493 | network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold) 494 | 495 | initial_conditions = {'network':network, 496 | 'supply': initial_supply, 497 | 'funds':initial_funds, 498 | 'sentiment': initial_sentiment} 499 | 500 | #power of 1 token forever 501 | # conviction_capactity = [2] 502 | # alpha = [1-1/cc for cc in conviction_capactity] 503 | # print(alpha) 504 | 505 | params={ 506 | 'sensitivity': [.75], 507 | 'tmin': [7], #unit days; minimum periods passed before a proposal can pass 508 | 'sentiment_decay': [.001], #termed mu in the state update function 509 | 'alpha': [0.5, 0.9], 510 | 'base_completion_rate': [10], 511 | 'trigger_func': [trigger_threshold] 512 | } 513 | 514 | 515 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 516 | # Settings of general simulation parameters, unrelated to the system itself 517 | # `T` is a range with the number of discrete units of time the simulation will run for; 518 | # `N` is the number of times the simulation will be run (Monte Carlo runs) 519 | time_periods_per_run = 250 520 | monte_carlo_runs = 1 521 | 522 | simulation_parameters = config_sim({ 523 | 'T': range(time_periods_per_run), 524 | 'N': monte_carlo_runs, 525 | 'M': params 526 | }) 527 | 528 | 529 | from cadCAD.configuration import append_configs 530 | 531 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 532 | # The configurations above are then packaged into a `Configuration` object 533 | append_configs( 534 | initial_state=initial_conditions, #dict containing variable names and initial values 535 | partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions 536 | sim_configs=simulation_parameters #dict containing simulation parameters 537 | ) 538 | 539 | from cadCAD.engine import ExecutionMode, ExecutionContext, Executor 540 | from cadCAD import configs 541 | import pandas as pd 542 | 543 | exec_mode = ExecutionMode() 544 | multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) 545 | run = Executor(exec_context=multi_proc_ctx, configs=configs) 546 | 547 | i = 0 548 | for raw_result, tensor_field in run.execute(): 549 | result = pd.DataFrame(raw_result) 550 | print() 551 | print(f"Tensor Field: {type(tensor_field)}") 552 | print(tabulate(tensor_field, headers='keys', tablefmt='psql')) 553 | print(f"Output: {type(result)}") 554 | print(tabulate(result, headers='keys', tablefmt='psql')) 555 | print() 556 | i += 1 557 | -------------------------------------------------------------------------------- /scratwork_cadcad/conviction_system_logic_sim.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from cadCAD.configuration.utils import config_sim 4 | from conviction_helpers import * 5 | #import networkx as nx 6 | from scipy.stats import expon, gamma 7 | 8 | 9 | #functions for partial state update block 1 10 | 11 | #Driving processes: arrival of participants, proposals and funds 12 | ##----------------------------------------- 13 | def gen_new_participant(network, new_participant_holdings): 14 | 15 | i = len([node for node in network.nodes]) 16 | 17 | network.add_node(i) 18 | network.nodes[i]['type']="participant" 19 | 20 | s_rv = np.random.rand() 21 | network.nodes[i]['sentiment'] = s_rv 22 | network.nodes[i]['holdings']=new_participant_holdings 23 | 24 | for j in get_nodes_by_type(network, 'proposal'): 25 | network.add_edge(i, j) 26 | 27 | rv = np.random.rand() 28 | a_rv = 1-4*(1-rv)*rv #polarized distribution 29 | network.edges[(i, j)]['affinity'] = a_rv 30 | network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] 31 | network.edges[(i, j)]['conviction'] = 0 32 | 33 | return network 34 | 35 | 36 | scale_factor = 1000 37 | 38 | def gen_new_proposal(network, funds, supply, trigger_func): 39 | j = len([node for node in network.nodes]) 40 | network.add_node(j) 41 | network.nodes[j]['type']="proposal" 42 | 43 | network.nodes[j]['conviction']=0 44 | network.nodes[j]['status']='candidate' 45 | network.nodes[j]['age']=0 46 | 47 | rescale = scale_factor*funds 48 | r_rv = gamma.rvs(3,loc=0.001, scale=rescale) 49 | network.node[j]['funds_requested'] = r_rv 50 | 51 | network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) 52 | 53 | participants = get_nodes_by_type(network, 'participant') 54 | proposing_participant = np.random.choice(participants) 55 | 56 | for i in participants: 57 | network.add_edge(i, j) 58 | if i==proposing_participant: 59 | network.edges[(i, j)]['affinity']=1 60 | else: 61 | rv = np.random.rand() 62 | a_rv = 1-4*(1-rv)*rv #polarized distribution 63 | network.edges[(i, j)]['affinity'] = a_rv 64 | 65 | network.edges[(i, j)]['conviction'] = 0 66 | network.edges[(i,j)]['tokens'] = 0 67 | return network 68 | 69 | 70 | 71 | def driving_process(params, step, sL, s): 72 | 73 | #placeholder plumbing for random processes 74 | arrival_rate = 10/s['sentiment'] 75 | rv1 = np.random.rand() 76 | new_participant = bool(rv1<1/arrival_rate) 77 | if new_participant: 78 | h_rv = expon.rvs(loc=0.0, scale=1000) 79 | new_participant_holdings = h_rv 80 | else: 81 | new_participant_holdings = 0 82 | 83 | network = s['network'] 84 | affinities = [network.edges[e]['affinity'] for e in network.edges ] 85 | median_affinity = np.median(affinities) 86 | 87 | proposals = get_nodes_by_type(network, 'proposal') 88 | fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] 89 | 90 | funds = s['funds'] 91 | total_funds_requested = np.sum(fund_requests) 92 | 93 | proposal_rate = 10/median_affinity * total_funds_requested/funds 94 | rv2 = np.random.rand() 95 | new_proposal = bool(rv2<1/proposal_rate) 96 | 97 | sentiment = s['sentiment'] 98 | funds = s['funds'] 99 | scale_factor = 1+4000*sentiment**2 100 | 101 | #this shouldn't happen but expon is throwing domain errors 102 | if scale_factor > 1: 103 | funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) 104 | else: 105 | funds_arrival = 0 106 | 107 | return({'new_participant':new_participant, 108 | 'new_participant_holdings':new_participant_holdings, 109 | 'new_proposal':new_proposal, 110 | 'funds_arrival':funds_arrival}) 111 | 112 | 113 | #Mechanisms for updating the state based on driving processes 114 | ##--- 115 | def update_network(params, step, sL, s, _input): 116 | 117 | network = s['network'] 118 | funds = s['funds'] 119 | supply = s['supply'] 120 | trigger_func = params['trigger_func'] 121 | 122 | new_participant = _input['new_participant'] #T/F 123 | new_proposal = _input['new_proposal'] #T/F 124 | 125 | if new_participant: 126 | new_participant_holdings = _input['new_participant_holdings'] 127 | network = gen_new_participant(network, new_participant_holdings) 128 | 129 | if new_proposal: 130 | network= gen_new_proposal(network,funds,supply,trigger_func ) 131 | 132 | #update age of the existing proposals 133 | proposals = get_nodes_by_type(network, 'proposal') 134 | 135 | for j in proposals: 136 | network.nodes[j]['age'] = network.nodes[j]['age']+1 137 | if network.nodes[j]['status'] == 'candidate': 138 | requested = network.nodes[j]['funds_requested'] 139 | network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) 140 | else: 141 | network.nodes[j]['trigger'] = np.nan 142 | 143 | key = 'network' 144 | value = network 145 | 146 | return (key, value) 147 | 148 | def increment_funds(params, step, sL, s, _input): 149 | 150 | funds = s['funds'] 151 | funds_arrival = _input['funds_arrival'] 152 | 153 | #increment funds 154 | funds = funds + funds_arrival 155 | 156 | key = 'funds' 157 | value = funds 158 | 159 | return (key, value) 160 | 161 | def increment_supply(params, step, sL, s, _input): 162 | 163 | supply = s['supply'] 164 | supply_arrival = _input['new_participant_holdings'] 165 | 166 | #increment funds 167 | supply = supply + supply_arrival 168 | 169 | key = 'supply' 170 | value = supply 171 | 172 | return (key, value) 173 | 174 | #functions for partial state update block 2 175 | 176 | #Driving processes: completion of previously funded proposals 177 | ##----------------------------------------- 178 | 179 | def check_progress(params, step, sL, s): 180 | 181 | network = s['network'] 182 | proposals = get_nodes_by_type(network, 'proposal') 183 | 184 | completed = [] 185 | for j in proposals: 186 | if network.nodes[j]['status'] == 'active': 187 | grant_size = network.nodes[j]['funds_requested'] 188 | base_completion_rate=params['base_completion_rate'] 189 | likelihood = 1.0/(base_completion_rate+np.log(grant_size)) 190 | if np.random.rand() < likelihood: 191 | completed.append(j) 192 | 193 | return({'completed':completed}) 194 | 195 | 196 | #Mechanisms for updating the state based on check progress 197 | ##--- 198 | def complete_proposal(params, step, sL, s, _input): 199 | 200 | network = s['network'] 201 | participants = get_nodes_by_type(network, 'participant') 202 | 203 | completed = _input['completed'] 204 | for j in completed: 205 | network.nodes[j]['status']='completed' 206 | for i in participants: 207 | force = network.edges[(i,j)]['affinity'] 208 | sentiment = network.node[i]['sentiment'] 209 | network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) 210 | 211 | key = 'network' 212 | value = network 213 | 214 | return (key, value) 215 | 216 | def update_sentiment_on_completion(params, step, sL, s, _input): 217 | 218 | network = s['network'] 219 | proposals = get_nodes_by_type(network, 'proposal') 220 | completed = _input['completed'] 221 | 222 | grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) 223 | 224 | grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) 225 | 226 | sentiment = s['sentiment'] 227 | 228 | force = grants_completed/grants_outstanding 229 | mu = params['sentiment_decay'] 230 | if (force >=0) and (force <=1): 231 | sentiment = get_sentimental(sentiment, force, mu) 232 | else: 233 | sentiment = get_sentimental(sentiment, 0, mu) 234 | 235 | 236 | key = 'sentiment' 237 | value = sentiment 238 | 239 | return (key, value) 240 | 241 | def get_sentimental(sentiment, force, decay=0): 242 | mu = decay 243 | sentiment = sentiment*(1-mu) + force 244 | 245 | if sentiment > 1: 246 | sentiment = 1 247 | 248 | return sentiment 249 | 250 | #functions for partial state update block 3 251 | 252 | #Decision processes: trigger function policy 253 | ##----------------------------------------- 254 | 255 | def trigger_function(params, step, sL, s): 256 | 257 | network = s['network'] 258 | funds = s['funds'] 259 | supply = s['supply'] 260 | proposals = get_nodes_by_type(network, 'proposal') 261 | tmin = params['tmin'] 262 | 263 | accepted = [] 264 | triggers = {} 265 | for j in proposals: 266 | if network.nodes[j]['status'] == 'candidate': 267 | requested = network.nodes[j]['funds_requested'] 268 | age = network.nodes[j]['age'] 269 | threshold = trigger_threshold(requested, funds, supply) 270 | if age > tmin: 271 | conviction = network.nodes[j]['conviction'] 272 | if conviction >threshold: 273 | accepted.append(j) 274 | else: 275 | threshold = np.nan 276 | 277 | triggers[j] = threshold 278 | 279 | 280 | 281 | return({'accepted':accepted, 'triggers':triggers}) 282 | 283 | def decrement_funds(params, step, sL, s, _input): 284 | 285 | funds = s['funds'] 286 | network = s['network'] 287 | accepted = _input['accepted'] 288 | 289 | #decrement funds 290 | for j in accepted: 291 | funds = funds - network.nodes[j]['funds_requested'] 292 | 293 | key = 'funds' 294 | value = funds 295 | 296 | return (key, value) 297 | 298 | def update_proposals(params, step, sL, s, _input): 299 | 300 | network = s['network'] 301 | accepted = _input['accepted'] 302 | triggers = _input['triggers'] 303 | participants = get_nodes_by_type(network, 'participant') 304 | proposals = get_nodes_by_type(network, 'proposals') 305 | sensitivity = params['sensitivity'] 306 | 307 | for j in proposals: 308 | network.nodes[j]['trigger'] = triggers[j] 309 | 310 | #bookkeeping conviction and participant sentiment 311 | for j in accepted: 312 | network.nodes[j]['status']='active' 313 | network.nodes[j]['conviction']=np.nan 314 | #change status to active 315 | for i in participants: 316 | 317 | #operating on edge = (i,j) 318 | #reset tokens assigned to other candidates 319 | network.edges[(i,j)]['tokens']=0 320 | network.edges[(i,j)]['conviction'] = np.nan 321 | 322 | #update participants sentiments (positive or negative) 323 | affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] 324 | if len(affinities)>1: 325 | max_affinity = np.max(affinities) 326 | force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity 327 | else: 328 | force = 0 329 | 330 | #based on what their affinities to the accepted proposals 331 | network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) 332 | 333 | 334 | key = 'network' 335 | value = network 336 | 337 | return (key, value) 338 | 339 | def update_sentiment_on_release(params, step, sL, s, _input): 340 | 341 | network = s['network'] 342 | proposals = get_nodes_by_type(network, 'proposal') 343 | accepted = _input['accepted'] 344 | 345 | proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) 346 | 347 | proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) 348 | 349 | sentiment = s['sentiment'] 350 | force = proposals_accepted/proposals_outstanding 351 | if (force >=0) and (force <=1): 352 | sentiment = get_sentimental(sentiment, force, False) 353 | else: 354 | sentiment = get_sentimental(sentiment, 0, False) 355 | 356 | key = 'sentiment' 357 | value = sentiment 358 | 359 | return (key, value) 360 | 361 | def participants_decisions(params, step, sL, s): 362 | network = s['network'] 363 | participants = get_nodes_by_type(network, 'participant') 364 | proposals = get_nodes_by_type(network, 'proposal') 365 | candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] 366 | sensitivity = params['sensitivity'] 367 | 368 | gain = .01 369 | delta_holdings={} 370 | proposals_supported ={} 371 | for i in participants: 372 | force = network.nodes[i]['sentiment']-sensitivity 373 | delta_holdings[i] = network.nodes[i]['holdings']*gain*force 374 | 375 | support = [] 376 | for j in candidates: 377 | affinity = network.edges[(i, j)]['affinity'] 378 | cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates]) 379 | if cutoff <.5: 380 | cutoff = .5 381 | 382 | if affinity > cutoff: 383 | support.append(j) 384 | 385 | proposals_supported[i] = support 386 | 387 | return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) 388 | 389 | def update_tokens(params, step, sL, s, _input): 390 | 391 | network = s['network'] 392 | delta_holdings = _input['delta_holdings'] 393 | proposals = get_nodes_by_type(network, 'proposal') 394 | proposals_supported = _input['proposals_supported'] 395 | participants = get_nodes_by_type(network, 'participant') 396 | alpha = params['alpha'] 397 | 398 | for i in participants: 399 | network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] 400 | supported = proposals_supported[i] 401 | total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) 402 | for j in proposals: 403 | if j in supported: 404 | normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity 405 | network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] 406 | else: 407 | network.edges[(i, j)]['tokens'] = 0 408 | 409 | prior_conviction = network.edges[(i, j)]['conviction'] 410 | current_tokens = network.edges[(i, j)]['tokens'] 411 | network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction 412 | 413 | for j in proposals: 414 | network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) 415 | 416 | key = 'network' 417 | value = network 418 | 419 | return (key, value) 420 | 421 | def update_supply(params, step, sL, s, _input): 422 | 423 | supply = s['supply'] 424 | delta_holdings = _input['delta_holdings'] 425 | delta_supply = np.sum([v for v in delta_holdings.values()]) 426 | 427 | supply = supply + delta_supply 428 | 429 | key = 'supply' 430 | value = supply 431 | 432 | return (key, value) 433 | 434 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 435 | # The Partial State Update Blocks 436 | partial_state_update_blocks = [ 437 | { 438 | 'policies': { 439 | #new proposals or new participants 440 | 'random': driving_process 441 | }, 442 | 'variables': { 443 | 'network': update_network, 444 | 'funds':increment_funds, 445 | 'supply':increment_supply 446 | } 447 | }, 448 | { 449 | 'policies': { 450 | 'completion': check_progress #see if any of the funded proposals completes 451 | }, 452 | 'variables': { # The following state variables will be updated simultaneously 453 | 'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it 454 | 'network': complete_proposal #book-keeping 455 | } 456 | }, 457 | { 458 | 'policies': { 459 | 'release': trigger_function #check each proposal to see if it passes 460 | }, 461 | 'variables': { # The following state variables will be updated simultaneously 462 | 'funds': decrement_funds, #funds expended 463 | 'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment 464 | 'network': update_proposals #reset convictions, and participants sentiments 465 | #update based on affinities 466 | } 467 | }, 468 | { 469 | 'policies': { 470 | 'participants_act': participants_decisions, #high sentiment, high affinity =>buy 471 | #low sentiment, low affinities => burn 472 | #assign tokens to top affinities 473 | }, 474 | 'variables': { 475 | 'supply': update_supply, 476 | 'network': update_tokens #update everyones holdings 477 | #and their conviction for each proposal 478 | } 479 | } 480 | ] 481 | 482 | n= 25 #initial participants 483 | m= 3 #initial proposals 484 | 485 | initial_sentiment = .5 486 | 487 | network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold) 488 | 489 | initial_conditions = {'network':network, 490 | 'supply': initial_supply, 491 | 'funds':initial_funds, 492 | 'sentiment': initial_sentiment} 493 | 494 | #power of 1 token forever 495 | # conviction_capactity = [2] 496 | # alpha = [1-1/cc for cc in conviction_capactity] 497 | # print(alpha) 498 | 499 | params={ 500 | 'sensitivity': [.75], 501 | 'tmin': [7], #unit days; minimum periods passed before a proposal can pass 502 | 'sentiment_decay': [.001], #termed mu in the state update function 503 | 'alpha': [0.5], 504 | 'base_completion_rate': [10], 505 | 'trigger_func': [trigger_threshold] 506 | } 507 | 508 | 509 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 510 | # Settings of general simulation parameters, unrelated to the system itself 511 | # `T` is a range with the number of discrete units of time the simulation will run for; 512 | # `N` is the number of times the simulation will be run (Monte Carlo runs) 513 | time_periods_per_run = 250 514 | monte_carlo_runs = 1 515 | 516 | simulation_parameters = config_sim({ 517 | 'T': range(time_periods_per_run), 518 | 'N': monte_carlo_runs, 519 | 'M': params 520 | }) 521 | 522 | 523 | from cadCAD.configuration import append_configs 524 | 525 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 526 | # The configurations above are then packaged into a `Configuration` object 527 | append_configs( 528 | initial_state=initial_conditions, #dict containing variable names and initial values 529 | partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions 530 | sim_configs=simulation_parameters #dict containing simulation parameters 531 | ) 532 | 533 | from cadCAD.engine import ExecutionMode, ExecutionContext, Executor 534 | from cadCAD import configs 535 | 536 | exec_mode = ExecutionMode() 537 | multi_proc_ctx = ExecutionContext(context=exec_mode.multi_proc) 538 | run = Executor(exec_context=multi_proc_ctx, configs=configs) 539 | raw_result, tensor = run.execute() 540 | 541 | #import pandas as pd 542 | #df = pd.DataFrame(raw_result) 543 | # exec_mode = ExecutionMode() 544 | # exec_context = ExecutionContext(context=exec_mode.multi_proc) 545 | # # run = Executor(exec_context=exec_context, configs=configs) 546 | # executor = Executor(exec_context, configs) # Pass the configuration object inside an array 547 | # raw_result, tensor = executor.execute() # The `main()` method returns a tuple; its first elements contains the raw results -------------------------------------------------------------------------------- /scratwork_cadcad/conviction_test_sim.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from conviction_helpers import get_nodes_by_type, initialize_network, total_funds_given_total_supply, trigger_threshold 3 | #import networkx as nx 4 | from scipy.stats import expon, gamma 5 | 6 | 7 | #functions for partial state update block 1 8 | 9 | #Driving processes: arrival of participants, proposals and funds 10 | ##----------------------------------------- 11 | def gen_new_participant(network, new_participant_holdings): 12 | 13 | i = len([node for node in network.nodes]) 14 | 15 | network.add_node(i) 16 | network.nodes[i]['type']="participant" 17 | 18 | s_rv = np.random.rand() 19 | network.nodes[i]['sentiment'] = s_rv 20 | network.nodes[i]['holdings']=new_participant_holdings 21 | 22 | for j in get_nodes_by_type(network, 'proposal'): 23 | network.add_edge(i, j) 24 | 25 | rv = np.random.rand() 26 | a_rv = 1-4*(1-rv)*rv #polarized distribution 27 | network.edges[(i, j)]['affinity'] = a_rv 28 | network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings'] 29 | network.edges[(i, j)]['conviction'] = 0 30 | 31 | return network 32 | 33 | 34 | scale_factor = 1000 35 | 36 | def gen_new_proposal(network, funds, supply, total_funds, trigger_func): 37 | j = len([node for node in network.nodes]) 38 | network.add_node(j) 39 | network.nodes[j]['type']="proposal" 40 | 41 | network.nodes[j]['conviction']=0 42 | network.nodes[j]['status']='candidate' 43 | network.nodes[j]['age']=0 44 | 45 | rescale = scale_factor*funds/total_funds 46 | r_rv = gamma.rvs(3,loc=0.001, scale=rescale) 47 | network.node[j]['funds_requested'] = r_rv 48 | 49 | network.nodes[j]['trigger']= trigger_func(r_rv, funds, supply) 50 | 51 | participants = get_nodes_by_type(network, 'participant') 52 | proposing_participant = np.random.choice(participants) 53 | 54 | for i in participants: 55 | network.add_edge(i, j) 56 | if i==proposing_participant: 57 | network.edges[(i, j)]['affinity']=1 58 | else: 59 | rv = np.random.rand() 60 | a_rv = 1-4*(1-rv)*rv #polarized distribution 61 | network.edges[(i, j)]['affinity'] = a_rv 62 | 63 | network.edges[(i, j)]['conviction'] = 0 64 | network.edges[(i,j)]['tokens'] = 0 65 | return network 66 | 67 | 68 | 69 | def driving_process(params, step, sL, s): 70 | 71 | #placeholder plumbing for random processes 72 | arrival_rate = 10/s['sentiment'] 73 | rv1 = np.random.rand() 74 | new_participant = bool(rv1<1/arrival_rate) 75 | if new_participant: 76 | h_rv = expon.rvs(loc=0.0, scale=1000) 77 | new_participant_holdings = h_rv 78 | else: 79 | new_participant_holdings = 0 80 | 81 | network = s['network'] 82 | affinities = [network.edges[e]['affinity'] for e in network.edges ] 83 | median_affinity = np.median(affinities) 84 | 85 | proposals = get_nodes_by_type(network, 'proposal') 86 | fund_requests = [network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate' ] 87 | 88 | funds = s['funds'] 89 | total_funds_requested = np.sum(fund_requests) 90 | 91 | proposal_rate = 10/median_affinity * total_funds_requested/funds 92 | rv2 = np.random.rand() 93 | new_proposal = bool(rv2<1/proposal_rate) 94 | 95 | sentiment = s['sentiment'] 96 | funds = s['funds'] 97 | scale_factor = 1+4000*sentiment**2 98 | 99 | #this shouldn't happen but expon is throwing domain errors 100 | if scale_factor > 1: 101 | funds_arrival = expon.rvs(loc = 0, scale = scale_factor ) 102 | else: 103 | funds_arrival = 0 104 | 105 | return({'new_participant':new_participant, 106 | 'new_participant_holdings':new_participant_holdings, 107 | 'new_proposal':new_proposal, 108 | 'funds_arrival':funds_arrival}) 109 | 110 | 111 | #Mechanisms for updating the state based on driving processes 112 | ##--- 113 | def update_network(params, step, sL, s, _input): 114 | 115 | print(params) 116 | print(type(params)) 117 | 118 | network = s['network'] 119 | funds = s['funds'] 120 | supply = s['supply'] 121 | trigger_func = params['trigger_func'] 122 | 123 | new_participant = _input['new_participant'] #T/F 124 | new_proposal = _input['new_proposal'] #T/F 125 | 126 | if new_participant: 127 | new_participant_holdings = _input['new_participant_holdings'] 128 | network = gen_new_participant(network, new_participant_holdings) 129 | 130 | if new_proposal: 131 | network= gen_new_proposal(network,funds,supply ) 132 | 133 | #update age of the existing proposals 134 | proposals = get_nodes_by_type(network, 'proposal') 135 | 136 | for j in proposals: 137 | network.nodes[j]['age'] = network.nodes[j]['age']+1 138 | if network.nodes[j]['status'] == 'candidate': 139 | requested = network.nodes[j]['funds_requested'] 140 | network.nodes[j]['trigger'] = trigger_func(requested, funds, supply) 141 | else: 142 | network.nodes[j]['trigger'] = np.nan 143 | 144 | key = 'network' 145 | value = network 146 | 147 | return (key, value) 148 | 149 | def increment_funds(params, step, sL, s, _input): 150 | 151 | funds = s['funds'] 152 | funds_arrival = _input['funds_arrival'] 153 | 154 | #increment funds 155 | funds = funds + funds_arrival 156 | 157 | key = 'funds' 158 | value = funds 159 | 160 | return (key, value) 161 | 162 | def increment_supply(params, step, sL, s, _input): 163 | 164 | supply = s['supply'] 165 | supply_arrival = _input['new_participant_holdings'] 166 | 167 | #increment funds 168 | supply = supply + supply_arrival 169 | 170 | key = 'supply' 171 | value = supply 172 | 173 | return (key, value) 174 | 175 | #functions for partial state update block 2 176 | 177 | #Driving processes: completion of previously funded proposals 178 | ##----------------------------------------- 179 | 180 | def check_progress(params, step, sL, s): 181 | 182 | network = s['network'] 183 | proposals = get_nodes_by_type(network, 'proposal') 184 | 185 | completed = [] 186 | for j in proposals: 187 | if network.nodes[j]['status'] == 'active': 188 | grant_size = network.nodes[j]['funds_requested'] 189 | base_completion_rate=params['base_completion_rate'] 190 | likelihood = 1.0/(base_completion_rate+np.log(grant_size)) 191 | if np.random.rand() < likelihood: 192 | completed.append(j) 193 | 194 | return({'completed':completed}) 195 | 196 | 197 | #Mechanisms for updating the state based on check progress 198 | ##--- 199 | def complete_proposal(params, step, sL, s, _input): 200 | 201 | network = s['network'] 202 | participants = get_nodes_by_type(network, 'participant') 203 | 204 | completed = _input['completed'] 205 | for j in completed: 206 | network.nodes[j]['status']='completed' 207 | for i in participants: 208 | force = network.edges[(i,j)]['affinity'] 209 | sentiment = network.node[i]['sentiment'] 210 | network.node[i]['sentiment'] = get_sentimental(sentiment, force, decay=0) 211 | 212 | key = 'network' 213 | value = network 214 | 215 | return (key, value) 216 | 217 | def update_sentiment_on_completion(params, step, sL, s, _input): 218 | 219 | network = s['network'] 220 | proposals = get_nodes_by_type(network, 'proposal') 221 | completed = _input['completed'] 222 | 223 | grants_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='active']) 224 | 225 | grants_completed = np.sum([network.nodes[j]['funds_requested'] for j in completed]) 226 | 227 | sentiment = s['sentiment'] 228 | 229 | force = grants_completed/grants_outstanding 230 | mu = params['sentiment_decay'] 231 | if (force >=0) and (force <=1): 232 | sentiment = get_sentimental(sentiment, force, mu) 233 | else: 234 | sentiment = get_sentimental(sentiment, 0, mu) 235 | 236 | 237 | key = 'sentiment' 238 | value = sentiment 239 | 240 | return (key, value) 241 | 242 | def get_sentimental(sentiment, force, decay=0): 243 | mu = decay 244 | sentiment = sentiment*(1-mu) + force 245 | 246 | if sentiment > 1: 247 | sentiment = 1 248 | 249 | return sentiment 250 | 251 | #functions for partial state update block 3 252 | 253 | #Decision processes: trigger function policy 254 | ##----------------------------------------- 255 | 256 | def trigger_function(params, step, sL, s): 257 | 258 | network = s['network'] 259 | funds = s['funds'] 260 | supply = s['supply'] 261 | proposals = get_nodes_by_type(network, 'proposal') 262 | tmin = params['tmin'] 263 | 264 | accepted = [] 265 | triggers = {} 266 | for j in proposals: 267 | if network.nodes[j]['status'] == 'candidate': 268 | requested = network.nodes[j]['funds_requested'] 269 | age = network.nodes[j]['age'] 270 | threshold = trigger_threshold(requested, funds, supply) 271 | if age > tmin: 272 | conviction = network.nodes[j]['conviction'] 273 | if conviction >threshold: 274 | accepted.append(j) 275 | else: 276 | threshold = np.nan 277 | 278 | triggers[j] = threshold 279 | 280 | 281 | 282 | return({'accepted':accepted, 'triggers':triggers}) 283 | 284 | def decrement_funds(params, step, sL, s, _input): 285 | 286 | funds = s['funds'] 287 | network = s['network'] 288 | accepted = _input['accepted'] 289 | 290 | #decrement funds 291 | for j in accepted: 292 | funds = funds - network.nodes[j]['funds_requested'] 293 | 294 | key = 'funds' 295 | value = funds 296 | 297 | return (key, value) 298 | 299 | def update_proposals(params, step, sL, s, _input): 300 | 301 | network = s['network'] 302 | accepted = _input['accepted'] 303 | triggers = _input['triggers'] 304 | participants = get_nodes_by_type(network, 'participant') 305 | proposals = get_nodes_by_type(network, 'proposals') 306 | sensitivity = params['sensitivity'] 307 | 308 | for j in proposals: 309 | network.nodes[j]['trigger'] = triggers[j] 310 | 311 | #bookkeeping conviction and participant sentiment 312 | for j in accepted: 313 | network.nodes[j]['status']='active' 314 | network.nodes[j]['conviction']=np.nan 315 | #change status to active 316 | for i in participants: 317 | 318 | #operating on edge = (i,j) 319 | #reset tokens assigned to other candidates 320 | network.edges[(i,j)]['tokens']=0 321 | network.edges[(i,j)]['conviction'] = np.nan 322 | 323 | #update participants sentiments (positive or negative) 324 | affinities = [network.edges[(i,p)]['affinity'] for p in proposals if not(p in accepted)] 325 | if len(affinities)>1: 326 | max_affinity = np.max(affinities) 327 | force = network.edges[(i,j)]['affinity']-sensitivity*max_affinity 328 | else: 329 | force = 0 330 | 331 | #based on what their affinities to the accepted proposals 332 | network.nodes[i]['sentiment'] = get_sentimental(network.nodes[i]['sentiment'], force, False) 333 | 334 | 335 | key = 'network' 336 | value = network 337 | 338 | return (key, value) 339 | 340 | def update_sentiment_on_release(params, step, sL, s, _input): 341 | 342 | network = s['network'] 343 | proposals = get_nodes_by_type(network, 'proposal') 344 | accepted = _input['accepted'] 345 | 346 | proposals_outstanding = np.sum([network.nodes[j]['funds_requested'] for j in proposals if network.nodes[j]['status']=='candidate']) 347 | 348 | proposals_accepted = np.sum([network.nodes[j]['funds_requested'] for j in accepted]) 349 | 350 | sentiment = s['sentiment'] 351 | force = proposals_accepted/proposals_outstanding 352 | if (force >=0) and (force <=1): 353 | sentiment = get_sentimental(sentiment, force, False) 354 | else: 355 | sentiment = get_sentimental(sentiment, 0, False) 356 | 357 | key = 'sentiment' 358 | value = sentiment 359 | 360 | return (key, value) 361 | 362 | def participants_decisions(params, step, sL, s): 363 | 364 | network = s['network'] 365 | participants = get_nodes_by_type(network, 'participant') 366 | proposals = get_nodes_by_type(network, 'proposal') 367 | candidates = [j for j in proposals if network.nodes[j]['status']=='candidate'] 368 | sensitivity = params['sensitivity'] 369 | 370 | gain = .01 371 | delta_holdings={} 372 | proposals_supported ={} 373 | for i in participants: 374 | force = network.nodes[i]['sentiment']-sensitivity 375 | delta_holdings[i] = network.nodes[i]['holdings']*gain*force 376 | 377 | support = [] 378 | for j in candidates: 379 | affinity = network.edges[(i, j)]['affinity'] 380 | cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates]) 381 | if cutoff <.5: 382 | cutoff = .5 383 | 384 | if affinity > cutoff: 385 | support.append(j) 386 | 387 | proposals_supported[i] = support 388 | 389 | return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported}) 390 | 391 | def update_tokens(params, step, sL, s, _input): 392 | 393 | network = s['network'] 394 | delta_holdings = _input['delta_holdings'] 395 | proposals = get_nodes_by_type(network, 'proposal') 396 | proposals_supported = _input['proposals_supported'] 397 | participants = get_nodes_by_type(network, 'participant') 398 | alpha = params['alpha'] 399 | 400 | for i in participants: 401 | network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i] 402 | supported = proposals_supported[i] 403 | total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported]) 404 | for j in proposals: 405 | if j in supported: 406 | normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity 407 | network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings'] 408 | else: 409 | network.edges[(i, j)]['tokens'] = 0 410 | 411 | prior_conviction = network.edges[(i, j)]['conviction'] 412 | current_tokens = network.edges[(i, j)]['tokens'] 413 | network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction 414 | 415 | for j in proposals: 416 | network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants]) 417 | 418 | key = 'network' 419 | value = network 420 | 421 | return (key, value) 422 | 423 | def update_supply(params, step, sL, s, _input): 424 | 425 | supply = s['supply'] 426 | delta_holdings = _input['delta_holdings'] 427 | delta_supply = np.sum([v for v in delta_holdings.values()]) 428 | 429 | supply = supply + delta_supply 430 | 431 | key = 'supply' 432 | value = supply 433 | 434 | return (key, value) 435 | 436 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 437 | # The Partial State Update Blocks 438 | partial_state_update_blocks = [ 439 | { 440 | 'policies': { 441 | #new proposals or new participants 442 | 'random': driving_process 443 | }, 444 | 'variables': { 445 | 'network': update_network, 446 | 'funds':increment_funds, 447 | 'supply':increment_supply 448 | } 449 | }, 450 | { 451 | 'policies': { 452 | 'completion': check_progress #see if any of the funded proposals completes 453 | }, 454 | 'variables': { # The following state variables will be updated simultaneously 455 | 'sentiment': update_sentiment_on_completion, #note completing decays sentiment, completing bumps it 456 | 'network': complete_proposal #book-keeping 457 | } 458 | }, 459 | { 460 | 'policies': { 461 | 'release': trigger_function #check each proposal to see if it passes 462 | }, 463 | 'variables': { # The following state variables will be updated simultaneously 464 | 'funds': decrement_funds, #funds expended 465 | 'sentiment': update_sentiment_on_release, #releasing funds can bump sentiment 466 | 'network': update_proposals #reset convictions, and participants sentiments 467 | #update based on affinities 468 | } 469 | }, 470 | { 471 | 'policies': { 472 | 'participants_act': participants_decisions, #high sentiment, high affinity =>buy 473 | #low sentiment, low affinities => burn 474 | #assign tokens to top affinities 475 | }, 476 | 'variables': { 477 | 'supply': update_supply, 478 | 'network': update_tokens #update everyones holdings 479 | #and their conviction for each proposal 480 | } 481 | } 482 | ] 483 | 484 | n= 25 #initial participants 485 | m= 3 #initial proposals 486 | 487 | initial_sentiment = .5 488 | 489 | network, initial_funds, initial_supply, total_requested = initialize_network(n,m,total_funds_given_total_supply,trigger_threshold) 490 | 491 | initial_conditions = {'network':network, 492 | 'supply': initial_supply, 493 | 'funds':initial_funds, 494 | 'sentiment': initial_sentiment} 495 | 496 | #power of 1 token forever 497 | # conviction_capactity = [2] 498 | # alpha = [1-1/cc for cc in conviction_capactity] 499 | # print(alpha) 500 | 501 | params={ 502 | 'sensitivity': [.75], 503 | 'tmin': [7], #unit days; minimum periods passed before a proposal can pass 504 | 'sentiment_decay': [.001], #termed mu in the state update function 505 | 'alpha': [0.5, 0.9], 506 | 'base_completion_rate': [10], 507 | 'trigger_func': [trigger_threshold] 508 | } 509 | 510 | 511 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 512 | # Settings of general simulation parameters, unrelated to the system itself 513 | # `T` is a range with the number of discrete units of time the simulation will run for; 514 | # `N` is the number of times the simulation will be run (Monte Carlo runs) 515 | time_periods_per_run = 250 516 | monte_carlo_runs = 1 517 | 518 | simulation_parameters = { 519 | 'T': range(time_periods_per_run), 520 | 'N': monte_carlo_runs, 521 | 'M': params 522 | } 523 | 524 | 525 | from cadCAD.engine import ExecutionMode, ExecutionContext, Executor 526 | from cadCAD.configuration import append_configs 527 | from cadCAD import configs 528 | 529 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 530 | # The configurations above are then packaged into a `Configuration` object 531 | config = append_configs( 532 | initial_state=initial_conditions, #dict containing variable names and initial values 533 | partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions 534 | sim_configs=simulation_parameters #dict containing simulation parameters 535 | ) 536 | 537 | exec_mode = ExecutionMode() 538 | exec_context = ExecutionContext(context=exec_mode.multi_proc) 539 | run = Executor(exec_context=exec_context, configs=configs) 540 | executor = Executor(exec_context, configs) # Pass the configuration object inside an array 541 | raw_result, tensor = executor.execute() # The `main()` method returns a tuple; its first elements contains the raw results -------------------------------------------------------------------------------- /social-sensorfusion.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockScience/conviction/885ab9278c9a4d57e93d6709d0770e84d38f15e5/social-sensorfusion.pdf --------------------------------------------------------------------------------