diff --git a/simra.py b/simra.py index 06185d3aa3e0a5f3bab83534923176f50b83ff72..baf971395d30dd1fbcad759c9793710646fc5e5f 100755 --- a/simra.py +++ b/simra.py @@ -1,6 +1,10 @@ -#!/usr/bin/env python3 -import tkinter as TK +/#!/usr/bin/env python3 import collections +from functools import reduce +from itertools import count +import tkinter as TK + +# ################################################################ def dict_filter(dictionary, keys): """ @@ -8,13 +12,11 @@ def dict_filter(dictionary, keys): """ return dict((key, dictionary[key]) for key in keys) -def flatten(l): - """Given a list of lists l, concatenate all elements of l.""" - return [item for sublist in l for item in sublist] +# ################################################################ class Node: """ - An Automata Network Node. + An automata network node. Constructor fields: name -- Any hashable value, used for printing and identification @@ -159,6 +161,85 @@ class AN: # ################################################################ +def land(b1, b2): + """Like `and`, but a function (usable with `reduce`)""" + return b1 and 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 + and negative neighbours 2, 4. + """ + def node_update_func(args): + nonlocal dep_list + l = [] + for n in dep_list: + if n<0: l.append(not args[-n]) + else: l.append(args[n]) + return reduce(land, l) + return node_update_func + +def and_not(dep_lists, init_vals, update_mode): + """ + Builds an and-not network. + Nodes are named 1, …, N where N=len(dep_lists)==len(init_vals). + dep_lists[0] and init_vals[0] are ignored. + + dep_lists[i] is the list of neighbours of i (so a list of integers). + A negative neighbour means a negated neighbour. + For example if dep_list[1] contains 4, then 1 depends positively on 4; + if dep_list[1] contains -4, then 1 depends negatively on 4. + + init_vals is a list of booleans, so that init_vals[i] is the initial + value of node i. + + update_mode is a periodic update mode, i.e. a list of sets of integers. + """ + 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) + 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) + +# ################################################################ + +example_deps = [ + None, #ignored + [3, 5], + [4, 6], + [1, 7], + [2, 7], + [1, -8], + [2, -8], + [3, 4, 9], + [-5, -6, -9, -10], + [-8, 13, 14], + [11, 12], + [10], + [10], + [9, 14], + [9, 13], +] + +example_init_vals = [ + None, #ignored +] + +example_update = [ + [], +] + +# ################################################################ + +def parallel_mode(AN): + """Return a parallel update mode for AN.""" + return [[node.name for node in AN.nodes]] + +# ################################################################ + def button_press(event): print("clicked at ({},{})".format(event.x, event.y))