Guest User

Untitled

a guest
Oct 20th, 2017
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.26 KB | None | 0 0
  1. from graph_smell import *
  2. import math
  3. import time
  4. import pickle
  5. import threading
  6.  
  7. # quick explanation:
  8. # N[a][b]['amount'] means amount of b tokens that a has
  9. # N[a][b]['trust'] means how many percent of a's total tokens can be b tokens
  10. # can be less but cannot be more than that
  11.  
  12. class NetworkKeeper(SmellyGraph):
  13.  
  14. initial_token_amount = 20000
  15. universal_basic_income = 10000
  16.  
  17. def __init__(self):
  18. SmellyGraph.__init__(self)
  19. self.graph_edit_lock = threading.RLock()
  20.  
  21. def register_node(self, name, public_key=None, overwrite=False):
  22. # first check if this name is already registered
  23. if name in self and not overwrite:
  24. raise RuntimeError('this name already is registered')
  25. self.add_node(name)
  26. self.nodes[name]['public_key'] = public_key
  27. self.add_edge(name, name)
  28. self[name][name]['trust'] = 1 # you must trust yourself completely
  29. self[name][name]['amount'] = self.initial_token_amount # use only integers !!
  30.  
  31. def create_edge(self, node1, node2):
  32. self.add_edge(node1, node2)
  33. self.add_edge(node2, node1)
  34. self[node1][node2]['trust'] = 0
  35. self[node2][node1]['trust'] = 0
  36. self[node1][node2]['amount'] = 0
  37. self[node2][node1]['amount'] = 0
  38. self[node1][node2]['potential_trust'] = 0
  39. self[node2][node1]['potential_trust'] = 0
  40.  
  41. def update_trust(self, node1, node2):
  42. #with self.graph_edit_lock:
  43. potential1 = self[node1][node2]['potential_trust']
  44. potential2 = self[node2][node1]['potential_trust']
  45. min_potential = min(potential1, potential2)
  46. if min_potential > self[node1][node2]['trust']:
  47. # we can make the trust lvl higher
  48. self[node1][node2]['trust'] = min_potential
  49. self[node2][node1]['trust'] = min_potential
  50. return min_potential
  51. ratio1 = self[node1][node2]['amount'] / self.total_tokens(node1)
  52. ratio2 = self[node2][node1]['amount'] / self.total_tokens(node2)
  53. new_lvl = max(min_potential, ratio1, ratio2)
  54. self[node1][node2]['trust'] = new_lvl
  55. self[node2][node1]['trust'] = new_lvl
  56. if new_lvl == 0:
  57. self.remove_edge(node1, node2)
  58. return new_lvl
  59.  
  60. def get_connections(self, node):
  61. return [[neighbor, self[node][neighbor]['trust'], self[node][neighbor]['amount']]
  62. for neighbor in self.neighbors(node)]
  63.  
  64. def pay_ubi(self):
  65. with self.graph_edit_lock:
  66. for node in self.nodes:
  67. self[node][node]['amount'] = self.universal_basic_income
  68. self.last_ubi_payout = time.time()
  69.  
  70. def total_tokens(self, node):
  71. return sum([self[node][neighbor]['amount']
  72. for neighbor in self.neighbors(node)])
  73.  
  74. def max_possible_direct_transfer(self, sender, receiver):
  75. # how many sender tokens receiver can accept
  76. sender_tokens = self[receiver][sender]['trust'] * self.total_tokens(receiver)
  77. # money should always be int so none is lost due to precision
  78. sender_tokens = int(sender_tokens)
  79. # minus sender's tokens he already has
  80. sender_tokens -= self[receiver][sender]['amount']
  81. # check if the sender has this much sender tokens
  82. sender_tokens = min(sender_tokens, self[sender][sender]['amount'])
  83. # receiver tokens that the sender possesses
  84. receiver_tokens = self[sender][receiver]['amount']
  85. # returns amount of sender tokens and receiver tokens needed to make maximum transfer
  86. return sender_tokens, receiver_tokens
  87.  
  88. def direct_transfer(self, sender, receiver, amount, which_tokens, force=False):
  89. with self.graph_edit_lock:
  90. st_max, rt_max = self.max_possible_direct_transfer(sender, receiver)
  91. if which_tokens == 'sender':
  92. if amount > st_max and not force:
  93. raise Exception('incorrect direct transfer')
  94. self[sender][sender]['amount'] -= amount
  95. self[receiver][sender]['amount'] += amount
  96. elif which_tokens == 'receiver':
  97. if amount > rt_max and not force:
  98. raise Exception('incorrect direct transfer')
  99. self[sender][receiver]['amount'] -= amount
  100. self[receiver][receiver]['amount'] += amount
  101. else:
  102. raise RuntimeException('incorrect token name')
  103.  
  104. def try_to_transfer_with_only_one_branch(self, sender, receiver, amount, max_nodes_you_can_visit=40):
  105. with self.graph_edit_lock:
  106. # branch we will grow and use for transfer
  107. branch = [sender]
  108. nodes_visited_from = {sender: []}
  109. counter = 0
  110. def step_back():
  111. del branch[-1]
  112. if branch == []:
  113. # we deleted even the sender node
  114. raise RuntimeError('couldnt find a single branch for transfer')
  115. while branch[-1] != receiver and counter <= max_nodes_you_can_visit:
  116. try:
  117. candidate = self.smelling_policy(branch[-1], receiver,
  118. cursed_nodes=nodes_visited_from[branch[-1]] + branch)
  119. except RuntimeError:
  120. # this exception was raised if path finder was stuck in dead end, so we have to step back
  121. step_back()
  122. continue
  123. nodes_visited_from[branch[-1]].append(candidate)
  124. counter += 1
  125. if amount <= sum(self.max_possible_direct_transfer(branch[-1], candidate)):
  126. branch.append(candidate)
  127. try:
  128. nodes_visited_from[candidate]
  129. except:
  130. # it doesn't exist so create it
  131. nodes_visited_from[candidate] = []
  132. if branch[-1] != receiver:
  133. raise RuntimeError('exceeded maximum number of nodes to visit')
  134. # translate the branch into a list of direct transfers
  135. list_of_direct_transfers = []
  136. for n in range(len(branch) - 1):
  137. sender_tokens, receiver_tokens = self.max_possible_direct_transfer(branch[n], branch[n+1])
  138. if amount > receiver_tokens:
  139. sender_tokens = amount - receiver_tokens
  140. else:
  141. sender_tokens = 0
  142. receiver_tokens = amount
  143. list_of_direct_transfers.append([branch[n], branch[n+1], sender_tokens, receiver_tokens])
  144. # execute only after we are sure it's possible
  145. self.execute_direct_transfers(list_of_direct_transfers)
  146. return list_of_direct_transfers
  147.  
  148. def transfer(self, sender, receiver, amount, test=False):
  149. with self.graph_edit_lock:
  150. if amount == 'all':
  151. amount = self.total_tokens(sender)
  152. amount_so_far = [0] # it's wrapped into a list so it can be passed by reference
  153. list_of_direct_transfers = []
  154. def transfer_recursively(self, sender, receiver, amount,
  155. amount_so_far, list_of_direct_transfers, max_recursion_lvl=10):
  156. try:
  157. list_of_direct_transfers += self.try_to_transfer_with_only_one_branch(sender, receiver, amount)
  158. amount_so_far[0] += amount
  159. except RuntimeError:
  160. if max_recursion_lvl == 0:
  161. raise RuntimeError('reached maximum recursion level and failed to transfer fully')
  162. transfer_recursively(self, sender, receiver, math.ceil(amount/2),
  163. amount_so_far, list_of_direct_transfers,
  164. max_recursion_lvl=max_recursion_lvl-1)
  165. transfer_recursively(self, sender, receiver, math.floor(amount / 2),
  166. amount_so_far, list_of_direct_transfers,
  167. max_recursion_lvl=max_recursion_lvl - 1)
  168. try:
  169. transfer_recursively(self, sender, receiver, amount,
  170. amount_so_far, list_of_direct_transfers)
  171. if test:
  172. self.execute_direct_transfers(list_of_direct_transfers, reverse=True)
  173. except RuntimeError:
  174. print("could't find a way to transfer ", amount)
  175. print('maximum possible amount you can transfer is: ', amount_so_far[0])
  176. # rollback
  177. self.execute_direct_transfers(list_of_direct_transfers, reverse=True)
  178. return amount_so_far[0], list_of_direct_transfers
  179.  
  180. def execute_direct_transfers(self, list_of_direct_transfers, reverse=False):
  181. with self.graph_edit_lock:
  182. if reverse:
  183. for transfer in reversed(list_of_direct_transfers):
  184. self.direct_transfer(transfer[1], transfer[0], transfer[2], 'receiver')
  185. self.direct_transfer(transfer[1], transfer[0], transfer[3], 'sender', force=True)
  186. else:
  187. for transfer in list_of_direct_transfers:
  188. self.direct_transfer(transfer[0], transfer[1], transfer[2], 'sender')
  189. self.direct_transfer(transfer[0], transfer[1], transfer[3], 'receiver')
  190.  
  191.  
  192.  
  193. # for running simulations
  194. def make_random_transfers(N, amount, iterations):
  195. for i in range(iterations):
  196. node1 = np.random.choice(N.nodes)
  197. node2 = np.random.choice(N.nodes)
  198. N.transfer(node1, node2, amount)
  199.  
  200.  
  201.  
  202. if __name__ == "__main__":
  203. # generate a scale-free graph which resembles a social network
  204. # nx.watts_strogatz_graph(1000, 10, 0.1)
  205. # or nx.barabasi_albert_graph(1000, 10)
  206. g = nx.watts_strogatz_graph(1000, 30, 0.3)
  207. g = g.to_directed()
  208. N = NetworkKeeper()
  209. N.load_graph_structure(g)
  210.  
  211. N.initialize_random_smells(dimensions=300)
  212.  
  213. for i in range(30):
  214. N.dissipate_smells(change_rate=0.1)
  215. # make tests
  216. test_random_paths(N, 10)
  217.  
  218. # set default trust level for every connection
  219. nx.set_edge_attributes(N, 0.2, 'trust')
  220. # amount of somebody's tokens is initially 0
  221. nx.set_edge_attributes(N, 0, 'amount')
  222.  
  223. for node in N.nodes:
  224. N.register_node(node, overwrite=True)
Add Comment
Please, Sign In to add comment