Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
SimRA
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Model registry
Analyze
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Guilhem Gamard
SimRA
Commits
d0da03de
Commit
d0da03de
authored
5 years ago
by
Guilhem Gamard
Browse files
Options
Downloads
Patches
Plain Diff
Fixed nasty bug in AN.update()
parent
14d7e75d
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
simra.py
+152
-24
152 additions, 24 deletions
simra.py
with
152 additions
and
24 deletions
simra.py
+
152
−
24
View file @
d0da03de
/
#!/usr/bin/env python3
#!/usr/bin/env python3
import
collections
import
copy
from
functools
import
reduce
from
itertools
import
count
import
tkinter
as
TK
# ################################################################
# General utilities
def
dict_filter
(
dictionary
,
keys
):
"""
...
...
@@ -12,7 +14,17 @@ def dict_filter(dictionary, keys):
"""
return
dict
((
key
,
dictionary
[
key
])
for
key
in
keys
)
def
integers
(
n
):
"""
Return the list of integers from 0 (included) to n-1 (included).
"""
return
list
(
range
(
n
))
def
flatten
(
l
):
return
[
item
for
sublist
in
l
for
item
in
sublist
]
# ################################################################
# Automata network themselves
class
Node
:
"""
...
...
@@ -37,13 +49,17 @@ class Node:
self
.
reset_state
()
# Sets self.state
def
reset_state
(
self
):
self
.
state
=
self
.
init_state
.
copy
()
self
.
state
=
self
.
init_state
def
add_dependency
(
self
,
new_dependency
):
self
.
dependencies
.
append
(
new_dependency
)
def
update
(
self
,
neigh_vals
):
self
.
state
=
self
.
update_func
(
neigh_vals
)
f
=
self
.
update_func
self
.
state
=
f
(
neigh_vals
)
def
__repr__
(
self
):
return
"
{{node {}: {}}}
"
.
format
(
self
.
name
,
self
.
state
)
class
AN
:
"""
...
...
@@ -59,11 +75,11 @@ class AN:
Other fields:
up -- Stands for Update Pointer.
It is an index in update_mode tha
n
t “points” to the next update
It is an index in update_mode that “points” to the next update
to perform. Note that foo.up can be publicly read and written.
A modulo len(update_mode) is implicitly applied, so it is safe
to do, e.g., foo.up +=1 on an update.
history -- A circular buffer of deep copies of the previous values of _node
history -- A circular buffer of deep copies of the previous values of _node
s
(see below).
It can be publicly accessed. Please do not alter the history
by yourself (Stalin did nothing right).
...
...
@@ -85,13 +101,18 @@ class AN:
@property
def
nodes
(
self
):
"""
Return the set of all nodes.
"""
return
self
.
_nodes
.
item
s
()
return
self
.
_nodes
.
value
s
()
@nodes.setter
def
nodes
(
self
,
new_nodes
):
self
.
_nodes
=
{}
for
node
in
new_nodes
:
self
.
add_node
(
node
)
@property
def
state
(
self
):
"""
Return a dictionary mapping node names to node states.
"""
return
dict
((
name
,
self
.
_nodes
[
name
].
state
)
for
name
in
self
.
_nodes
.
keys
())
def
add_node
(
self
,
node
):
"""
Adds a new node to the AN.
"""
self
.
_nodes
[
node
.
name
]
=
node
...
...
@@ -122,11 +143,18 @@ class AN:
new_history
.
append
(
self
.
history
[
-
(
i
+
1
)])
self
.
history
=
new_history
def
__repr__
(
self
):
result
=
"
{AN
\n
"
for
node
in
self
.
nodes
:
result
+=
"
"
+
str
(
node
)
+
"
\n
"
result
+=
"
}
"
return
result
# ################################################################
def
reset_state
(
self
):
"""
Set each node to its initial value, and the up to 0.
"""
new_nodes
=
self
.
_nodes
.
deep_
copy
()
new_nodes
=
self
.
_nodes
.
copy
()
for
node
in
new_nodes
:
node
.
reset_state
()
...
...
@@ -136,9 +164,9 @@ class AN:
def
update
(
self
):
"""
Update the AN once.
"""
new_nodes
=
self
.
_nodes
.
deep_copy
(
)
new_nodes
=
copy
.
deepcopy
(
self
.
_nodes
)
for
n
in
self
.
update_mode
[
self
.
up
]:
local_view
=
dict_filter
(
self
.
nodes
,
self
.
nodes
[
n
].
dependencies
)
local_view
=
dict_filter
(
self
.
state
,
self
.
_
nodes
[
n
].
dependencies
)
new_nodes
[
n
].
update
(
local_view
)
self
.
history
.
append
(
self
.
nodes
)
...
...
@@ -147,7 +175,7 @@ class AN:
def
unupdate
(
self
):
"""
Roll back one step of the AN, within limits of history_len.
"""
self
.
_nodes
=
self
.
history
.
pop
()
self
.
_nodes
=
copy
.
deepcopy
(
self
.
history
.
pop
()
)
self
.
up
-=
1
def
run
(
self
,
steps
):
...
...
@@ -160,6 +188,112 @@ class AN:
self
.
update
()
# ################################################################
# Update modes
def
parallel_mode
(
node_names
):
"""
Return a parallel update mode.
"""
return
[
node_names
]
def
local_clocks
(
period
,
deltas
):
"""
Return a local clocks update mode.
period -- The global period update.
deltas -- A dictionary mapping node names to phases.
"""
result
=
[]
*
global_periods
for
k
in
deltas
.
keys
():
result
[
deltas
[
k
]].
append
(
k
)
return
result
def
is_block_seq
(
mode
):
"""
Return whether an update mode is block sequential
(no node is updated twice).
"""
already_seen
=
set
()
for
node_name
in
flatten
(
mode
):
if
node_name
in
already_seen
:
return
False
else
:
already_seen
.
add
(
name
)
return
True
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
is_block_seq
(
mode
):
raise
ValueError
()
return
mode
def
periodic_mode
(
mode
):
"""
The identity function, with another name.
Useful for functionnal programming.
"""
return
mode
# ################################################################
# Trivial nodes
def
node_fixpoint
(
name
,
init_val
=
0
):
def
identity
(
args
):
for
k
in
args
.
keys
():
return
args
[
k
]
return
Node
(
name
,
identity
,
[
name
],
init_val
)
def
node_constant
(
name
,
value
=
0
,
init_val
=
value
):
def
constant
(
args
):
nonlocal
value
return
value
return
Node
(
name
,
constant
,
[],
init_val
)
# ################################################################
# Cycle ANs
def
cycle_update_func
(
sign
=
True
):
"""
Return an update function for a node in a cycle.
All update functions assume only one neighbour.
If sign=True, the returned update function is identity.
If sign=False, the returned update function is negation.
"""
def
positive
(
neighs
):
for
k
in
neighs
.
keys
():
return
neighs
[
k
]
def
negative
(
neighs
):
for
k
in
neighs
.
keys
():
return
not
neighs
[
k
]
if
sign
:
return
positive
else
:
return
negative
def
cycle
(
signs
,
init_vals
=
None
,
update_mode
=
None
):
"""
Return a cyclic AN.
Node names are 0,…,n-1, with n=len(signs). Node i depends on node i-1 mod n.
signs -- signs[i] is True if i depends positively on i-1, False if
negatively
init_vals -- list of initial values (booleans); defaults to all True
update_mode -- a periodic update mode; defaults to parallel
"""
num_nodes
=
len
(
signs
)
node_names
=
integers
(
num_nodes
)
if
not
init_vals
:
init_vals
=
[
True
]
*
num_nodes
if
not
update_mode
:
update_mode
=
parallel_mode
(
node_names
)
nodes
=
[]
for
(
sign
,
init_val
,
i
)
in
zip
(
signs
,
init_vals
,
count
()):
update_func
=
cycle_update_func
(
sign
)
node
=
Node
(
i
,
update_func
,
[(
i
-
1
)
%
num_nodes
],
init_val
)
nodes
.
append
(
node
)
return
AN
(
nodes
,
update_mode
)
# ################################################################
# And-not symmetric networks
def
land
(
b1
,
b2
):
"""
Like `and`, but a function (usable with `reduce`)
"""
...
...
@@ -182,19 +316,19 @@ def and_not_update_func(dep_list):
def
and_not
(
dep_lists
,
init_vals
,
update_mode
):
"""
Builds
an and-not network.
Return
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).
dep_lists -- dep_lists[0] is 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.
init_vals -- init_vals[0] is ignored; init_vals[i] is the initial value of
node i
update_mode -- 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
)):
...
...
@@ -234,12 +368,6 @@ 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
))
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment