|
|
@ -0,0 +1,9 @@
|
||||||
|
__pycache__
|
||||||
|
.vscode
|
||||||
|
.ipynb_checkpoints
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
models/__pycache__
|
||||||
|
models/v3/__pycache__
|
||||||
|
models/v3/model/__pycache__
|
||||||
|
models/v3/model/parts/__pycache__
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"python.pythonPath": "/opt/anaconda3/bin/python"
|
||||||
|
}
|
||||||
122
README.md
|
|
@ -1,35 +1,126 @@
|
||||||
# Aragon_Conviction_Voting
|
# Aragon Conviction Voting
|
||||||
|
|
||||||
[Conviction Voting](https://medium.com/giveth/conviction-voting-a-novel-continuous-decision-making-alternative-to-governance-aa746cfb9475) is a novel decision making process where votes express their preference for which proposals they would like to see approved in a continuous rather than discrete way. The longer the community keeps a preference on an individual proposal, the “stronger” the proposal conviction becomes. In the conviction voting model, a graph structure is used to record the the introduction and removal of participants, candidates, proposals, and their outcomes.
|
This cadCAD model and notebook series is a collaboration between [Aragon Project](aragon.org), [1Hive](1hive.org), [BlockScience](block.science), and [the Commons Stack](commonsstack.org). A brief table of contents follows to explain the file structure of the various documents produced in this collaboration.
|
||||||
|
|
||||||
|
#### Note: If viewing on Github, matehmatical type setting does not render. To view mathematical rendering, visit the following [link](TODO)
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
### 1. Supporting documentation for context
|
||||||
|
* [Readme doc]() (you are here): For a high level exploration of Conviction Voting and what exactly we're trying to do with this model, start right here.
|
||||||
|
* [Conviction Voting Algorithm Overview](https://github.com/BlockScience/Aragon_Conviction_Voting/blob/master/algorithm_overview.md): For a deeper understanding of the CV algorithm, including it's mathematical derivation, read this document
|
||||||
|
* [Deriving the Alpha Parameter](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Deriving_Alpha.ipynb): For an in-depth look at the specific considerations around the alpha parameter, which sets the half life decay of conviction, read this notebook
|
||||||
|
* [Explaining the Trigger Function](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Trigger_Function_Explanation.ipynb): For an in-depth look at the trigger function equation and how proposals pass from candidate to active status, read this notebook
|
||||||
|
|
||||||
|
|
||||||
|
### 2. Simulation Notebooks
|
||||||
|
|
||||||
|
* [V3 - 1Hive model](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Aragon_Conviction_Voting_Model.ipynb): The latest notebook iteration of CV, modeling 1Hive's deployment
|
||||||
|
* [V2 - Increased complexity model](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v2/Aragon_Conviction_Voting_Model.ipynb): a former version of the CV model with increased mechanism complexity over v1
|
||||||
|
* [V1 - Initial model](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v1/Aragon_Conviction_Voting_Model.ipynb): the simplest version of the CV model. Start here if you are looking to understand and replicate this model in cadCAD
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
___
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
# Background information & concepts addressed
|
||||||
|
|
||||||
|
## What is this cadCAD model trying to do?
|
||||||
|
In cyber-physical systems like international power grids, global flight networks, or socioeconomic community ecosystems, engineers model simulated replicas of their system, called digital twins. These models help to manage the complexity of systems that have trillions of data points and are constantly in flux. These simulations channel the information into pathways that allow humans to understand what is going on in their ecosystem at a high level, so they can intervene where and as appropriate. (Like hitting a breaker switch when a fault is cleared in a power system).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Digital twins can be considered like a flight simulator, which can be used to run your system through a billion different "tests", varying one parameter at a time, to see what effects may throw your system out of balance. As engineers with public safety in mind, we must understand the tipping points of our systems, and ensure mechanisms are in place to push the system back towards balance if and when they enter their boundary conditions of safety.
|
||||||
|
|
||||||
|
This cadCAD model is a digital twin of Conviction Voting, as applied in the 1Hive DAO ecosystem. It can be used to provide operational support in decision making both during the design stage, and also in the continuous governance of the 1Hive system, providing [Computer Aided Governance](https://medium.com/block-science/computer-aided-governance-cag-a-revolution-in-automated-decision-support-systems-9faa009e57a2) for 1Hive members.
|
||||||
|
|
||||||
|
The notebooks contained here are a mix of code snippets, explainer content, simulations, and a whole lot of background to get you more familiar with CV as a concept, and perhaps even diving into modelling similar systems, or extending this model even further using cadCAD. If you have any questions about this model or how to build with it in cadCAD, feel free to email jeff@block.science.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Conviction Voting Basics
|
||||||
|
|
||||||
|
[Conviction Voting](https://medium.com/commonsstack/conviction-voting-a-novel-continuous-decision-making-alternative-to-governance-62e215ad2b3d) 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 a set threshold specified by a proposal according to the amount of funds requested, at which point it passes and funds are released so work may begin. Conviction voting improves on discrete voting processes by allowing participants to vote at any time, and eliminates the need for consensus on each proposal. This eliminates the governance bottleneck of large distributed communities, where a quorum of participants is required to vote on every proposal.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Legacy voting systems face several difficulties in transforming private, distributed, continuous and time varying individual signals (e.g. desiring our roads to be safer) into public, centralized, discrete and event-based collective outcomes (e.g. filling potholes on streets in your neighbourhood). Conviction Voting is a real-time governance tool designed to aggregate collective preferences, expressed continuously.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
As our governance toolkits continue to expand with novel tools like Conviction Voting, we can consider designing governance systems in the context of the community to which they belong. In the 1Hive community, holding Honey tokens gives you certain rights in the 1Hive organization. Below, we consider the rights granted, how those rights are controlled, the attack vectors they present, and how those vectors can be mitigated.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Conviction Voting offers us new insight into the collective intent of our communities. It offers us a richer signal of the emergent and dynamic preferences of a group, such that we can better understand and discuss important issues as communities. It eliminates attack vectors of ad hoc voting such as last minute vote swings, and reduces user friction by not requiring set times to cast a vote.
|
||||||
|
|
||||||
|
## Different Flavors of Conviction Voting
|
||||||
|
|
||||||
|
The design space for this new governance tool is wide and unexplored. From its academic origins in Dr. Zargham's PhD research in multi agent coordination systems, Conviction Voting was called [Social Sensor Fusion](https://github.com/BlockScience/conviction/blob/master/social-sensorfusion.pdf) and was a continuous 'fusion' of individual desires into a collective sentiment signal. This suggests there could be multiple "flavors" of Conviction Voting:
|
||||||
|
|
||||||
|
* **Discrete proposal CV**: Like the 1Hive or Commons Stack model, this version of CV fuses continuous preferences into a conviction signal, passing the proposal at a specific point in time, when sufficient community support has been reached.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* **Continuous parameter CV:** A community may wish to have certain aspects of their socioeconomic system to be continuously decided by collective sentiment. Perhaps the rate of a community token entry/exit (Tobin) tax, or the rate of community UBI.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
There are likely to be many more useful applications of this real-time governance tool in community decision making and beyond. We look forward to continuing this research and creating the open source foundations of models which can be iterated towards widely varying scenarios for creating high leverage impact.
|
||||||
|
|
||||||
|
## Conviction Voting In-Depth
|
||||||
|
Conviction voting is based on a linear system akin to a capacitor which "charges up" dynamically and proposals pass when a certain level of collective energy is reached. The details are explained and demonstrated throughout this repo but the best place to start is the [Conviction Voting Algorithm Overview](algorithm_overview.md). For more details on the charging up mechanics and the alpha parameter see the [Deriving Alpha Parameter Explainer](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Deriving_Alpha.ipynb) notebook and for more details on the proposal passing mechanism, see the [Trigger Function Explainer](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Trigger_Function_Explanation.ipynb) notebook.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
# Current CV Experiments
|
||||||
|
|
||||||
|
## 1Hive
|
||||||
|
The [1Hive](https://www.1hive.org) community has been actively developing Conviction Voting contracts in collaboration with BlockScience and the Commons Stack since early 2019. They currently have a DAO live on the xDAI network at [1hive.org](https://www.1hive.org) that uses a native governance token (Honey) to allocate funds to proposals via Conviction Voting.
|
||||||
|
|
||||||
|
To see Conviction Voting deployed in smart contracts with a basic user interface, check out the [1Hive Github](https://github.com/1Hive/conviction-voting-app).
|
||||||
|
|
||||||
|
## Commons Simulator
|
||||||
|
|
||||||
|
The [Commons Stack](https://www.commonsstack.org) has been working on a 'Commons Simulator' to facilitate user understanding of these novel governance tools. Progress on Conviction Voting can be viewed in [the Commons Stack Github repo](https://github.com/commons-stack/coodcad/tree/bigrewrite).
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
## What is cadCAD?
|
## What is cadCAD?
|
||||||
cadCAD (complex adaptive dynamics Computer-Aided Design) is a python based modeling framework for research, validation, and Computer Aided Design of complex systems. Given a model of a complex system, cadCAD can simulate the impact that a set of actions might have on it. This helps users make informed, rigorously tested decisions on how best to modify or interact with the system in order to achieve their goals. cadCAD supports different system modeling approaches and can be easily integrated with common empirical data science workflows. Monte Carlo methods, A/B testing and parameter sweeping features are natively supported and optimized for.
|
cadCAD (complex adaptive dynamics Computer-Aided Design) is a python based modeling framework for research, validation, and Computer Aided Design of complex systems. Given a model of a complex system, cadCAD can simulate the impact that a set of actions might have on it. This helps users make informed, rigorously tested decisions on how best to modify or interact with the system in order to achieve their goals. cadCAD supports different system modeling approaches and can be easily integrated with common empirical data science workflows. Monte Carlo methods, A/B testing and parameter sweeping features are natively supported and optimized for.
|
||||||
|
|
||||||
|
cadCAD links:
|
||||||
|
* https://community.cadcad.org/t/introduction-to-cadcad/15
|
||||||
|
* https://community.cadcad.org/t/putting-cadcad-in-context/19
|
||||||
|
* https://github.com/cadCAD-org/demos
|
||||||
|
|
||||||
## Reproducibility
|
<br>
|
||||||
In order to reperform this code, we recommend the researcher use the following link to download https://www.anaconda.com/products/individual to download Python 3.7.To install the specific version of cadCAD this repository was built with, run the following code:
|
|
||||||
pip install cadCAD==0.4.17
|
## Model Reproducibility
|
||||||
|
In order to reperform this code, we recommend the researcher use the following link https://www.anaconda.com/products/individual to download Python 3.7. To install the specific version of cadCAD this repository was built with, run the following code:
|
||||||
|
pip install cadCAD==0.4.21
|
||||||
|
|
||||||
Then run cd Aragon_Conviction_Voting to enter the repository. Finally, run jupyter notebook to open a notebook server to run the various notebooks in this repository.
|
Then run cd Aragon_Conviction_Voting to enter the repository. Finally, run jupyter notebook to open a notebook server to run the various notebooks in this repository.
|
||||||
|
|
||||||
## Simulations
|
Check out the [cadCAD forum](https://community.cadcad.org/t/python-newbies-setup-for-cadcad/101) for more information about installing and using cadCAD.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## Background information & concepts addressed
|
___
|
||||||
|
|
||||||
|
## Further Background Reading
|
||||||
|
|
||||||
### Systems Thinking
|
### Systems Thinking
|
||||||
* https://community.cadcad.org/t/introduction-to-systems-thinking/18
|
* https://community.cadcad.org/t/introduction-to-systems-thinking/18
|
||||||
* https://community.cadcad.org/t/working-glossary-of-systems-concepts/17
|
* https://community.cadcad.org/t/working-glossary-of-systems-concepts/17
|
||||||
|
|
||||||
|
|
||||||
### cadCAD
|
|
||||||
|
|
||||||
* https://community.cadcad.org/t/introduction-to-cadcad/15
|
|
||||||
* https://community.cadcad.org/t/putting-cadcad-in-context/19
|
|
||||||
* https://github.com/BlockScience/cadCAD/tree/master/tutorials
|
|
||||||
|
|
||||||
|
|
||||||
### Token Engineering
|
### Token Engineering
|
||||||
|
|
||||||
* https://blog.oceanprotocol.com/towards-a-practice-of-token-engineering-b02feeeff7ca
|
* https://blog.oceanprotocol.com/towards-a-practice-of-token-engineering-b02feeeff7ca
|
||||||
|
|
@ -37,10 +128,11 @@ Then run cd Aragon_Conviction_Voting to enter the repository. Finally, run jupyt
|
||||||
|
|
||||||
### Complex systems
|
### Complex systems
|
||||||
|
|
||||||
|
* https://ergodicityeconomics.com/lecture-notes/
|
||||||
* https://www.frontiersin.org/articles/10.3389/fams.2015.00007/full
|
* https://www.frontiersin.org/articles/10.3389/fams.2015.00007/full
|
||||||
* https://epub.wu.ac.at/7433/1/zargham_paruch_shorish.pdf
|
* https://epub.wu.ac.at/7433/1/zargham_paruch_shorish.pdf
|
||||||
|
|
||||||
|
|
||||||
### Systems Engineering
|
### Systems Engineering
|
||||||
|
|
||||||
* http://systems.hitchins.net/systems-engineering/se-monographs/seessence.pdf
|
* http://systems.hitchins.net/systems-engineering/se-monographs/seessence.pdf
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
# Mathematically Formalizing the Conviction Voting Algorithm
|
||||||
|
|
||||||
|
## Background
|
||||||
|
---
|
||||||
|
Conviction Voting is an approach to organizing a communities preferences into discrete decisions in the management of that communities resources. Strictly speaking conviction voting is less like voting and more like signal processing. Framing the approach and the initial algorithm design was done by Michael Zargham and published in a short research proposal [Social Sensor Fusion](https://github.com/BlockScience/conviction/blob/master/social-sensorfusion.pdf). This work is based on a dynamic resource allocation algorithm presented in Dr. Zargham's PhD Thesis.
|
||||||
|
|
||||||
|
The work proceeded in collaboration with the Commons Stack, including expanding on the python implementation to makeup part of the Commons Simulator game. An implemention of Conviction Voting as a smart contract within the Aragon Framework was developed by [1Hive](https://1hive.org/) and is currently being used for community decision making around allocations their community currency, Honey.
|
||||||
|
|
||||||
|
## Defining the Word Problem
|
||||||
|
___
|
||||||
|
|
||||||
|
Suppose a group of people want to coordinate to make a collective decision. Social dynamics such as discussions, signaling, and even changing ones mind based on feedback from others input play an important role in these processes. While the actual decision making process involves a lot of informal processes, in order to be fair the ultimate decision making process still requires a set of formal rules that the community collecively agrees to, which serves to functionally channel a plurality of preferences into a discrete outcomes. In our case we are interested in a procedure which supports asynchronous interactions, an provides visibility into likely outcomes prior to their resolution to serve as a driver of good faith, debate and healthy forms of coalition building. Furthermore, participations should be able to show support for multiple initiatives, and to vary the level of support shown. Participants a quantity of signaling power which may be fixed or variable, homogenous or heterogenous. For the purpose of this document, we'll focus on the case where the discrete decisions to be made are decisions to allocate funds from a shared funding pool towards projects of interest to the community.
|
||||||
|
|
||||||
|
## Converting to a Math Problem
|
||||||
|
___
|
||||||
|
|
||||||
|
Let's start taking these words and constructing a mathematical representation that supports a design that meets the description above. To start we need to define participants.
|
||||||
|
|
||||||
|
### Defining the Participants
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
Let $\mathcal{A}$ be the set of participants. Consider a participant $a\in \mathcal{A}$. Any participant $a$ has some capacity to participate in the voting process $h[a]$. In a fixed quantity, homogenous system $h[a] = h$ for all $a\in \mathcal{A}$ where $h$ is a constant. The access control process managing how one becomes a participant determines the total supply of "votes" $S = \sum_{a\in \mathcal{A}} = n\cdot h$ where the number of participants is $n = |\mathcal{A}|$. In a smart contract setting, the set $\mathcal{A}$ is a set of addresses, and $h[a]$ is a quantity of tokens held by each address $a\in \mathcal{A}$.
|
||||||
|
|
||||||
|
### Defining Proposals & Shared Resources
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
Next, we introduce the idea of proposals. Consider a proposal $i\in \mathcal{C}$. Any proposal $i$ is associated with a request for resources $r[i]$. Those requested resources would be allocated from a constrained pool of communal resources currently totaling $R$. The pool of resources may become depleted because when a proposal $i$ passes $R^+= R-r[i]$. Therefore it makes sense for us to consider what fraction of the shared resources are being request $\mu_i = \frac{r[i]}{R}$, which means that thre resource depletion from passing proposals can be bounded by requiring $\mu_i < \mu$ where $\mu$ is a constant representing the maximum fraction of the shared resources which can be dispersed by any one proposal. In order for the system to be sustainable a source of new resources is required. In the case where $R$ is funding, new funding can come from revenues, donations, or in some DAO use cases minting tokens.
|
||||||
|
|
||||||
|
### Defining Participants Preferences for Proposals
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
Most of the interesting information in this system is distributed amongst the participants and it manifests as preferences over the proposals. This can be thought of as a matrix $W\in \mathbb{R}^{n \times m}$.
|
||||||
|

|
||||||
|
|
||||||
|
These private hidden signals drive discussions and voting actions. Each participant individually decides how to allocate their votes across the available proposals. Participant $a$ supports proposal $i$ by setting $x[a,i]>0$ but they are limited by their capacity $\sum_{k\in \mathcal{C}} x[a,k] \le h[a]$. Assuming each participant chooses a subset of the proposals to support, a support graph is formed.
|
||||||
|

|
||||||
|
|
||||||
|
## Aggregating Information
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
In order to break out of the synchronous ad-hoc voting model, a dynamical systems model of this system is introduced that fuses collective community preferences into a single signal. The mathematical derivation of this process can be found below.
|
||||||
|
|
||||||
|
### Participants Allocate Voting Power
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|

|
||||||
|
In the above diagram, we examine the participant view. Participant $a$ with holdings $h$ at time $t$ supports proposals $i$ and $j$ with $x$ conviction. The sum of all conviction asserted by participant $a$ is between 0 and the total holdings of participant $a$.
|
||||||
|
|
||||||
|
### System Accounting of Proposal Conviction
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In the above diagram, we examine the proposal view. Proposal $j$ with total conviction $y$ at time $t$ is supported by participants $a$, $b$ and $c$ with $x$ conviction. The total conviction $y$ at time $t+1$ is equal to the total conviction at time $t$ decremented by an exponential decay $\\alpha$ plus the sum of all conviction from $k$ agents in time step $t$.
|
||||||
|
|
||||||
|
### Understanding the Alpha Parameter
|
||||||
|
___
|
||||||
|
For a deeper exploration of the $alpha$ parameter, please read more in the [Deriving Alpha notebook](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Deriving_Alpha.ipynb)
|
||||||
|
|
||||||
|
|
||||||
|
## Converting Signals to Discrete Decisions
|
||||||
|
___
|
||||||
|
|
||||||
|
Conviction can be considered like a fluctuating kinetic energy, with the Trigger function acting as a required activation energy for proposals to pass. This is the mechanism by which a continuous community preference turns into a discrete action event: passing a proposal. See the [Trigger Function Explanation notebook](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Trigger_Function_Explanation.ipynb) for more details around the trigger function and how it works.
|
||||||
|
|
||||||
|
### The Trigger Function
|
||||||
|
___
|
||||||
|
For a deeper exploration of the trigger function, please read more in the [Trigger Function Explainer notebook](https://nbviewer.jupyter.org/github/BlockScience/Aragon_Conviction_Voting/blob/master/models/v3/Trigger_Function_Explanation.ipynb)
|
||||||
|
|
||||||
|
|
||||||
|
### Resolving Passed Proposals
|
||||||
|
___
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Social Systems Modeling
|
||||||
|
___
|
||||||
|
|
||||||
|
In the conviction voting model, multiple graph structures are used to represent participants and proposals to represent a subjective, exploratory modeling of the social system interacting.
|
||||||
|
|
||||||
|
### Sentiment:
|
||||||
|
|
||||||
|
* Global Sentiment denotes the outside world appreciating the output of the community.
|
||||||
|
* Local Sentiment denotes the agents within the system feeling good about output of the community.
|
||||||
|
* Sentiment increases when proposals pass and work is completed in the community, and decreases when proposals fail and community progress stalls.
|
||||||
|
|
||||||
|
### Relationships between Participants:
|
||||||
|
|
||||||
|
* Edges from participant to participant denote influence (to represent subjective social influence) and are assigned randomly via mixing processes.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Relationships between Proposals
|
||||||
|
|
||||||
|
* Edges from proposal to proposal represent conflict, which can be positive or negative.
|
||||||
|
* Some proposals are synergistic (passing one makes the other more desirable).
|
||||||
|
* Some proposals are (partially) substitutable (passing one makes the other less desirable).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Glossary of Notation
|
||||||
|
___
|
||||||
|
### Summary of State Variables
|
||||||
|
|
||||||
|
Notation | Definition|
|
||||||
|
|--- | --- |
|
||||||
|
|$\mathcal{A}_t$ | |
|
||||||
|
|$\mathcal{C}_t$ | |
|
||||||
|
|$n_t$ | |
|
||||||
|
|$m_t$ | |
|
||||||
|
|$W_t$ | |
|
||||||
|
|$X_t$ | Individual participant conviction at time t|
|
||||||
|
|$y_t$ | Total aggregated community conviction at time t|
|
||||||
|
|$y^*_t$ | |
|
||||||
|
|$R_t$ | Available resources in the communal funding pool|
|
||||||
|
|$S_t$ | Effective supply of tokens available for community governance|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Summary Laws of Motion / State Transition
|
||||||
|
|
||||||
|
* A new address $a$ joins the community of participants:
|
||||||
|
$\mathcal{A}_{t+1} = \mathcal{A}_t \cup \{a\}$
|
||||||
|
$h_{t+1}[a]= \Delta h >0$
|
||||||
|
|
||||||
|
* An address $a$ leaves the community of participants:
|
||||||
|
$\mathcal{A}_{t+1} = \mathcal{A}_t \backslash \{a\}$
|
||||||
|
$h_{t+1}[a]= 0$
|
||||||
|
|
||||||
|
* A proposal $i$ is added to the set of candidates
|
||||||
|
$\mathcal{C}_{t+1} = \mathcal{C}_t \cup \{i\}$
|
||||||
|
|
||||||
|
* A proposal $i$ is removed from the set of candidates
|
||||||
|
$\mathcal{C}_{t+1} = \mathcal{C}_t \backslash\{i\}$
|
||||||
|
|
||||||
|
* Resources are added to the shared resource pool
|
||||||
|
$R_{t+1}= R_t+ \Delta r$
|
||||||
|
|
||||||
|
* Update Conviction Required to pass proposals
|
||||||
|
$y^*_{t+1} = [\cdots ,f(\mu_i), \cdots]$
|
||||||
|
where $\mu_i = \frac{r[i]}{R_t}$
|
||||||
|
|
||||||
|
* A participant allocates their support
|
||||||
|
$X_{t+1}[a,: ] = [\cdots,x[a,i],\cdots]$
|
||||||
|
s.t. $\sum_{i\in \mathcal{C}_t}x[a,i]\le h[a]$
|
||||||
|
|
||||||
|
* A proposal is passed given $y_t[i] \ge y^*_t[i]$
|
||||||
|
$\mathcal{C}_{t+1} = \mathcal{C}_t \backslash\{i\}$
|
||||||
|
$R_{t+1}= R_t- r[i]$
|
||||||
|
|
||||||
|
* Update Conviction
|
||||||
|
$y_{t+1}[i] =\alpha\cdot y_t[i] + \sum_{a\in \mathcal{A}_t} x[a, i]$
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
Notation | Definition|
|
||||||
|
|--- | --- |
|
||||||
|
|$\alpha$ | The decay rate for previously accumulated conviction |
|
||||||
|
|$\beta$ | Upper bound on share of funds dispersed in the example Trigger Function|
|
||||||
|
|$f(z)$| Trigger function that determines when a proposal has sufficient conviction to pass|
|
||||||
|
|$\rho$ | Scale Parameter for the example Trigger Function |
|
||||||
|
|
||||||
|
Recall that the Trigger Function, $f(z)$ satisfies $f:[0,1]\rightarrow \mathbb{R}_+$
|
||||||
|
e.g. $f(z) = \frac{\rho S }{(1-\alpha)(z-\beta)^2}$
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Additional Considerations when Deploying CV
|
||||||
|
|
||||||
|
* Timescales
|
||||||
|
* whether your system is operating in block times, or more human understandable timescales like hours, days or weeks, these considerations need to be factored into your model
|
||||||
|
* Minimum candidacy times
|
||||||
|
* proposals should be active for a minimum period to ensure appropriate dialog occurs within the community, regardless of level of support
|
||||||
|
* Minimum conviction required for small proposals
|
||||||
|
* to prevent small proposal spamming from draining the communal funding pool, all proposals should have some minimum conviction required to pass
|
||||||
|
* Effective supply
|
||||||
|
* to avoid slow conviction aggregation due to "inactive" tokens (e.g. locked up in cold storage or liquidity pool, without active participation in governance), effective supply is the portion of tokens that are active in community governance
|
||||||
|
* Proposal statuses
|
||||||
|
* proposals can exist in multiple status types, which can be expanded as this model grows
|
||||||
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
|
@ -4,7 +4,7 @@ from datetime import timedelta
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from cadCAD.configuration import append_configs
|
from cadCAD.configuration import Experiment
|
||||||
from cadCAD.configuration.utils import bound_norm_random, ep_time_step, config_sim, access_block
|
from cadCAD.configuration.utils import bound_norm_random, ep_time_step, config_sim, access_block
|
||||||
|
|
||||||
from .genesis_states import genesis_states
|
from .genesis_states import genesis_states
|
||||||
|
|
@ -21,7 +21,9 @@ seeds = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
append_configs(
|
exp = Experiment()
|
||||||
|
|
||||||
|
exp.append_configs(
|
||||||
sim_configs=sim_config,
|
sim_configs=sim_config,
|
||||||
initial_state=genesis_states,
|
initial_state=genesis_states,
|
||||||
seeds=seeds,
|
seeds=seeds,
|
||||||
|
|
@ -30,6 +32,7 @@ append_configs(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_configs():
|
def get_configs():
|
||||||
'''
|
'''
|
||||||
Function to extract the configuration information for display in a notebook.
|
Function to extract the configuration information for display in a notebook.
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# v1 readme
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
-- specific cadCAD versions
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
-- difference from prior conviction model in the other repo (zargham's early R&D model)
|
||||||
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
|
@ -0,0 +1,43 @@
|
||||||
|
import math
|
||||||
|
from decimal import Decimal
|
||||||
|
from datetime import timedelta
|
||||||
|
import numpy as np
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
from cadCAD.configuration import Experiment
|
||||||
|
from cadCAD.configuration.utils import bound_norm_random, ep_time_step, config_sim, access_block
|
||||||
|
|
||||||
|
from .genesis_states import genesis_states
|
||||||
|
from .partial_state_update_block import partial_state_update_blocks
|
||||||
|
|
||||||
|
|
||||||
|
sim_config = config_sim({
|
||||||
|
'N': 1,
|
||||||
|
'T': range(60), #day
|
||||||
|
})
|
||||||
|
|
||||||
|
seeds = {
|
||||||
|
'p': np.random.RandomState(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exp = Experiment()
|
||||||
|
|
||||||
|
exp.append_configs(
|
||||||
|
sim_configs=sim_config,
|
||||||
|
initial_state=genesis_states,
|
||||||
|
seeds=seeds,
|
||||||
|
partial_state_update_blocks=partial_state_update_blocks
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_configs():
|
||||||
|
'''
|
||||||
|
Function to extract the configuration information for display in a notebook.
|
||||||
|
'''
|
||||||
|
|
||||||
|
sim_config,genesis_states,seeds,partial_state_update_blocks
|
||||||
|
|
||||||
|
return sim_config,genesis_states,seeds,partial_state_update_blocks
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
from .model.initialization import *
|
||||||
|
|
||||||
|
|
||||||
|
genesis_states = {
|
||||||
|
'network':network,
|
||||||
|
'funds':initial_funds,
|
||||||
|
'sentiment': initial_sentiment,
|
||||||
|
'supply':supply
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,665 @@
|
||||||
|
import networkx as nx
|
||||||
|
from scipy.stats import expon, gamma
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.colors as colors
|
||||||
|
import matplotlib.cm as cmx
|
||||||
|
import seaborn as sns
|
||||||
|
|
||||||
|
#beta = .2 #later we should set this to be param so we can sweep it
|
||||||
|
# tuning param for the trigger function
|
||||||
|
#rho = .001
|
||||||
|
#alpha = 1 - 0.9999599
|
||||||
|
|
||||||
|
|
||||||
|
def trigger_threshold(requested, funds, supply, beta, rho, alpha):
|
||||||
|
'''
|
||||||
|
Function that determines threshold for proposals being accepted.
|
||||||
|
'''
|
||||||
|
share = requested/funds
|
||||||
|
if share < beta:
|
||||||
|
threshold = rho*supply/(beta-share)**2 * 1/(1-alpha)
|
||||||
|
return threshold
|
||||||
|
else:
|
||||||
|
return np.inf
|
||||||
|
|
||||||
|
def initial_social_network(network, scale = 1, sigmas=3):
|
||||||
|
'''
|
||||||
|
Function to initialize network x social network edges
|
||||||
|
'''
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
|
||||||
|
for i in participants:
|
||||||
|
for j in participants:
|
||||||
|
if not(j==i):
|
||||||
|
influence_rv = expon.rvs(loc=0.0, scale=scale)
|
||||||
|
if influence_rv > scale+sigmas*scale**2:
|
||||||
|
network.add_edge(i,j)
|
||||||
|
network.edges[(i,j)]['influence'] = influence_rv
|
||||||
|
network.edges[(i,j)]['type'] = 'influence'
|
||||||
|
return network
|
||||||
|
|
||||||
|
def initial_conflict_network(network, rate = .25):
|
||||||
|
'''
|
||||||
|
Definition:
|
||||||
|
Function to initialize network x conflict edges
|
||||||
|
'''
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
|
||||||
|
for i in proposals:
|
||||||
|
for j in proposals:
|
||||||
|
if not(j==i):
|
||||||
|
conflict_rv = np.random.rand()
|
||||||
|
if conflict_rv < rate :
|
||||||
|
network.add_edge(i,j)
|
||||||
|
network.edges[(i,j)]['conflict'] = 1-conflict_rv
|
||||||
|
network.edges[(i,j)]['type'] = 'conflict'
|
||||||
|
return network
|
||||||
|
|
||||||
|
def gen_new_participant(network, new_participant_holdings):
|
||||||
|
'''
|
||||||
|
Definition:
|
||||||
|
Driving processes for the arrival of participants.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
network: networkx object
|
||||||
|
new_participant_holdings: Tokens of new participants
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
Initialized network x object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Update network x object
|
||||||
|
'''
|
||||||
|
|
||||||
|
i = len([node for node in network.nodes])
|
||||||
|
|
||||||
|
network.add_node(i)
|
||||||
|
network.nodes[i]['type']="participant"
|
||||||
|
|
||||||
|
s_rv = np.random.rand()
|
||||||
|
network.nodes[i]['sentiment'] = s_rv
|
||||||
|
network.nodes[i]['holdings']=new_participant_holdings
|
||||||
|
|
||||||
|
for j in get_nodes_by_type(network, 'proposal'):
|
||||||
|
network.add_edge(i, j)
|
||||||
|
|
||||||
|
a_rv = a_rv = np.random.uniform(-1,1,1)[0]
|
||||||
|
network.edges[(i, j)]['affinity'] = a_rv
|
||||||
|
network.edges[(i,j)]['tokens'] = a_rv*network.nodes[i]['holdings']
|
||||||
|
network.edges[(i, j)]['conviction'] = 0
|
||||||
|
network.edges[(i,j)]['type'] = 'support'
|
||||||
|
|
||||||
|
return network
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def gen_new_proposal(network, funds, supply, beta, rho, alpha, funds_requested):
|
||||||
|
'''
|
||||||
|
Definition:
|
||||||
|
Driving processes for the arrival of proposals.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
network: networkx object
|
||||||
|
funds:
|
||||||
|
supply:
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
Initialized network x object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Update network x object
|
||||||
|
'''
|
||||||
|
j = len([node for node in network.nodes])
|
||||||
|
network.add_node(j)
|
||||||
|
network.nodes[j]['type']="proposal"
|
||||||
|
|
||||||
|
network.nodes[j]['conviction']=0
|
||||||
|
network.nodes[j]['status']='candidate'
|
||||||
|
network.nodes[j]['age']=0
|
||||||
|
|
||||||
|
# rescale = funds*scale_factor
|
||||||
|
# r_rv = gamma.rvs(1.5,loc=0.001, scale=rescale)
|
||||||
|
network.nodes[j]['funds_requested'] =funds_requested
|
||||||
|
|
||||||
|
network.nodes[j]['trigger']= trigger_threshold(funds_requested, funds, supply, beta, rho, alpha)
|
||||||
|
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
proposing_participant = np.random.choice(participants)
|
||||||
|
|
||||||
|
for i in participants:
|
||||||
|
network.add_edge(i, j)
|
||||||
|
if i==proposing_participant:
|
||||||
|
network.edges[(i, j)]['affinity']=1
|
||||||
|
else:
|
||||||
|
a_rv = np.random.uniform(-1,1,1)[0]
|
||||||
|
network.edges[(i, j)]['affinity'] = a_rv
|
||||||
|
|
||||||
|
network.edges[(i, j)]['conviction'] = 0
|
||||||
|
network.edges[(i,j)]['tokens'] = 0
|
||||||
|
network.edges[(i,j)]['type'] = 'support'
|
||||||
|
|
||||||
|
return network
|
||||||
|
|
||||||
|
|
||||||
|
def get_nodes_by_type(g, node_type_selection):
|
||||||
|
'''
|
||||||
|
Definition:
|
||||||
|
Function to extract nodes based by named type
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
g: network x object
|
||||||
|
node_type_selection: node type
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List column of the desired information as:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
|
||||||
|
'''
|
||||||
|
return [node for node in g.nodes if g.nodes[node]['type']== node_type_selection ]
|
||||||
|
|
||||||
|
def get_sentimental(sentiment, force, decay=.1):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
mu = decay
|
||||||
|
sentiment = sentiment*(1-mu) + force*mu
|
||||||
|
|
||||||
|
if sentiment > 1:
|
||||||
|
sentiment = 1
|
||||||
|
elif sentiment < 0:
|
||||||
|
sentiment = 0
|
||||||
|
|
||||||
|
return sentiment
|
||||||
|
|
||||||
|
def get_edges_by_type(g, edge_type_selection):
|
||||||
|
'''
|
||||||
|
Functions to extract edges based on type
|
||||||
|
'''
|
||||||
|
return [edge for edge in g.edges if g.edges[edge]['type']== edge_type_selection ]
|
||||||
|
|
||||||
|
|
||||||
|
def conviction_order(network, proposals):
|
||||||
|
'''
|
||||||
|
Function to sort conviction order
|
||||||
|
'''
|
||||||
|
ordered = sorted(proposals, key=lambda j:network.nodes[j]['conviction'] , reverse=True)
|
||||||
|
|
||||||
|
return ordered
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def social_links(network, participant, scale = 1):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
|
||||||
|
i = participant
|
||||||
|
for j in participants:
|
||||||
|
if not(j==i):
|
||||||
|
influence_rv = expon.rvs(loc=0.0, scale=scale)
|
||||||
|
if influence_rv > scale+scale**2:
|
||||||
|
network.add_edge(i,j)
|
||||||
|
network.edges[(i,j)]['influence'] = influence_rv
|
||||||
|
network.edges[(i,j)]['type'] = 'influence'
|
||||||
|
return network
|
||||||
|
|
||||||
|
|
||||||
|
def conflict_links(network,proposal ,rate = .25):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
|
||||||
|
i = proposal
|
||||||
|
for j in proposals:
|
||||||
|
if not(j==i):
|
||||||
|
conflict_rv = np.random.rand()
|
||||||
|
if conflict_rv < rate :
|
||||||
|
network.add_edge(i,j)
|
||||||
|
network.edges[(i,j)]['conflict'] = 1-conflict_rv
|
||||||
|
network.edges[(i,j)]['type'] = 'conflict'
|
||||||
|
return network
|
||||||
|
|
||||||
|
def social_affinity_booster(network, proposal, participant):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
influencers = get_edges_by_type(network, 'influence')
|
||||||
|
|
||||||
|
j=proposal
|
||||||
|
i=participant
|
||||||
|
|
||||||
|
i_tokens = network.nodes[i]['holdings']
|
||||||
|
|
||||||
|
influence = np.array([network.edges[(i,node)]['influence'] for node in participants if (i, node) in influencers ])
|
||||||
|
#print(influence)
|
||||||
|
tokens = np.array([network.edges[(node,j)]['tokens'] for node in participants if (i, node) in influencers ])
|
||||||
|
#print(tokens)
|
||||||
|
|
||||||
|
|
||||||
|
influence_sum = np.sum(influence)
|
||||||
|
|
||||||
|
if influence_sum>0:
|
||||||
|
boosts = np.sum(tokens*influence)/(influence_sum*i_tokens)
|
||||||
|
else:
|
||||||
|
boosts = 0
|
||||||
|
|
||||||
|
return np.sum(boosts)
|
||||||
|
|
||||||
|
|
||||||
|
def snap_plot(nets, size_scale = 1/10, dims = (30,30), savefigs=False):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
|
||||||
|
last_net = nets[-1]
|
||||||
|
|
||||||
|
last_props=get_nodes_by_type(last_net, 'proposal')
|
||||||
|
M = len(last_props)
|
||||||
|
last_parts=get_nodes_by_type(last_net, 'participant')
|
||||||
|
N = len(last_parts)
|
||||||
|
pos = {}
|
||||||
|
|
||||||
|
for ind in range(N):
|
||||||
|
i = last_parts[ind]
|
||||||
|
pos[i] = np.array([0, 2*ind-N])
|
||||||
|
|
||||||
|
for ind in range(M):
|
||||||
|
j = last_props[ind]
|
||||||
|
pos[j] = np.array([1, 2*N/M *ind-N])
|
||||||
|
|
||||||
|
|
||||||
|
if savefigs:
|
||||||
|
counter = 0
|
||||||
|
length = 10
|
||||||
|
|
||||||
|
for net in nets:
|
||||||
|
edges = get_edges_by_type(net, 'support')
|
||||||
|
max_tok = np.max([net.edges[e]['tokens'] for e in edges])
|
||||||
|
|
||||||
|
E = len(edges)
|
||||||
|
|
||||||
|
net_props = get_nodes_by_type(net, 'proposal')
|
||||||
|
net_parts = get_nodes_by_type(net, 'participant')
|
||||||
|
net_node_label ={}
|
||||||
|
|
||||||
|
num_nodes = len([node for node in net.nodes])
|
||||||
|
|
||||||
|
node_color = np.empty((num_nodes,4))
|
||||||
|
node_size = np.empty(num_nodes)
|
||||||
|
|
||||||
|
edge_color = np.empty((E,4))
|
||||||
|
cm = plt.get_cmap('Reds')
|
||||||
|
|
||||||
|
cNorm = colors.Normalize(vmin=0, vmax=max_tok)
|
||||||
|
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
|
||||||
|
|
||||||
|
net_cand = [j for j in net_props if net.nodes[j]['status']=='candidate']
|
||||||
|
|
||||||
|
for j in net_props:
|
||||||
|
node_size[j] = net.nodes[j]['funds_requested']*size_scale
|
||||||
|
if net.nodes[j]['status']=="candidate":
|
||||||
|
node_color[j] = colors.to_rgba('blue')
|
||||||
|
trigger = net.nodes[j]['trigger']
|
||||||
|
conviction = net.nodes[j]['conviction']
|
||||||
|
percent_of_trigger = " "+str(int(100*conviction/trigger))+'%'
|
||||||
|
net_node_label[j] = str(percent_of_trigger)
|
||||||
|
elif net.nodes[j]['status']=="active":
|
||||||
|
node_color[j] = colors.to_rgba('orange')
|
||||||
|
net_node_label[j] = ''
|
||||||
|
elif net.nodes[j]['status']=="completed":
|
||||||
|
node_color[j] = colors.to_rgba('green')
|
||||||
|
net_node_label[j] = ''
|
||||||
|
elif net.nodes[j]['status']=="failed":
|
||||||
|
node_color[j] = colors.to_rgba('gray')
|
||||||
|
net_node_label[j] = ''
|
||||||
|
elif net.nodes[j]['status']=="killed":
|
||||||
|
node_color[j] = colors.to_rgba('black')
|
||||||
|
net_node_label[j] = ''
|
||||||
|
|
||||||
|
for i in net_parts:
|
||||||
|
node_size[i] = net.nodes[i]['holdings']*size_scale/10
|
||||||
|
node_color[i] = colors.to_rgba('red')
|
||||||
|
net_node_label[i] = ''
|
||||||
|
|
||||||
|
included_edges = []
|
||||||
|
for ind in range(E):
|
||||||
|
e = edges[ind]
|
||||||
|
tokens = net.edges[e]['tokens']
|
||||||
|
edge_color[ind] = scalarMap.to_rgba(tokens)
|
||||||
|
if e[1] in net_cand:
|
||||||
|
included_edges.append(e)
|
||||||
|
|
||||||
|
|
||||||
|
iE = len(included_edges)
|
||||||
|
included_edge_color = np.empty((iE,4))
|
||||||
|
for ind in range(iE):
|
||||||
|
e = included_edges[ind]
|
||||||
|
tokens = net.edges[e]['tokens']
|
||||||
|
included_edge_color[ind] = scalarMap.to_rgba(tokens)
|
||||||
|
|
||||||
|
# nx.draw(net,
|
||||||
|
# pos=pos,
|
||||||
|
# node_size = node_size,
|
||||||
|
# node_color = node_color,
|
||||||
|
# edge_color = included_edge_color,
|
||||||
|
# edgelist=included_edges,
|
||||||
|
# labels = net_node_label)
|
||||||
|
# plt.title('Tokens Staked by Partipants to Proposals')
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
plt.figure()
|
||||||
|
nx.draw(net,
|
||||||
|
pos=pos,
|
||||||
|
node_size = node_size,
|
||||||
|
node_color = node_color,
|
||||||
|
edge_color = included_edge_color,
|
||||||
|
edgelist=included_edges,
|
||||||
|
labels = net_node_label)
|
||||||
|
plt.title('Tokens Staked by Partipants to Proposals')
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.axis('on')
|
||||||
|
plt.xticks([])
|
||||||
|
plt.yticks([])
|
||||||
|
if savefigs:
|
||||||
|
#plt.savefig('images/' + unique_id+'_fig'+str(counter)+'.png')
|
||||||
|
plt.savefig('images/snap/'+str(counter)+'.png',bbox_inches='tight')
|
||||||
|
|
||||||
|
counter = counter+1
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def pad(vec, length,fill=True):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
|
||||||
|
if fill:
|
||||||
|
padded = np.zeros(length,)
|
||||||
|
else:
|
||||||
|
padded = np.empty(length,)
|
||||||
|
padded[:] = np.nan
|
||||||
|
|
||||||
|
for i in range(len(vec)):
|
||||||
|
padded[i]= vec[i]
|
||||||
|
|
||||||
|
return padded
|
||||||
|
|
||||||
|
def make2D(key, data, fill=False):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
maxL = data[key].apply(len).max()
|
||||||
|
newkey = 'padded_'+key
|
||||||
|
data[newkey] = data[key].apply(lambda x: pad(x,maxL,fill))
|
||||||
|
reshaped = np.array([a for a in data[newkey].values])
|
||||||
|
|
||||||
|
return reshaped
|
||||||
|
|
||||||
|
|
||||||
|
def quantile_plot(xkey, ykey, dataframe, dq=.1, logy=False, return_df = False):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
qX = np.arange(0,1+dq,dq)
|
||||||
|
|
||||||
|
data = dataframe[[xkey,ykey]].copy()
|
||||||
|
|
||||||
|
qkeys = []
|
||||||
|
for q in qX:
|
||||||
|
qkey= 'quantile'+str(int(100*q))
|
||||||
|
#print(qkey)
|
||||||
|
data[qkey] = data[ykey].apply(lambda arr: np.quantile(arr,q) )
|
||||||
|
#print(data[qkey].head())
|
||||||
|
qkeys.append(qkey)
|
||||||
|
|
||||||
|
data[[xkey]+qkeys].plot(x=xkey, logy=logy)
|
||||||
|
|
||||||
|
plt.title(ykey + " Quantile Plot" )
|
||||||
|
plt.ylabel(ykey)
|
||||||
|
labels = [str(int(100*q))+"$^{th}$ Percentile" for q in qX ]
|
||||||
|
|
||||||
|
plt.legend(labels, ncol = 1,loc='center left', bbox_to_anchor=(1, .5))
|
||||||
|
if return_df:
|
||||||
|
return data
|
||||||
|
|
||||||
|
def affinities_plot(df, dims = (8.5, 11) ):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
last_net= df.network.values[-1]
|
||||||
|
last_props=get_nodes_by_type(last_net, 'proposal')
|
||||||
|
M = len(last_props)
|
||||||
|
last_parts=get_nodes_by_type(last_net, 'participant')
|
||||||
|
N = len(last_parts)
|
||||||
|
|
||||||
|
affinities = np.empty((N,M))
|
||||||
|
for i_ind in range(N):
|
||||||
|
for j_ind in range(M):
|
||||||
|
i = last_parts[i_ind]
|
||||||
|
j = last_props[j_ind]
|
||||||
|
affinities[i_ind][j_ind] = last_net.edges[(i,j)]['affinity']
|
||||||
|
|
||||||
|
fig, ax = plt.subplots(figsize=dims)
|
||||||
|
|
||||||
|
sns.heatmap(affinities.T,
|
||||||
|
xticklabels=last_parts,
|
||||||
|
yticklabels=last_props,
|
||||||
|
square=True,
|
||||||
|
cbar=True,
|
||||||
|
cmap = plt.cm.RdYlGn,
|
||||||
|
ax=ax)
|
||||||
|
|
||||||
|
plt.title('affinities between participants and proposals')
|
||||||
|
plt.ylabel('proposal_id')
|
||||||
|
plt.xlabel('participant_id')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def trigger_sweep(field, trigger_func,beta,rho,alpha,supply=10**9):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
xmax= beta
|
||||||
|
|
||||||
|
if field == 'effective_supply':
|
||||||
|
share_of_funds = np.arange(.001,xmax,.001)
|
||||||
|
total_supply = np.arange(0,supply*10, supply/100)
|
||||||
|
demo_data_XY = np.outer(share_of_funds,total_supply)
|
||||||
|
|
||||||
|
demo_data_Z0=np.empty(demo_data_XY.shape)
|
||||||
|
demo_data_Z1=np.empty(demo_data_XY.shape)
|
||||||
|
demo_data_Z2=np.empty(demo_data_XY.shape)
|
||||||
|
demo_data_Z3=np.empty(demo_data_XY.shape)
|
||||||
|
for sof_ind in range(len(share_of_funds)):
|
||||||
|
sof = share_of_funds[sof_ind]
|
||||||
|
for ts_ind in range(len(total_supply)):
|
||||||
|
ts = total_supply[ts_ind]
|
||||||
|
tc = ts /(1-alpha)
|
||||||
|
trigger = trigger_func(sof, 1, ts,beta,rho,alpha)
|
||||||
|
demo_data_Z0[sof_ind,ts_ind] = np.log10(trigger)
|
||||||
|
demo_data_Z1[sof_ind,ts_ind] = trigger
|
||||||
|
demo_data_Z2[sof_ind,ts_ind] = trigger/tc #share of maximum possible conviction
|
||||||
|
demo_data_Z3[sof_ind,ts_ind] = np.log10(trigger/tc)
|
||||||
|
return {'log10_trigger':demo_data_Z0,
|
||||||
|
'trigger':demo_data_Z1,
|
||||||
|
'share_of_max_conv': demo_data_Z2,
|
||||||
|
'log10_share_of_max_conv':demo_data_Z3,
|
||||||
|
'total_supply':total_supply,
|
||||||
|
'share_of_funds':share_of_funds,
|
||||||
|
'alpha':alpha}
|
||||||
|
elif field == 'alpha':
|
||||||
|
#note if alpha >.01 then this will give weird results max alpha will be >1
|
||||||
|
alpha = np.arange(0,.5,.001)
|
||||||
|
share_of_funds = np.arange(.001,xmax,.001)
|
||||||
|
demo_data_XY = np.outer(share_of_funds,alpha)
|
||||||
|
|
||||||
|
demo_data_Z4=np.empty(demo_data_XY.shape)
|
||||||
|
demo_data_Z5=np.empty(demo_data_XY.shape)
|
||||||
|
demo_data_Z6=np.empty(demo_data_XY.shape)
|
||||||
|
demo_data_Z7=np.empty(demo_data_XY.shape)
|
||||||
|
for sof_ind in range(len(share_of_funds)):
|
||||||
|
sof = share_of_funds[sof_ind]
|
||||||
|
for a_ind in range(len(alpha)):
|
||||||
|
ts = supply
|
||||||
|
a = alpha[a_ind]
|
||||||
|
tc = ts /(1-a)
|
||||||
|
trigger = trigger_func(sof, 1, ts, beta, rho, a)
|
||||||
|
demo_data_Z4[sof_ind,a_ind] = np.log10(trigger)
|
||||||
|
demo_data_Z5[sof_ind,a_ind] = trigger
|
||||||
|
demo_data_Z6[sof_ind,a_ind] = trigger/tc #share of maximum possible conviction
|
||||||
|
demo_data_Z7[sof_ind,a_ind] = np.log10(trigger/tc)
|
||||||
|
|
||||||
|
return {'log10_trigger':demo_data_Z4,
|
||||||
|
'trigger':demo_data_Z5,
|
||||||
|
'share_of_max_conv': demo_data_Z6,
|
||||||
|
'log10_share_of_max_conv':demo_data_Z7,
|
||||||
|
'alpha':alpha,
|
||||||
|
'share_of_funds':share_of_funds,
|
||||||
|
'supply':supply}
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "invalid field"
|
||||||
|
|
||||||
|
def trigger_plotter(share_of_funds,Z, color_label,y, ylabel,cmap='jet'):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
dims = (10, 5)
|
||||||
|
fig, ax = plt.subplots(figsize=dims)
|
||||||
|
|
||||||
|
cf = plt.contourf(share_of_funds, y, Z.T, 100, cmap=cmap)
|
||||||
|
cbar=plt.colorbar(cf)
|
||||||
|
plt.axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]])
|
||||||
|
#ax.set_xscale('log')
|
||||||
|
plt.ylabel(ylabel)
|
||||||
|
plt.xlabel('Share of Funds Requested')
|
||||||
|
plt.title('Trigger Function Map')
|
||||||
|
|
||||||
|
cbar.ax.set_ylabel(color_label)
|
||||||
|
|
||||||
|
def trigger_grid(supply_sweep, alpha_sweep):
|
||||||
|
|
||||||
|
fig, axs = plt.subplots(nrows=2, ncols=1,figsize=(20,20))
|
||||||
|
axs = axs.flatten()
|
||||||
|
|
||||||
|
# cut out the plots that didn't require the heatmap
|
||||||
|
# and switch to (2,1) subplots
|
||||||
|
|
||||||
|
# share_of_funds = alpha_sweep['share_of_funds']
|
||||||
|
# Z = alpha_sweep['log10_share_of_max_conv']
|
||||||
|
# y = alpha_sweep['alpha']
|
||||||
|
# ylabel = 'alpha'
|
||||||
|
|
||||||
|
# axs[0].contourf(share_of_funds, y, Z.T,100, cmap='jet', )
|
||||||
|
# #axs[0].colorbar(cf)
|
||||||
|
# axs[0].axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]])
|
||||||
|
# axs[0].set_ylabel(ylabel)
|
||||||
|
# axs[0].set_xlabel('Share of Funds Requested')
|
||||||
|
# axs[0].set_title('Trigger Function Map - Alpha sweep')
|
||||||
|
|
||||||
|
share_of_funds = alpha_sweep['share_of_funds']
|
||||||
|
Z = alpha_sweep['log10_trigger']
|
||||||
|
y = alpha_sweep['alpha']
|
||||||
|
ylabel = 'alpha'
|
||||||
|
supply = alpha_sweep['supply']
|
||||||
|
|
||||||
|
cp0=axs[0].contourf(share_of_funds, y, Z.T,100, cmap='jet', )
|
||||||
|
axs[0].axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]])
|
||||||
|
axs[0].set_ylabel(ylabel)
|
||||||
|
axs[0].set_xlabel('Share of Funds Requested')
|
||||||
|
axs[0].set_title('Trigger Function Map - Alpha sweep; Supply ='+str(supply))
|
||||||
|
cb0=plt.colorbar(cp0, ax=axs[0])
|
||||||
|
cb0.set_label('log10 of conviction to trigger')
|
||||||
|
|
||||||
|
# share_of_funds = supply_sweep['share_of_funds']
|
||||||
|
# Z = supply_sweep['log10_share_of_max_conv']
|
||||||
|
# y = supply_sweep['total_supply']
|
||||||
|
# ylabel = 'Effective Supply'
|
||||||
|
|
||||||
|
# axs[2].contourf(share_of_funds, y, Z.T,100, cmap='jet', )
|
||||||
|
# axs[2].axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]])
|
||||||
|
# axs[2].set_ylabel(ylabel)
|
||||||
|
# axs[2].set_xlabel('Share of Funds Requested')
|
||||||
|
# axs[2].set_title('Trigger Function Map - Supply sweep - Z: share_of_max_conv')
|
||||||
|
|
||||||
|
share_of_funds = supply_sweep['share_of_funds']
|
||||||
|
Z = supply_sweep['log10_trigger']
|
||||||
|
y = supply_sweep['total_supply']
|
||||||
|
ylabel = 'Effective Supply'
|
||||||
|
alpha = supply_sweep['alpha']
|
||||||
|
|
||||||
|
max_conv = y/(1-alpha)
|
||||||
|
|
||||||
|
cp1=axs[1].contourf(share_of_funds, y, Z.T,100, cmap='jet', )
|
||||||
|
axs[1].axis([share_of_funds[0], share_of_funds[-1], y[0], y[-1]])
|
||||||
|
axs[1].set_ylabel(ylabel)
|
||||||
|
axs[1].set_xlabel('Share of Funds Requested')
|
||||||
|
axs[1].set_title('Trigger Function Map - Supply sweep; alpha='+str(alpha))
|
||||||
|
axs[1].set_label('log10 of conviction to trigger')
|
||||||
|
cb1=plt.colorbar(cp1, ax=axs[1])
|
||||||
|
cb1.set_label('log10 of conviction to trigger')
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_network(n,m, initial_funds, supply, beta, rho, alpha):
|
||||||
|
'''
|
||||||
|
Definition:
|
||||||
|
Function to initialize network x object
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
Assumptions:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
'''
|
||||||
|
# initilize network x graph
|
||||||
|
network = nx.DiGraph()
|
||||||
|
# create participant nodes with type and token holding
|
||||||
|
for i in range(n):
|
||||||
|
network.add_node(i)
|
||||||
|
network.nodes[i]['type']= "participant"
|
||||||
|
|
||||||
|
h_rv = expon.rvs(loc=0.0, scale= supply/n)
|
||||||
|
network.nodes[i]['holdings'] = h_rv # SOL check
|
||||||
|
|
||||||
|
s_rv = np.random.rand()
|
||||||
|
network.nodes[i]['sentiment'] = s_rv
|
||||||
|
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
initial_supply = np.sum([ network.nodes[i]['holdings'] for i in participants])
|
||||||
|
|
||||||
|
|
||||||
|
# Generate initial proposals
|
||||||
|
for ind in range(m):
|
||||||
|
j = n+ind
|
||||||
|
network.add_node(j)
|
||||||
|
network.nodes[j]['type']="proposal"
|
||||||
|
network.nodes[j]['conviction'] = 0
|
||||||
|
network.nodes[j]['status'] = 'candidate'
|
||||||
|
network.nodes[j]['age'] = 0
|
||||||
|
|
||||||
|
r_rv = gamma.rvs(3,loc=0.001, scale=500)
|
||||||
|
network.nodes[j]['funds_requested'] = r_rv
|
||||||
|
|
||||||
|
network.nodes[j]['trigger']= trigger_threshold(r_rv, initial_funds, initial_supply,beta,rho,alpha)
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
network.add_edge(i, j)
|
||||||
|
|
||||||
|
rv = np.random.rand()
|
||||||
|
a_rv = np.random.uniform(-1,1,1)[0]
|
||||||
|
network.edges[(i, j)]['affinity'] = a_rv
|
||||||
|
network.edges[(i, j)]['tokens'] = 0
|
||||||
|
network.edges[(i, j)]['conviction'] = 0
|
||||||
|
network.edges[(i, j)]['type'] = 'support'
|
||||||
|
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
total_requested = np.sum([ network.nodes[i]['funds_requested'] for i in proposals])
|
||||||
|
|
||||||
|
network = initial_conflict_network(network, rate = .25)
|
||||||
|
network = initial_social_network(network, scale = 1)
|
||||||
|
|
||||||
|
return network
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
# import libraries
|
||||||
|
import networkx as nx
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from .conviction_helper_functions import *
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
# maximum share of funds a proposal can take
|
||||||
|
beta = .2 #later we should set this to be param so we can sweep it
|
||||||
|
# tuning param for the trigger function
|
||||||
|
rho = .0025
|
||||||
|
#alpha = 1 - 0.9999599 #native timescale for app as in contract code
|
||||||
|
alpha = 1-1/2**3 #timescale set in days with 3 day halflife (from comments in contract comments)
|
||||||
|
supply = 21706 # Honey supply balance as of 7-17-2020
|
||||||
|
initial_sentiment = .6
|
||||||
|
|
||||||
|
|
||||||
|
n= 30 #initial participants
|
||||||
|
m= 7 #initial proposals
|
||||||
|
|
||||||
|
|
||||||
|
sensitivity = .75
|
||||||
|
tmin = 0 #unit days; minimum periods passed before a proposal can pass
|
||||||
|
min_supp = 1 #number of tokens that must be stake for a proposal to be a candidate
|
||||||
|
# sentiment_decay = .01 #termed mu in the state update function
|
||||||
|
base_completion_rate = 45
|
||||||
|
base_failure_rate = 180
|
||||||
|
|
||||||
|
initial_funds = 48000 # in xDai
|
||||||
|
|
||||||
|
network = initialize_network(n,m,initial_funds,supply, beta, rho, alpha)
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from .initialization import *
|
||||||
|
from .conviction_helper_functions import *
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
# hyperparameters
|
||||||
|
mu = 0.01
|
||||||
|
|
||||||
|
# Phase 2
|
||||||
|
# Behaviors
|
||||||
|
def check_progress(params, step, sL, s):
|
||||||
|
'''
|
||||||
|
Driving processes: completion of previously funded proposals
|
||||||
|
'''
|
||||||
|
|
||||||
|
network = s['network']
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
|
||||||
|
completed = []
|
||||||
|
failed = []
|
||||||
|
for j in proposals:
|
||||||
|
if network.nodes[j]['status'] == 'active':
|
||||||
|
grant_size = network.nodes[j]['funds_requested']
|
||||||
|
likelihood = 1.0/(base_completion_rate+np.log(grant_size))
|
||||||
|
|
||||||
|
failure_rate = 1.0/(base_failure_rate+np.log(grant_size))
|
||||||
|
if np.random.rand() < likelihood:
|
||||||
|
completed.append(j)
|
||||||
|
elif np.random.rand() < failure_rate:
|
||||||
|
failed.append(j)
|
||||||
|
|
||||||
|
return({'completed':completed, 'failed':failed})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Mechanisms
|
||||||
|
def complete_proposal(params, step, sL, s, _input):
|
||||||
|
'''
|
||||||
|
Book-keeping of failed and completed proposals. Update network object
|
||||||
|
'''
|
||||||
|
|
||||||
|
network = s['network']
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
competitors = get_edges_by_type(network, 'conflict')
|
||||||
|
|
||||||
|
completed = _input['completed']
|
||||||
|
for j in completed:
|
||||||
|
network.nodes[j]['status']='completed'
|
||||||
|
|
||||||
|
for c in proposals:
|
||||||
|
if (j,c) in competitors:
|
||||||
|
conflict = network.edges[(j,c)]['conflict']
|
||||||
|
for i in participants:
|
||||||
|
network.edges[(i,c)]['affinity'] = network.edges[(i,c)]['affinity'] *(1-conflict)
|
||||||
|
|
||||||
|
for i in participants:
|
||||||
|
force = network.edges[(i,j)]['affinity']
|
||||||
|
sentiment = network.nodes[i]['sentiment']
|
||||||
|
network.nodes[i]['sentiment'] = get_sentimental(sentiment, force, decay=0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
failed = _input['failed']
|
||||||
|
for j in failed:
|
||||||
|
network.nodes[j]['status']='failed'
|
||||||
|
for i in participants:
|
||||||
|
force = -network.edges[(i,j)]['affinity']
|
||||||
|
sentiment = network.nodes[i]['sentiment']
|
||||||
|
network.nodes[i]['sentiment'] = get_sentimental(sentiment, force, decay=0)
|
||||||
|
|
||||||
|
key = 'network'
|
||||||
|
value = network
|
||||||
|
|
||||||
|
return (key, value)
|
||||||
|
|
||||||
|
def update_sentiment_on_completion(params, step, sL, s, _input):
|
||||||
|
|
||||||
|
network = s['network']
|
||||||
|
completed = _input['completed']
|
||||||
|
failed = _input['failed']
|
||||||
|
sentiment = s['sentiment']
|
||||||
|
|
||||||
|
completed_count = len(completed)
|
||||||
|
failed_count = len(failed)
|
||||||
|
|
||||||
|
if completed_count+failed_count>0:
|
||||||
|
sentiment = get_sentimental(sentiment,completed_count-failed_count, .25)
|
||||||
|
else:
|
||||||
|
sentiment = get_sentimental(sentiment, 0, 0)
|
||||||
|
|
||||||
|
key = 'sentiment'
|
||||||
|
value = sentiment
|
||||||
|
|
||||||
|
return (key, value)
|
||||||
|
|
||||||
|
|
||||||
|
# Phase 3
|
||||||
|
# Behaviors
|
||||||
|
def participants_decisions(params, step, sL, s):
|
||||||
|
'''
|
||||||
|
High sentiment, high affinity =>buy
|
||||||
|
Low sentiment, low affinities => burn
|
||||||
|
Assign tokens to top affinities
|
||||||
|
'''
|
||||||
|
network = s['network']
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
candidates = [j for j in proposals if network.nodes[j]['status']=='candidate']
|
||||||
|
#sensitivity = params['sensitivity']
|
||||||
|
|
||||||
|
gain = .01
|
||||||
|
delta_holdings={}
|
||||||
|
proposals_supported ={}
|
||||||
|
for i in participants:
|
||||||
|
|
||||||
|
engagement_rate = .3*network.nodes[i]['sentiment']
|
||||||
|
if np.random.rand()<engagement_rate:
|
||||||
|
|
||||||
|
force = network.nodes[i]['sentiment']-sensitivity
|
||||||
|
delta_holdings[i] = network.nodes[i]['holdings']*gain*force
|
||||||
|
|
||||||
|
support = []
|
||||||
|
for j in candidates:
|
||||||
|
booster = social_affinity_booster(network, j, i)
|
||||||
|
affinity = network.edges[(i, j)]['affinity']+booster
|
||||||
|
cutoff = sensitivity*np.max([network.edges[(i,p)]['affinity'] for p in candidates])
|
||||||
|
# range is [-1,1], where 0 is indifference, this determines min affinity supported
|
||||||
|
# if no proposal meets this threshold participants may support a null proposal
|
||||||
|
if cutoff <.3:
|
||||||
|
cutoff = .3
|
||||||
|
|
||||||
|
if affinity > cutoff:
|
||||||
|
support.append(j)
|
||||||
|
|
||||||
|
proposals_supported[i] = support
|
||||||
|
else:
|
||||||
|
delta_holdings[i] = 0
|
||||||
|
proposals_supported[i] = [j for j in candidates if network.edges[(i,j)]['tokens']>0 ]
|
||||||
|
|
||||||
|
return({'delta_holdings':delta_holdings, 'proposals_supported':proposals_supported})
|
||||||
|
|
||||||
|
# Mechanisms
|
||||||
|
def update_tokens(params, step, sL, s, _input):
|
||||||
|
'''
|
||||||
|
Description:
|
||||||
|
Udate everyones holdings and their conviction for each proposal
|
||||||
|
'''
|
||||||
|
|
||||||
|
network = s['network']
|
||||||
|
delta_holdings = _input['delta_holdings']
|
||||||
|
proposals = get_nodes_by_type(network, 'proposal')
|
||||||
|
candidates = [j for j in proposals if network.nodes[j]['status']=='candidate']
|
||||||
|
proposals_supported = _input['proposals_supported']
|
||||||
|
participants = get_nodes_by_type(network, 'participant')
|
||||||
|
|
||||||
|
for i in participants:
|
||||||
|
network.nodes[i]['holdings'] = network.nodes[i]['holdings']+delta_holdings[i]
|
||||||
|
supported = proposals_supported[i]
|
||||||
|
total_affinity = np.sum([ network.edges[(i, j)]['affinity'] for j in supported])
|
||||||
|
for j in candidates:
|
||||||
|
if j in supported:
|
||||||
|
normalized_affinity = network.edges[(i, j)]['affinity']/total_affinity
|
||||||
|
network.edges[(i, j)]['tokens'] = normalized_affinity*network.nodes[i]['holdings']
|
||||||
|
else:
|
||||||
|
network.edges[(i, j)]['tokens'] = 0
|
||||||
|
|
||||||
|
prior_conviction = network.edges[(i, j)]['conviction']
|
||||||
|
current_tokens = network.edges[(i, j)]['tokens']
|
||||||
|
network.edges[(i, j)]['conviction'] =current_tokens+alpha*prior_conviction
|
||||||
|
|
||||||
|
for j in candidates:
|
||||||
|
network.nodes[j]['conviction'] = np.sum([ network.edges[(i, j)]['conviction'] for i in participants])
|
||||||
|
total_tokens = np.sum([network.edges[(i, j)]['tokens'] for i in participants ])
|
||||||
|
if total_tokens < min_supp:
|
||||||
|
network.nodes[j]['status'] = 'killed'
|
||||||
|
|
||||||
|
key = 'network'
|
||||||
|
value = network
|
||||||
|
|
||||||
|
return (key, value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||