Skip to content
Snippets Groups Projects
Commit 500c61ff authored by Guilhem Gamard's avatar Guilhem Gamard
Browse files

First draft

parent 35425efa
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3 #!/usr/bin/env python3
import collections from collections import deque
import copy from copy import deepcopy
from functools import reduce from functools import reduce
from itertools import count from itertools import count
import tkinter as TK import tkinter as TK
...@@ -8,6 +8,19 @@ import tkinter as TK ...@@ -8,6 +8,19 @@ import tkinter as TK
# ################################################################ # ################################################################
# General utilities # 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): def dict_filter(dictionary, keys):
""" """
Return a subdictionary of `dictionary` comprising only the given `keys`. Return a subdictionary of `dictionary` comprising only the given `keys`.
...@@ -21,6 +34,9 @@ def integers(n): ...@@ -21,6 +34,9 @@ def integers(n):
return list(range(n)) return list(range(n))
def flatten(l): 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] return [item for sublist in l for item in sublist]
# ################################################################ # ################################################################
...@@ -32,6 +48,7 @@ class Node: ...@@ -32,6 +48,7 @@ class Node:
Constructor fields: Constructor fields:
name -- Any hashable value, used for printing and identification 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 update_func -- Update Function; it must take one parameter: a dictionary
mapping name of neighbours to their values mapping name of neighbours to their values
dependencies -- List of dependency names (other nodes' names) dependencies -- List of dependency names (other nodes' names)
...@@ -40,7 +57,6 @@ class Node: ...@@ -40,7 +57,6 @@ class Node:
Other fields: Other fields:
state -- Curent value of the node state -- Curent value of the node
""" """
def __init__(self, name, update_func=None, dependencies=[], init_state=0): def __init__(self, name, update_func=None, dependencies=[], init_state=0):
self.name = name self.name = name
self.update_func = update_func self.update_func = update_func
...@@ -55,8 +71,7 @@ class Node: ...@@ -55,8 +71,7 @@ class Node:
self.dependencies.append(new_dependency) self.dependencies.append(new_dependency)
def update(self, neigh_vals): def update(self, neigh_vals):
f = self.update_func self.state = self.update_func(neigh_vals)
self.state = f(neigh_vals)
def __repr__(self): def __repr__(self):
return "{{node {}: {}}}".format(self.name, self.state) return "{{node {}: {}}}".format(self.name, self.state)
...@@ -90,11 +105,11 @@ class AN: ...@@ -90,11 +105,11 @@ class AN:
written to `_nodes`. 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.nodes = nodes
self.update_mode = update_mode self.update_mode = update_mode
self.up = 0 self.up = 0
self.history = collections.deque(maxlen=history_len) self.history = deque(maxlen=history_len)
# ################################################################ # ################################################################
...@@ -138,7 +153,7 @@ class AN: ...@@ -138,7 +153,7 @@ class AN:
@history_len.setter @history_len.setter
def history_len(self, new_history_len): 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)): for i in range(min(new_history_len, self.history_len)):
new_history.append(self.history[-(i+1)]) new_history.append(self.history[-(i+1)])
self.history = new_history self.history = new_history
...@@ -154,28 +169,26 @@ class AN: ...@@ -154,28 +169,26 @@ class AN:
def reset_state(self): def reset_state(self):
"""Set each node to its initial value, and the up to 0.""" """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: for node in new_nodes:
node.reset_state() node.reset_state()
self.history.append(self._nodes) self.history.append(self._nodes)
self._nodes = new_nodes self._nodes = new_nodes
self.up = 0 self.up = 0
def update(self): def update(self):
"""Update the AN once.""" """Update the AN once."""
new_nodes = copy.deepcopy(self._nodes) new_nodes = deepcopy(self._nodes)
for n in self.update_mode[self.up]: for n in self.update_mode[self.up]:
local_view = dict_filter(self.state, self._nodes[n].dependencies) local_view = dict_filter(self.state, self._nodes[n].dependencies)
new_nodes[n].update(local_view) new_nodes[n].update(local_view)
self.history.append(self._nodes)
self.history.append(self.nodes)
self._nodes = new_nodes self._nodes = new_nodes
self.up += 1 self.up += 1
def unupdate(self): def unupdate(self):
"""Roll back one step of the AN, within limits of history_len.""" """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 self.up -= 1
def run(self, steps): def run(self, steps):
...@@ -220,7 +233,7 @@ def is_block_seq(mode): ...@@ -220,7 +233,7 @@ def is_block_seq(mode):
def block_seq(mode): def block_seq(mode):
""" """
Checks that `mode` is block-sequential. If so, return it. 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() if not is_block_seq(mode): raise ValueError()
return mode return mode
...@@ -228,12 +241,12 @@ def block_seq(mode): ...@@ -228,12 +241,12 @@ def block_seq(mode):
def periodic_mode(mode): def periodic_mode(mode):
""" """
The identity function, with another name. The identity function, with another name.
Useful for functionnal programming. Useful for functional programming.
""" """
return mode return mode
# ################################################################ # ################################################################
# Trivial nodes # Node construction
def node_fixpoint(name, init_val=0): def node_fixpoint(name, init_val=0):
""" """
...@@ -302,7 +315,7 @@ def cycle(signs, init_vals=None, update_mode=None): ...@@ -302,7 +315,7 @@ def cycle(signs, init_vals=None, update_mode=None):
return AN(nodes, update_mode) return AN(nodes, update_mode)
# ################################################################ # ################################################################
# And-not symmetric networks # And-not networks
def land(b1, b2): def land(b1, b2):
"""Like `and`, but a function (usable with `reduce`)""" """Like `and`, but a function (usable with `reduce`)"""
...@@ -311,7 +324,7 @@ def land(b1, b2): ...@@ -311,7 +324,7 @@ def land(b1, b2):
def and_not_update_func(dep_list): def and_not_update_func(dep_list):
""" """
Return an update function for a node in an and_not network. 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. and negative neighbours 2, 4.
""" """
def node_update_func(args): def node_update_func(args):
...@@ -341,11 +354,12 @@ def and_not(dep_lists, init_vals, update_mode): ...@@ -341,11 +354,12 @@ def and_not(dep_lists, init_vals, update_mode):
""" """
nodes = [] nodes = []
for (dep_list, init_val, i) in zip(dep_lists[1:], init_vals[1:], count(1)): 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] deps = [abs(d) for d in dep_list]
node = Node(i, update_func, deps, init_val) node = Node(i, update_func, deps, init_val)
nodes.append(node) nodes.append(node)
return AN(nodes[1:], update_mode) return AN(nodes, update_mode)
# ################################################################ # ################################################################
...@@ -375,27 +389,3 @@ example_update = [ ...@@ -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()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment