<jupyter_start><jupyter_text>First implementation from Julia<jupyter_code>import networkx as nx
from scipy.stats import binom
import numpy as np
from statistics import mean
ALPHA_PROB = 0.5
BETA_PROB = 0.6

def get_evidence(conf, nr_trials):
    if conf > 0.5:
        return np.random.binomial(nr_trials, BETA_PROB) / nr_trials
    else:
        return np.random.binomial(nr_trials, ALPHA_PROB) / nr_trials

    
def update_confidence(conf, evidences):
    new_confidence = np.mean([conf, np.mean(evidences)])
    return new_confidence
    
    
class DEpiNet:
    def __init__(self, net, conf_mean, conf_sd):
        self.structure = net
        confidences = np.random.normal(conf_mean, conf_sd, len(self.structure.nodes))
        for i in range(len(self.structure.nodes)):
            self.structure.nodes[i]['confidence'] = confidences[i]
G = nx.complete_graph(10)
from netwulf import visualize
visualize(G)
network = DEpiNet(G, 0.5, 0.2)
[network.structure.nodes[i]['confidence'] for i in range(len(network.structure.nodes))]
network.structure.nodes[0]
for step in range(5):
    new_net = network.structure.copy()
    
    # Posess evidences
    for i in range(len(network.structure.nodes)):
        network.structure.nodes[i]['evidence'] = get_evidence(
            network.structure.nodes[i]['confidence'], 10
        )

    # Update confidences
    for i in range(len(network.structure.nodes)):
        evidences = [network.structure.nodes[i]['evidence'] for i in list(network.structure.neighbors(1))]
        new_net.nodes[i]['confidence'] = update_confidence(
            conf=network.structure.nodes[i]['confidence'],
            evidences=evidences
        )
    network.structure = new_net
list(G.neighbors(1))
[network.structure.nodes[i]['confidence'] for i in range(len(network.structure.nodes))]<jupyter_output><empty_output><jupyter_text>Epistemic Network Model  Notes ModelThe basic setup of Bala and Goyal’s model is that there is **a groupof simple agents** — highly idealized representations of scientists, orknowledge seekers — who are **trying to choose between two actions**and who use information gathered by themselves and by others tomake this choice. The two actions are assumed to differ in howlikely they are to yield a desired outcome. This could represent thechoice between eating fish or not and so increasing or decreasingone’s risk of mercury poisoning; or it could be regulating smokestackemissions and so increasing or decreasing the risks of acid rain.For a very simple example, imagine someone faced with two slotmachines, trying to figure out which one pays out more often.  Over a series of rounds, each scientist in the model chooses oneaction or the other. They **make their choices on the basis of whatthey currently believe about the problem, and they record the resultsof their actions.** To begin with, the scientists are not sure aboutwhich action is more likely to yield the desired outcome. But as theymake their choices, they gradually see what sorts of outcomes eachaction yields. These **outcomes are the evidence they use to updatetheir beliefs.** Importantly, each scientist develops beliefs based notonly on the outcomes of their own actions, but also on those oftheir colleagues and friends.  We have an abstractnetwork consisting of a collection of “nodes,” each of which may ormay not be connected to other nodes by what is called an “edge.” Each node represents a scientist, and each edge connects two scientistswho have access to each other’s results.These networks can take different shapes. **These structures are often important in determining how informationand ideas flow through a group.**We should also emphasize that, although our examples comefrom science and we are calling the agents in our model “scientists,”these models can represent any group of people who are trying tomake their way in an unpredictable world. All of us act as scientistssometimes, when we make decisions based on our own experiencesand those of our friends. Ever buy a car? There is a good chancethat you took it for a test drive and asked the dealer some questions.You were gathering evidence before making a decision. Agents (Scientists)In the model,each node — each scientist — is associated with a number between 0and 1. This number represents the scientist’s level of certainty, orcredence, that action B is better than action A. An assignment of .7would mean that particular scientist thinks there is a 70 percentchance that action B is better than action A. Which action the scientisttakes is wholly determined by this number. If it is greaterthan .5, the scientist performs action B — by which we mean that wesimulate pulling a slot machine some number of times and countingthe number of times it pays off. Then we use Bayes’ rule toupdate the scientist’s credence in light of this result, and likewiseupdate the credences of all of the other neighboring scientists onthe network.If the scientist’s belief is less than .5, he or she performs actionA. In the simplest version of the model, we assume that everyoneknows that this action works exactly half the time. (When credences exactly equal .5, we also have the agent choose action A, butthis essentially never happens.) **Anyoneconnected to someone who tried action B—the new, unknowntreatment—will update their beliefs.**  **This process continues stepwise (try actions, update credences,try actions, update credences) until the scientists have converged ona consensus.** This can happen if all of the scientists have sufficientlyhigh credence—greater than .99—that action B is better; or all of them have sufficiently low credence, less than .5, so that no one inthe network ever performs action B, in which case they will notlearn anything further about it. In the first case, we say the networkhas converged to the true belief. In the second, we say it has convergedto the false one.  In general, these models tend to convergeto the true consensus—that is, the whole network comes to believethat action B is better. But sometimes go to thefalse one.22What we want to understand is this: **Under what circumstancesdo networks of scientists converge to false beliefs?** ActionsIn the model, one of the two actions — call it action B — is, in fact,better than action A. (To keep this straight, remember that A is for“All right,” but B is for “Better.”) But figuring out which action issuperior is not necessarily easy. **A crucial assumption in this modelis that evidence is probabilistic**, meaning that when the scientistsinvestigate the world — test a slot machine or warn a sick patient offfish — the results are not always the same. **Action B is better thanaction A because, on average, it yields better results.** But there canbe many individual instances when action A happens to yield a betterresult.In this way, we can think of action B as similar to a biased coin.It may land heads up more often than an ordinary coin — but thatdoes not mean that it never lands tails up. And if you flip a biasedcoin and an unbiased coin some number of times, there is no guaranteethat the biased one will land heads up more often. It is merelylikely that it will do so. Zollman effectThis trade-off, where connections propagate true beliefs butalso open channels for the spread of misleading evidence, meansthat sometimes it is actually better for a group of scientists to communicateless, especially when they work on a hard problem. Thisphenomenon, in which scientists improve their beliefs by failing tocommunicate, is known as the “Zollman effect,” after <NAME>,who discovered it.28 If everybody shares evidence, a chancestring of bad data can persuade the entire group to abandon thecorrect theory. But in a group where not everyone listens to everyoneelse, pockets of scientists can be protected from misleadingdata and continue to gather evidence on the true belief that eventuallypersuades the rest of the community. Network structure remarks1. Real human networks are often “small worlds,” which tend to have interconnectedcliques and short path lengths between any two nodes. Granovetter(1973) conceived of human networks as consisting of tight-knit cliques connectedby bridges. Watts and Strogatz (1998) developed their famous “smallworld” networks to try to capture realistic aspects of human networks. Onnelaet al. (2007) observe such structures among cell phone users. Newman(2001) finds the same for scientific collaboration networks.2. The harder the problem, the more potential social structure has toinfluence the outcome. (Zollman effect; Rosenstock, Bruner, and O’Connor (2016) show that Zollman’s research onconnectivity and consensus is sensitive to parameter values and that, in particular,in communities where scientists gather large data sets and data is more reliable, it is better to always communicate this data) Other remarks1. The social spread of knowledge is a double-edged sword. It gives usremarkable capabilities, as a species, to develop sophisticated knowledgeabout the world, but it also opens the door to the spread offalse belief.  Usage example<jupyter_code>%load_ext autoreload
%autoreload 2
cd ..
from src.epinet.agent import Agent
from src.epinet.models import Network, DynamicNetwork
from src.epinet.simulation import Simulation<jupyter_output><empty_output><jupyter_text>Agent<jupyter_code>a = Agent('1', 0.5, [])
print(f'Base credence: {a.credence}')
a.update_credence(10, 6)
print(f'Credence after update: {a.credence}')<jupyter_output>Base credence: 0.5
Credence after update: 0.6923076923076922<jupyter_text>Static Network<jupyter_code>import networkx as nx

# Create craph structure
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2)])
l = nx.convert.to_dict_of_lists(G)
print(f'Graph edges: {l}')

# Create epostemic network
g = Network(l)
print(f'Consensus check: {g.is_consensus()}')
g.plot('Network structure visualization')<jupyter_output>Graph edges: {0: [1, 2], 1: [0], 2: [0]}
Consensus check: False<jupyter_text>Dynamic Network<jupyter_code>import networkx as nx

# Create craph structure
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
l1 = nx.convert.to_dict_of_lists(G)

G.add_edges_from([(1, 2)])
l2 = nx.convert.to_dict_of_lists(G)

G.add_edges_from([(0, 3)])
l3 = nx.convert.to_dict_of_lists(G)

print('Graph edges:')
print(f' - Step 1: {l1}')
print(f' - Step 2: {l2}')
print(f' - Step 3: {l3}')

# Create epostemic network
g = DynamicNetwork(l1)
g.plot('Network structure visualization - Step 1')
g.update_structure(l2)
g.plot('Network structure visualization - Step 2')
g.update_structure(l3)
g.plot('Network structure visualization - Step 3')<jupyter_output>Graph edges:
 - Step 1: {0: [1, 2], 1: [0], 2: [0, 3], 3: [2]}
 - Step 2: {0: [1, 2], 1: [0, 2], 2: [0, 3, 1], 3: [2]}
 - Step 3: {0: [1, 2, 3], 1: [0, 2], 2: [0, 3, 1], 3: [2, 0]}<jupyter_text>Simulation<jupyter_code># Create epistemic network
G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (3, 4), (2, 5), (5, 6)])
l = nx.convert.to_dict_of_lists(G)
g = Network(l)
print(f'Consensus check: {g.is_consensus()}')

# Run simulation
s = Simulation(g, 10)
sim_res = s.run()
if sim_res:
    print('Consensus reached')
else:
    print('No consensus')<jupyter_output>Consensus check: False
Consensus reached<jupyter_text>Zollman effect<jupyter_code># TODO: run<jupyter_output><empty_output><jupyter_text>Cogsnet based  Implementation<jupyter_code>import networkx as nx
from numpy import random
from typing import Dict, List

class Agent:
    BASE_ACTION_PAYOFF = 0.5
    EPSILON = 0.001
    BETA_ACTION_PAYOFF = BASE_ACTION_PAYOFF + EPSILON
    
    def __init__(self, i, credence, neighbors):
        self.id = i
        self.credence = credence
        self.neighbors = neighbors
        self.n = 0
        self.k = 0
        
    def get_evidence(self, n_trials):
        self.n, self.k = 0, 0
        if self.credence > self.BASE_ACTION_PAYOFF:
            self.n = n_trials
            self.k = random.binomial(n_trials, self.BETA_ACTION_PAYOFF)

    def update_credence(self, n, k):
        #n = self.n + sum([neighbor.n for neighbor in self.neighbors])
        #k = self.k + sum([neighbor.k for neighbor in self.neighbors])
        self.credence = 1 / (1 + (1 - self.credence) * (((0.5 - self.EPSILON) / (0.5 + self.EPSILON)) ** (2 * k - n)) / self.credence)
    
    def set_neighbors(self, neighbors):
        self.neighbors = neighbors

class Network:
    CONSENSUS_EPSILON = 0.01
    BASE_ACTION_PAYOFF = 0.5
    N_TRIALS = 1000
    
    def __init__(self, structure: Dict[int, List[int]]):
        self.structure = structure
        self.agents = [Agent(i, random.uniform(0, 1), structure[i]) for i in structure.keys()]
    
    def run_evidence_collection(self):
        for agent in self.agents:
            agent.get_evidence(self.N_TRIALS)
    
    def run_credence_update(self):
        for agent in self.agents:
            n = agent.n + sum([self.agents[neighbor].n for neighbor in agent.neighbors])
            k = agent.k + sum([self.agents[neighbor].k for neighbor in agent.neighbors])
            if n > 0:
                agent.update_credence(n, k)
    
    def is_true_consensus(self):
        return all(agent.credence > 1-self.CONSENSUS_EPSILON for agent in self.agents)
    
    def is_consensus(self):
        """
        Checks if there is a consensus regarding superiority of one of the actions.
        """
        is_base_consensus = all(agent.credence < self.BASE_ACTION_PAYOFF for agent in self.agents)
        is_beta_consensus = all(agent.credence > 1-self.CONSENSUS_EPSILON for agent in self.agents)
        return is_base_consensus or is_beta_consensus

    def describe(self):
        for a in self.agents:
            print(f"- Agent {a.id}, {a.credence}, {a.n}, {a.k}, {a.neighbors}")
        print()
        
    def plot(self):
        g = nx.Graph(self.structure)
        nx.draw(g, with_labels=True)

class DynamicNetwork(Network):
    def update_structure(self, structure: Dict[int, List[int]]):
        for a in self.agents:
            a.set_neighbors(structure[a.id])
        self.structure = structure
        
class Simulation:
    def __init__(self, network, steps):
        self.network = network
        self.steps = steps
    
    def step(self):
        self.network.run_evidence_collection()
        self.network.run_credence_update()
        
    def run(self):
        i = 0
        while i < self.steps and not self.network.is_consensus():
            self.step()
            i += 1
        return self.network.is_true_consensus()<jupyter_output><empty_output><jupyter_text>Tests  Agent<jupyter_code>a = Agent(1, 0.59, [])
print(a.credence)
a.update_credence(20, 12)
print(a.credence)<jupyter_output>0.59
0.593864757454364<jupyter_text>Network<jupyter_code>G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2)])
l = nx.convert.to_dict_of_lists(G)
print(l)
g = Network(l)
print(g.is_consensus())
g.plot()
g.describe()
print(g.is_consensus())
g.run_evidence_collection()
g.run_credence_update()
g.describe()
print(g.is_consensus())<jupyter_output>- Agent 0, 0.9936481503774242, 10, 7, [1, 2]
- Agent 1, 0.9998307906465516, 10, 5, [0]
- Agent 2, 0.7533256365693816, 0, 0, [0]

False
- Agent 0, 0.9987388855485293, 10, 8, [1, 2]
- Agent 1, 0.9999247887726656, 10, 3, [0]
- Agent 2, 0.9873846997992822, 10, 6, [0]

False<jupyter_text>Simulation<jupyter_code>G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (1, 3), (3, 4), (2, 5), (5, 6)])
l = nx.convert.to_dict_of_lists(G)
g = Network(l)
g.is_consensus()

s = Simulation(g, 10)
s.run()
s = Simulation(g, 10)
s.run()<jupyter_output>- Agent 0, 0.9999999947041478, 10, 4, [1, 2]
- Agent 1, 0.9998499925031161, 10, 5, [0, 3]
- Agent 2, 0.999999998927444, 10, 5, [0, 5]
- Agent 3, 0.9996717369603754, 10, 4, [1, 4]
- Agent 4, 0.15903316352568994, 0, 0, [3]
- Agent 5, 0.9999999997277202, 10, 6, [2, 6]
- Agent 6, 0.9998604638279907, 10, 4, [5]<jupyter_text>Visualizations<jupyter_code>import matplotlib.pyplot as plt
%matplotlib inline

def plot_sims_results(d, a_numbers):
    for s in d.keys():
        plt.plot(a_numbers, d[s], label = s)
    plt.xlabel('Nr agents in network')
    plt.ylabel('Probability of correct consensus')
    plt.title('Simulations for different network structures')
    plt.legend()
    plt.show()<jupyter_output><empty_output><jupyter_text>Zollman effect<jupyter_code>MAX_STEPS = 10000

def test_complete(nr_nodes, nr_sims):
    g = nx.complete_graph(nr_nodes)
    l = nx.convert.to_dict_of_lists(g)
    res = []
    for i in range(nr_sims):
        n = Network(l)
        s = Simulation(n, MAX_STEPS)
        p_res = s.run()
        res.append(1 if p_res else 0)
    return np.mean(res)

def test_cycle(nr_nodes, nr_sims):
    g = nx.cycle_graph(nr_nodes)
    l = nx.convert.to_dict_of_lists(g)
    res = []
    for i in range(nr_sims):
        n = Network(l)
        s = Simulation(n, MAX_STEPS)
        p_res = s.run()
        res.append(1 if p_res else 0)
    return np.mean(res)

def test_wheel(nr_nodes, nr_sims):
    g = nx.wheel_graph(nr_nodes)
    l = nx.convert.to_dict_of_lists(g)
    res = []
    for i in range(nr_sims):
        n = Network(l)
        s = Simulation(n, MAX_STEPS)
        p_res = s.run()
        res.append(1 if p_res else 0)
    return np.mean(res)
import numpy as np

results = {
    'cycle': [],
    'wheel': [],
    'complete': []
}
agent_nrs = [5, 6, 7, 8, 9, 10]
sims_nr = 1000

for nr in agent_nrs:
    # Complete
    res = test_complete(nr, sims_nr)
    results['complete'].append(res)
    
    # Wheel
    res = test_wheel(nr, sims_nr)
    results['wheel'].append(res)
    
    # Cycle
    res = test_cycle(nr, sims_nr)
    results['cycle'].append(res)
plot_sims_results(results, agent_nrs)<jupyter_output><empty_output><jupyter_text>Dynamic network<jupyter_code>G = nx.Graph()
G.add_edges_from([(0, 1), (0, 2), (2, 3)])
l1 = nx.convert.to_dict_of_lists(G)
print(l1)
G.add_edges_from([(1, 2)])
l2 = nx.convert.to_dict_of_lists(G)
print(l2)
G.add_edges_from([(0, 3)])
l3 = nx.convert.to_dict_of_lists(G)
print(l3)
g = DynamicNetwork(l1)
g.plot()
g.update_structure(l2)
g.plot()
g.update_structure(l2)
g.plot()
g.update_structure(l3)
g.plot()<jupyter_output><empty_output><jupyter_text>Mesa implementation<jupyter_code>from mesa import Agent, Model
from mesa.time import RandomActivation

class MoneyAgent(Agent):
    """ An agent with fixed initial wealth."""
    def __init__(self, unique_id, model):
        super().__init__(unique_id, model)
        self.wealth = 1

    def step(self):
        if self.wealth == 0:
            return
        other_agent = self.random.choice(self.model.schedule.agents)
        other_agent.wealth += 1
        self.wealth -= 1

class MoneyModel(Model):
    """A model with some number of agents."""
    def __init__(self, N):
        self.num_agents = N
        self.schedule = RandomActivation(self)
        # Create agents
        for i in range(self.num_agents):
            a = MoneyAgent(i, self)
            self.schedule.add(a)

    def step(self):
        '''Advance the model by one step.'''
        self.schedule.step()
model = MoneyModel(1000)
for i in range(1000):
    model.step()
%matplotlib inline

# The below is needed for both notebooks and scripts
import matplotlib.pyplot as plt

agent_wealth = [a.wealth for a in model.schedule.agents]
plt.hist(agent_wealth)<jupyter_output><empty_output><jupyter_text>NetworkX usage<jupyter_code>import networkx as nx
G = nx.Graph()
G.add_edges_from([(1, 2), (1, 3)])
G.number_of_nodes()
G.number_of_edges()
for line in nx.generate_adjlist(G):
    print(line)
from networkx.readwrite import json_graph
nx.convert.to_dict_of_lists(G)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09937445964501856, 'fraction_numerical': 0.03056502059706047, 'mean_word_length': 3.9431875314228257, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 64, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 3, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '258981', 'license': 'MIT', 'n_tokens_mistral': 6753, 'n_tokens_neox': 6227, 'n_words': 2172, 'path': 'notebooks/experimental_implementations.ipynb', 'repo_name': 'KE4T5/network-epistemology', 'size': '19670'}