From 500c61ff8429f735ef00d2e7144e8644c7e3634b Mon Sep 17 00:00:00 2001
From: Guilhem Gamard <guilhem.gamard@normale.fr>
Date: Mon, 10 Feb 2020 14:03:20 +0100
Subject: [PATCH] First draft

---
 simra.py | 80 +++++++++++++++++++++++++-------------------------------
 1 file changed, 35 insertions(+), 45 deletions(-)

diff --git a/simra.py b/simra.py
index 9bfb3b0..8ae434a 100755
--- a/simra.py
+++ b/simra.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
-import collections
-import copy
+from collections import deque
+from copy import deepcopy
 from functools import reduce
 from itertools import count
 import tkinter as TK
@@ -8,6 +8,19 @@ import tkinter as TK
 # ################################################################
 # General utilities
 
+class Gensym:
+    """
+    Poor man's closure to generate unique names.
+    """
+    def __init__(self, prefix="g", seq=count()):
+        self.prefix = prefix
+        self.seq = seq
+    def __call__(self):
+        return "{}{}".format(self.prefix, next(self.seq))
+
+gensym = Gensym()
+# gensym() returns successively "g0", "g1", "g2", etc.
+
 def dict_filter(dictionary, keys):
     """
     Return a subdictionary of `dictionary` comprising only the given `keys`.
@@ -21,6 +34,9 @@ def integers(n):
     return list(range(n))
 
 def flatten(l):
+    """
+    Given a list of list, return the list of elements of sublists.
+    """
     return [item for sublist in l for item in sublist]
 
 # ################################################################
@@ -32,6 +48,7 @@ class Node:
 
     Constructor fields:
     name         -- Any hashable value, used for printing and identification
+                    (must be unique inside an AN or all hell breaks loose)
     update_func  -- Update Function; it must take one parameter: a dictionary
                     mapping name of neighbours to their values
     dependencies -- List of dependency names (other nodes' names)
@@ -40,7 +57,6 @@ class Node:
     Other fields:
     state -- Curent value of the node
     """
-    
     def __init__(self, name, update_func=None, dependencies=[], init_state=0):
         self.name = name
         self.update_func = update_func
@@ -55,8 +71,7 @@ class Node:
         self.dependencies.append(new_dependency)
     
     def update(self, neigh_vals):
-        f = self.update_func
-        self.state = f(neigh_vals)
+        self.state = self.update_func(neigh_vals)
 
     def __repr__(self):
         return "{{node {}: {}}}".format(self.name, self.state)
@@ -90,11 +105,11 @@ class AN:
                written to `_nodes`.
     """
 
-    def __init__(self, nodes={}, update_mode=[], history_len=10):
+    def __init__(self, nodes={}, update_mode=[], history_len=100):
         self.nodes = nodes
         self.update_mode = update_mode
         self.up = 0
-        self.history = collections.deque(maxlen=history_len)
+        self.history = deque(maxlen=history_len)
     
     # ################################################################
     
@@ -138,7 +153,7 @@ class AN:
     
     @history_len.setter
     def history_len(self, new_history_len):
-        new_history = collections.deque(new_history_len)
+        new_history = deque(maxlen=new_history_len)
         for i in range(min(new_history_len, self.history_len)):
             new_history.append(self.history[-(i+1)])
         self.history = new_history
@@ -154,28 +169,26 @@ class AN:
     
     def reset_state(self):
         """Set each node to its initial value, and the up to 0."""
-        new_nodes = self._nodes.copy()
+        new_nodes = deepcopy(self._nodes)
         for node in new_nodes:
             node.reset_state()
-        
         self.history.append(self._nodes)
         self._nodes = new_nodes
         self.up = 0
 
     def update(self):
         """Update the AN once."""
-        new_nodes = copy.deepcopy(self._nodes)
+        new_nodes = deepcopy(self._nodes)
         for n in self.update_mode[self.up]:
             local_view = dict_filter(self.state, self._nodes[n].dependencies)
             new_nodes[n].update(local_view)
-        
-        self.history.append(self.nodes)
+        self.history.append(self._nodes)
         self._nodes = new_nodes
         self.up += 1
 
     def unupdate(self):
         """Roll back one step of the AN, within limits of history_len."""
-        self._nodes = copy.deepcopy(self.history.pop())
+        self._nodes = self.history.pop()
         self.up -= 1
     
     def run(self, steps):
@@ -220,7 +233,7 @@ def is_block_seq(mode):
 def block_seq(mode):
     """
     Checks that `mode` is block-sequential. If so, return it.
-    If not, raise a ValueError exception. Useful for functoinal programming.
+    If not, raise a ValueError exception. Useful for functional programming.
     """
     if not is_block_seq(mode): raise ValueError()
     return mode
@@ -228,12 +241,12 @@ def block_seq(mode):
 def periodic_mode(mode):
     """
     The identity function, with another name.
-    Useful for functionnal programming.
+    Useful for functional programming.
     """
     return mode
 
 # ################################################################
-# Trivial nodes
+# Node construction
 
 def node_fixpoint(name, init_val=0):
     """
@@ -302,7 +315,7 @@ def cycle(signs, init_vals=None, update_mode=None):
     return AN(nodes, update_mode)
 
 # ################################################################
-# And-not symmetric networks
+# And-not networks
 
 def land(b1, b2):
     """Like `and`, but a function (usable with `reduce`)"""
@@ -311,7 +324,7 @@ def land(b1, b2):
 def and_not_update_func(dep_list):
     """
     Return an update function for a node in an and_not network.
-    If dep_list is [1, -2, 3, -4], the node has positive neighbours 1, 3
+    If dep_list is [1, -2, 3, -4], then the node has positive neighbours 1, 3
     and negative neighbours 2, 4.
     """
     def node_update_func(args):
@@ -341,11 +354,12 @@ def and_not(dep_lists, init_vals, update_mode):
     """
     nodes = []
     for (dep_list, init_val, i) in zip(dep_lists[1:], init_vals[1:], count(1)):
-        update_func = ant_not_update_func(dep_list)
+        print("{} {} {}".format(dep_list, init_val, i))
+        update_func = and_not_update_func(dep_list)
         deps = [abs(d) for d in dep_list]
         node = Node(i, update_func, deps, init_val)
         nodes.append(node)
-    return AN(nodes[1:], update_mode)
+    return AN(nodes, update_mode)
 
 # ################################################################
 
@@ -375,27 +389,3 @@ example_update = [
     [],
 ]
 
-# ################################################################
-
-def button_press(event):
-    print("clicked at ({},{})".format(event.x, event.y))
-
-def button_release(event):
-    print("Released at ({},{})".format(event.x, event.y))
-
-def main():
-    window = Tk()
-    window.title("SIMulateur de Réseau d'Automates")
-
-    canvas = Canvas(window)
-    canvas.create_rectangle(128, 64, 32, 32,
-        outline="#ff0000", fill="#aaaaaa")
-    canvas.bind("<Button-1>", button_press)
-    canvas.bind("<ButtonRelease-1>", button_release)
-    canvas.pack(fill=BOTH, expand=1)
-
-    window.mainloop()
-
-if __name__ == '__main__':
-    main()
-
-- 
GitLab