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

Fix docstrings, impl AN.__iter__ & AN.__call__

parent 95cfdba3
No related branches found
No related tags found
No related merge requests found
...@@ -4,42 +4,45 @@ import collections ...@@ -4,42 +4,45 @@ import collections
flatten = lambda l: [item for sublist in l for item in sublist] flatten = lambda l: [item for sublist in l for item in sublist]
# TODO: History should also store UP
# TODO: Store and enforce node dependencies
class Mergeable_dict(dict): class Mergeable_dict(dict):
"""Like dict, except that foo += bar update all entries of foo with the """
corresponding values in bar. This requires that bar.keys() is a subset of Subclass of dict, except that mergeable_dict += dict shall update all
foo.keys(), otherwise IndexError is raised.""" entries of foo with the corresponding values in bar.
This requires that bar.keys() is a subset of foo.keys(), otherwise
IndexError is raised.
"""
def __iadd__(self, other): def __iadd__(self, other):
for k in other.keys(): for k in other.keys():
if not k in self.keys(): raise IndexError() if not k in self.keys(): raise IndexError()
self[k] = other[k] self[k] = other[k]
return self return self
# BUG: changing the UP manually (not inc/dec) will break the assumption
# that an.unstep(); an.step() is no-op.
# The other way around, an.step(); an.unstep() is always working though.
class AN: class AN:
""" """
An automata network. An automata network.
Fields: Fields:
nodes -- a set node "names", that can be arbitrary (hashable) values nodes -- set node "names", that can be arbitrary (hashable) values
dependencies -- a dict mapping nodes to lists/sets of nodes, on which it dependencies -- dict mapping nodes to lists/sets of nodes, encoding the
depends for the update dependency graph of the network
update_funcs -- a dict mapping nodes to functions ; each function takes a update_funcs -- dict mapping nodes to functions; each function takes a
dictionary (neighbour node→current value) as a unique dictionary (neighbour node→current value) as sole argument
parameter
(it is possible to do my_an.update_funcs += dict to merge (it is possible to do my_an.update_funcs += dict to merge
in a dictionary, i.e. to change the functions of only some in a dictionary, i.e. to change the functions of only some
nodes) nodes)
update_mode -- a list of sets (or list of lists) interpreted as a update_mode -- list of sets (or list of lists) interpreted as a periodic
periodic update mode update mode
init_state -- a dict from nodes to values, that tells which values should init_state -- dict mapping nodes to values, that tells which values
be used when the network is (re)initialized should be used when the network is (re)initialized
state -- a dict nodes→values holding the current state (value of each state -- dict nodes→values holding the current state (value of each
node) of the network node) of the network
history -- a circular buffer of the last self.history_len states history -- circular buffer of the last self.history_len states
traversed by the network traversed by the network
up -- (Update Pointer) is an index for self.update_mode ; it up -- Update Pointer; an index for self.update_mode; it
“points” to the next update to do “points” to the next update to do
""" """
def __init__(self, nodes, dependencies, update_funcs, update_mode, init_state=None, history_len=10): def __init__(self, nodes, dependencies, update_funcs, update_mode, init_state=None, history_len=10):
...@@ -47,7 +50,7 @@ class AN: ...@@ -47,7 +50,7 @@ class AN:
self.dependencies = dependencies self.dependencies = dependencies
self.update_funcs = update_funcs self.update_funcs = update_funcs
self.update_mode = update_mode self.update_mode = update_mode
self.init_state = init_state self.init_state = init_state # None is properly handled by a property
self.reinit_state() # Sets self.state, self.up self.reinit_state() # Sets self.state, self.up
self.history = collections.deque(maxlen=history_len) self.history = collections.deque(maxlen=history_len)
...@@ -66,7 +69,7 @@ class AN: ...@@ -66,7 +69,7 @@ class AN:
def set_multiple_update_funcs(self, new_update_funcs): def set_multiple_update_funcs(self, new_update_funcs):
""" """
If update_funcs is a dictionary node->function, change the update If update_funcs is a dictionary nodefunction, change the update
function of each node to the corresponding function. All nodes in function of each node to the corresponding function. All nodes in
update_funcs.keys() need to exist; but not all nodes from the network update_funcs.keys() need to exist; but not all nodes from the network
need to be changed by this operation. need to be changed by this operation.
...@@ -96,17 +99,17 @@ class AN: ...@@ -96,17 +99,17 @@ class AN:
return self._update_mode return self._update_mode
@update_mode.setter @update_mode.setter
def update_mode(self, update_mode): def update_mode(self, new_update_mode):
""" """
Change the update mode. Change the update mode.
update_mode needs to be a list of sets (or list of lists) and is update_mode needs to be a list of sets (or list of lists) and is
interpreted as a periodic update mode. interpreted as a periodic update mode.
Changing the update_mode clears the state history to ensure that This resets up to 0 and clears the history as side-effects.
an.unstep() followed an.step() is no-op (or raises an exception).
""" """
if not set(flatten(update_mode)).issubset(self.nodes): if not set(flatten(new_update_mode)).issubset(self.nodes):
raise ValueError("update_mode tries to update a nonexistent node") raise ValueError("update_mode tries to update a nonexistent node")
self._update_mode = update_mode self._update_mode = new_update_mode
self.up = 0
self.history.clear() self.history.clear()
@property @property
...@@ -114,7 +117,7 @@ class AN: ...@@ -114,7 +117,7 @@ class AN:
return self._init_state return self._init_state
@init_state.setter @init_state.setter
def init_state(self, init_state): def init_state(self, new_init_state):
""" """
Change initial state. Change initial state.
Initial state is used when reinitializing the network. Initial state is used when reinitializing the network.
...@@ -122,11 +125,11 @@ class AN: ...@@ -122,11 +125,11 @@ class AN:
must be a dictionary node->value, where init_state.keys() is equal to must be a dictionary node->value, where init_state.keys() is equal to
the set of nodes of the network. the set of nodes of the network.
""" """
if init_state: if new_init_state:
if set(init_state.keys()) != self.nodes: if set(new_init_state.keys()) != self.nodes:
raise ValueError("init_state either misses state for a node or has a sate for a nonexistent node") raise ValueError("""init_state either misses state for a node
else: or has a sate for a nonexistent node""")
self._init_state = init_state.copy() self._init_state = new_init_state.copy()
else: else:
self._init_state = {} self._init_state = {}
for n in self.nodes: for n in self.nodes:
...@@ -147,7 +150,7 @@ class AN: ...@@ -147,7 +150,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 = collections.deque(new_history_len)
for i in range(min(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
...@@ -165,7 +168,8 @@ class AN: ...@@ -165,7 +168,8 @@ class AN:
def step(self): def step(self):
"""Execute one step of the AN.""" """Execute one step of the AN."""
new_state = self.state.copy() new_state = self.state.copy()
for n in self.update_mode[self.up]: for node in self.update_mode[self.up]:
local_view = dict((neighbour, self.state[neighbour]) for neighbour in self.dependencies[node])
new_state[n] = (self.update_func[n])(self.state) new_state[n] = (self.update_func[n])(self.state)
self.history.append(self.state) self.history.append(self.state)
...@@ -184,6 +188,15 @@ class AN: ...@@ -184,6 +188,15 @@ class AN:
# ################################################################ # ################################################################
def __iter__(self):
return self.nodes
def __call__(self):
self.step()
return self.state.copy()
# ################################################################
def button_press(event): def button_press(event):
print("clicked at ({},{})".format(event.x, event.y)) print("clicked at ({},{})".format(event.x, event.y))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment