Skip to content

Commit 9749b44

Browse files
authored
merge refactor (#14)
* [+] documentation, reformatting, new elements [-] trigrid class, multiprocessing objects * [+/-] complete refactoring [!] test after merge
1 parent 549544e commit 9749b44

26 files changed

+1580
-1010
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.

amoebot/elements/bot/agent.py

+92-338
Large diffs are not rendered by default.

amoebot/elements/bot/core.py

+137-47
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,97 @@
1+
# -*- coding: utf-8 -*-
2+
3+
""" elements/bot/manager.py
4+
"""
5+
6+
from ..core import Core
7+
18
import pickle
29
import numpy as np
310
from collections import defaultdict
4-
from numpy import array, ndarray, int8, uint8, float16
5-
6-
from ..core import Core
711

812
# global wait time for agents
9-
WAIT_TIME: uint8 = 64
13+
WAIT_TIME: np.uint8 = 64
1014

1115
class Amoebot(Core):
12-
r""" core amobeot functionalities, extended using algorithmic modules
16+
r"""
17+
Core amobeot functionalities, extended using algorithmic modules.
18+
19+
Attributes
20+
21+
__id (numpy.uint8) :: unique particle identifier.
22+
head (numpy.ndarray) :: position (x and y co-ordinates) of amoebot head.
23+
tail (numpy.ndarray) :: position (x and y co-ordinates) of amoebot tail.
24+
labels (numpy.ndarray) :: lookup directions in grid from port labels.
25+
h_neigbor_status (dict) :: status of the amoebot's neighbourhood around
26+
its head.
27+
t_neigbor_status (dict) :: status of the amoebot's neighbourhood around
28+
its tail.
29+
mu (numpy.uint8) :: rate parameter for Poisson clock.
30+
tau0 (numpy.float16) :: temperature parameter for compression.
31+
cflag (numpy.uint8) :: status flag for compression algorithm.
32+
active (bool) :: activation status of the amoebot
33+
clock (numpy.uint8) :: time on the Poisson clock.
34+
1335
"""
1436

15-
def __init__(self, __id:uint8, head:array, tail:array=None):
37+
def __init__(self, __id:np.uint8, head:np.ndarray, tail:np.ndarray=None):
38+
r"""
39+
Attributes
40+
41+
__id (numpy.uint8) :: unique particle identifier.
42+
head (numpy.ndarray) :: position (x and y co-ordinates) of amoebot head.
43+
tail (numpy.ndarray) default: None :: position (x and y co-ordinates) of
44+
amoebot tail.
45+
"""
1646

1747
# bots should be locally indistinguishable, this name-mangled
18-
# variable is accesible only to the manager
19-
self.__id:uint8 = __id
48+
# variable is accesible only to the `AmoebotManager` instance.
49+
self.__id:np.uint8 = __id
2050

2151
# head of the bot
22-
self.head:array = head
52+
self.head:np.ndarray = head
2353

2454
# tail of the bot, initially same as head
25-
self.tail:array = head if tail is None else tail
55+
self.tail:np.ndarray = head if tail is None else tail
2656

2757
# lookup/map for port labels to the general grid direction
28-
self.labels:dict = dict([(uint8(ix), None) for ix in range(6)])
58+
self.labels:dict = self._reset_neighbourhood_map()
2959

3060
# status of the bot's neighbourhood
31-
self.h_neighbor_status = dict([(uint8(ix), None) for ix in range(6)])
32-
self.t_neighbor_status = dict([(uint8(ix), None) for ix in range(6)])
61+
self.h_neighbor_status:dict = self._reset_neighbourhood_map()
62+
self.t_neighbor_status:dict = self._reset_neighbourhood_map()
3363

34-
# rate parameter for poisson clock
35-
self.mu:uint8 = uint8(1)
64+
# rate parameter for Poisson clock
65+
self.mu:np.uint8 = np.uint8(1)
3666

37-
# compression parameter
38-
self.lam:float16 = float16(5.)
67+
# temperature parameter for compression
68+
self.tau0:np.float16 = np.float16(1.)
3969

4070
# status flag for compression algorithm
41-
self.cflag:uint8 = uint8(0)
71+
self.cflag:np.uint8 = np.uint8(0)
4272

4373
# activation status, true means the bot is active
44-
self.active, self.clock = self._reset_clock(refresh=True)
74+
# self.active, self.clock = self._reset_clock(refresh=True)
4575

4676
# wait timer for agents
4777
# self.wait: uint8 = WAIT_TIME
4878

49-
def orient(self, nmap:defaultdict=None):
50-
r""" orients bot in clockwise ordering of ports starting at random
79+
def orient(self, __nmap:defaultdict=None):
80+
r"""
81+
Orients bot in clockwise ordering of ports starting at random by
82+
labelling each port.
83+
84+
NOTE this construction violates the basic constraint of the amoebot
85+
model that particles have no sense of direction; but is kept for
86+
convenience.
87+
88+
Attributes
89+
__nmap (defaultdict) default: None :: a dictionary of dictionaries
90+
used to index nodes using x and y co-odrinates.
5191
"""
5292

5393
# ports labellings of the node
54-
node_ports = array(['n', 'ne', 'se',
55-
's', 'sw', 'nw'
56-
], dtype='<U2')
94+
node_ports = np.array(['n', 'ne', 'se', 's', 'sw', 'nw'], dtype='<U2')
5795

5896
# choose a port at random
5997
choice = np.random.choice(node_ports)
@@ -64,63 +102,115 @@ def orient(self, nmap:defaultdict=None):
64102
self.labels[ix] = node_ports[choice_ix % 6]
65103
choice_ix += 1
66104

67-
if nmap is not None: self.generate_neighbourhood_map(nmap)
105+
if __nmap is not None: self.generate_neighbourhood_map(__nmap)
68106

69-
def generate_neighbourhood_map(self, nmap:defaultdict):
107+
def generate_neighbourhood_map(self, __nmap:defaultdict):
70108
r"""
71109
Creates a dictionary of neighbourhood statuses around the head
72110
(and tail) particles.
111+
112+
Attributes
113+
__nmap (defaultdict) :: a dictionary of dictionaries
114+
used to index nodes using x and y co-ordinates.
73115
"""
116+
117+
# map the neighbourhood around the particle head
74118
for port in self.h_neighbor_status:
75-
node = nmap[self.head[0]][self.head[1]]
119+
node = __nmap[self.head[0]][self.head[1]]
120+
121+
# get the position of neighbour on current port
76122
n_position = node.neighbors[self.labels[port]]
77123

78124
# mark the neighbourhood status except own tail
79125
if np.any(n_position != self.tail):
80126
if n_position is not None:
81-
neighbor = nmap[n_position[0]][n_position[1]]
127+
neighbor = __nmap[n_position[0]][n_position[1]]
82128
self.h_neighbor_status[port] = neighbor.get_occupancy_status
83-
else: self.h_neighbor_status[port] = int8(5)
84-
else: self.h_neighbor_status[port] = int8(-1)
129+
# mark own tail with (-1)
130+
else: self.h_neighbor_status[port] = np.int8(-1)
85131

86132
# if this is an expanded particle, also map the tail
87133
if not self._is_contracted:
88134
for port in self.t_neighbor_status:
89-
node = nmap[self.tail[0]][self.tail[1]]
135+
node = __nmap[self.tail[0]][self.tail[1]]
136+
137+
# get the position of neighbour on current port
90138
n_position = node.neighbors[self.labels[port]]
91139

92140
# mark the neighbourhood status except own tail
93141
if np.any(n_position != self.head):
94142
if n_position is not None:
95-
neighbor = nmap[n_position[0]][n_position[1]]
143+
neighbor = __nmap[n_position[0]][n_position[1]]
96144
self.t_neighbor_status[port] = \
97145
neighbor.get_occupancy_status
98-
else: self.t_neighbor_status[port] = int8(5)
99-
else: self.t_neighbor_status[port] = int8(-1)
100-
# else reset the tail to null state
101-
else: self.t_neighbor_status = dict([(ix, None) for ix in range(6)])
102-
103-
def open_ports(self, scan_tail:bool=False) -> list:
104-
r""" a list of open ports for bot to move to
146+
# mark own head with (-1)
147+
else: self.t_neighbor_status[port] = np.int8(-1)
148+
# else clear the tail neigbourhood status flags
149+
else: self.t_neighbor_status = self._reset_neighbourhood_map()
150+
151+
def _neighborhood_contraction_status(
152+
self,
153+
scan_tail:bool=False
154+
) -> np.ndarray:
155+
r"""
156+
Find the contraction status of the neighbourhood, a list that is
157+
1 when a neighbouring node is unoccupied, contracted or occupied by a
158+
tail and 0 when occupied by an expanded head particle.
159+
160+
Attributes
161+
scan_tail (bool) :: if true, scan the tail; else scan the particle
162+
head.
163+
164+
Return (np.ndarray): a list of contraction statuses in neighbouring
165+
positions.
105166
"""
167+
168+
scan = self.t_neighbor_status if scan_tail else self.h_neighbor_status
169+
return np.array([status != 2 for _, status in scan.items()])
170+
171+
def open_ports(self, scan_tail:bool=False) -> np.ndarrary:
172+
r"""
173+
Scan the particle neighbourhood for ports to expand into.
174+
175+
Attributes
176+
scan_tail (bool) default: False :: if true, scan the tail; else scan
177+
the particle head.
178+
179+
Return (numpy.ndarrary): a list of open or available ports.
180+
"""
181+
106182
open_port_list = list()
107183
scan = self.t_neighbor_status if scan_tail else self.h_neighbor_status
108184

109185
for port in scan:
110186
status = scan[port]
111-
if status == 0:
187+
188+
# nodes with status 0 are unoccupied
189+
if status is np.uint8(0):
112190
open_port_list.append(port)
113191

114-
return array(open_port_list)
192+
return np.array(open_port_list)
193+
194+
def _reset_neighbourhood_map(self) -> dict:
195+
r"""
196+
Return (dict): dictionary with port labels as keys and `None` values.
197+
"""
198+
return dict([(ix, None) for ix in range(6)])
115199

116-
def _reset_clock(self, refresh:bool) -> bool:
200+
def _reset_clock(self, refresh:bool) -> tuple:
117201
r"""
202+
Set or reset the amoebots Poisson clock whenever the particle execution
203+
occurs.
204+
205+
Attributes
206+
refresh (bool) :: refresh the clock activation if true.
207+
208+
Returns (tuple): 2-tuple with activation status (bool) and value of the
209+
Poisson clock.
118210
"""
119211
if refresh:
120-
self.clock = uint8(np.random.poisson(lam=self.mu))
121-
212+
self.clock = np.uint8(np.random.poisson(lam=self.mu))
122213
else: self.clock -= 1
123-
124214
return (False, self.clock) if self.clock else (True, self.clock)
125215

126216
@property
@@ -143,6 +233,6 @@ def unpickled(cls, data:bytes): return pickle.loads(data)
143233

144234
def execute(self): raise NotImplementedError
145235

146-
def move_agent(self, **kwargs): raise NotImplementedError
236+
def _move(self, **kwargs): raise NotImplementedError
147237

148-
def compress_agent(self, **kwargs): raise NotImplementedError
238+
def _compress(self, **kwargs): raise NotImplementedError

0 commit comments

Comments
 (0)