Guest User

Untitled

a guest
Jun 22nd, 2018
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.50 KB | None | 0 0
  1. 0 1 1
  2. 1 2 1
  3. 1 3 1
  4. 0 0 -1
  5. 1 1 -1
  6. 2 2 -1
  7.  
  8. -4
  9. +-++
  10.  
  11. import sys
  12. from operator import itemgetter
  13. from undirectedgraph import UndirectedGraph
  14. from itertools import combinations, product
  15.  
  16. STATES = [-1, 1]
  17.  
  18.  
  19. class IsingModel():
  20.  
  21. def __init__(self, graph, bias):
  22. self._graph = graph
  23. self._bias = bias
  24. self._spin_cache = {}
  25. self._energy_cache = {}
  26. self._state = {}
  27. self._energy = None
  28. self._root = None
  29.  
  30. @classmethod
  31. def from_string(obj, input):
  32. graph = UndirectedGraph()
  33. bias = {}
  34. i = 0
  35. valid = True
  36. for line in input:
  37. i += 1
  38. try:
  39. if line.strip():
  40. a, b, weight = line.split()
  41. a = int(a)
  42. b = int(b)
  43. weight = int(weight)
  44. if a == b:
  45. bias[a] = weight
  46. else:
  47. graph.add_weighted_edge(a, b, weight)
  48.  
  49. except ValueError:
  50. print('Error parsing input on line {} n {}'.format(i, line))
  51. valid = False
  52.  
  53. if not valid:
  54. exit()
  55.  
  56. return obj(graph, bias)
  57.  
  58. def bias(self, node):
  59. return self._bias.get(node, 0)
  60.  
  61. def system_energy(self, state):
  62. energy = 0
  63.  
  64. # sum s_i * h_i
  65. for i in self._graph.nodes():
  66. energy += state[i] * self.bias(i)
  67.  
  68. # sum s_i * s_j * J_ij
  69. for i, j in combinations(self._graph.nodes(), 2):
  70. if self._graph.has_edge(i, j):
  71. weight = self._graph.weight(i, j)
  72. energy += state[i] * state[j] * weight
  73. return energy
  74.  
  75. def brute_force_solve(self):
  76. nodes = self._graph.nodes()
  77. if nodes:
  78. self._root = list(nodes)[0]
  79. results = []
  80. for state_list in product([-1, 1], repeat=len(nodes)):
  81. state = dict(zip(nodes, state_list))
  82. energy = self.system_energy(state)
  83. results.append((energy, state))
  84. results.sort(key=itemgetter(0))
  85. self._state = results[0][1]
  86. self._energy = results[0][0]
  87. else:
  88. self._energy = 0
  89.  
  90. return self._energy
  91.  
  92. def min_energy(self, node, parent, parent_spin):
  93. args = (node, parent, parent_spin)
  94. if args in self._energy_cache:
  95. return self._energy_cache[args]
  96.  
  97. if self._graph.is_leaf(node) and parent != None:
  98. energies = []
  99. for spin in STATES:
  100. energy = spin * self.bias(node)
  101. energy += spin * parent_spin * self._graph.weight(node, parent)
  102. energies.append((energy, spin))
  103. return self._min_cache(args, energies)
  104.  
  105. energies = []
  106. for spin in STATES:
  107. energy = spin * self.bias(node)
  108. if parent != None:
  109. energy += spin * parent_spin * self._graph.weight(node, parent)
  110. for neighbour in self._graph.neighbours(node):
  111. if neighbour != parent and neighbour != node:
  112. energy += self.min_energy(neighbour, node, spin)
  113. energies.append((energy, spin))
  114.  
  115. return self._min_cache(args, energies)
  116.  
  117. def _min_cache(self, key, energies):
  118. energy, spin = min(energies)
  119. self._energy_cache[key] = energy
  120. self._spin_cache[key] = spin
  121. return energy
  122.  
  123. def walk_state(self, node, parent, parent_spin):
  124. spin = self._spin_cache[(node, parent, parent_spin)]
  125. self._state[node] = spin
  126. for child in self._graph.neighbours(node):
  127. if (child, node, spin) in self._spin_cache:
  128. self.walk_state(child, node, spin)
  129.  
  130. def tree_solve(self):
  131. nodes = self._graph.nodes()
  132. if nodes:
  133. self._root = list(nodes)[0]
  134. self._energy = self.min_energy(self._root, None, None)
  135. self.walk_state(self._root, None, None)
  136. else:
  137. self._energy = 0
  138. return self._energy
  139.  
  140. def solve(self):
  141. if self._graph.is_tree():
  142. self.tree_solve()
  143. else:
  144. print("Warning: graph is not a tree brute forcing solution")
  145. self.brute_force_solve()
  146.  
  147. def print_answer(self):
  148. print(self._energy)
  149. states = list(self._state.items())
  150. states.sort()
  151. symbols = {1: '+', -1: '-'}
  152. print(''.join([symbols[spin] for _, spin in states]))
  153.  
  154.  
  155. if __name__ == '__main__':
  156. model = IsingModel.from_string(sys.stdin)
  157. model.solve()
  158. model.print_answer()
  159.  
  160. from collections import deque
  161.  
  162.  
  163. class UndirectedGraph:
  164. def __init__(self):
  165. self._nodes = {}
  166. self._edges = {}
  167.  
  168. def _set_weight(self, a, b, weight):
  169. self._edges[(a, b)] = weight
  170.  
  171. def add_node(self, a):
  172. if a not in self._nodes:
  173. self._nodes[a] = set()
  174.  
  175. def add_edge(self, a, b):
  176. self.add_node(a)
  177. self.add_node(b)
  178. self._nodes[a].add(b)
  179. self._nodes[b].add(a)
  180. if (a, b) not in self._edges:
  181. self._set_weight(a, b, 0)
  182.  
  183. def add_weighted_edge(self, a, b, weight):
  184. self.add_edge(a, b)
  185. old = self.weight(a, b)
  186. self._set_weight(a, b, old + weight)
  187.  
  188. def set_weighted_edge(self, a, b, weight):
  189. self.add_edge(a, b)
  190. self._set_weight(a, b, weight)
  191.  
  192. def neighbours(self, a):
  193. return self._nodes.get(a, None)
  194.  
  195. def weight(self, a, b):
  196. w = self._edges.get((a, b), None)
  197. if w != None:
  198. return w
  199. return self._edges.get((b, a), None)
  200.  
  201. def has_edge(self, a, b):
  202. return (a, b) in self._edges or (b, a) in self._edges
  203.  
  204. def is_tree(self):
  205. # is tree if fully connected and acyclic
  206. if len(self._nodes) == 0:
  207. return False
  208. root = list(self._nodes.keys())[0]
  209. parent = {root: None}
  210. queue = deque()
  211. queue.append(root)
  212. while queue:
  213. node = queue.popleft()
  214. # check for cycle
  215. for neighbour in self.neighbours(node):
  216. if neighbour in parent:
  217. if parent[node] != neighbour:
  218. return False
  219. else:
  220. parent[neighbour] = node
  221. queue.append(neighbour)
  222.  
  223. # check fully connected
  224. return parent.keys() == self._nodes.keys()
  225.  
  226. def nodes(self):
  227. return self._nodes.keys()
  228.  
  229. def degree(self, node):
  230. return len(self._nodes[node])
  231.  
  232. def is_leaf(self, node):
  233. return self.degree(node) == 1
Add Comment
Please, Sign In to add comment