Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

A (one dimensional) cellular automaton is a function1 F : Σ → Σ with the property that there is a K > 0 such that F (x)i depends only on the 2K + 1 coordinates xi−K , xi−K+1, . . . , xi−1, xi, xi+1, . . . , xi+K . A periodic point of σ is any x such that σ^p (x) = x for some p ∈ N, and a periodic point of F is any x such that F^q (x) = x for some q ∈ N. Given a cellular automaton F, a point x ∈ Σ is jointly periodic if there are p, q ∈ N such that σ^p (x) = F^q (x) = x, that is, it is a periodic point under both functions.

This project aims to explore the nature of one-dimensional Cellular Automata, in the hope of finding the structure of cellular automata through its periodic points.

2034 views
License: MIT
ubuntu2004
1
from copy import deepcopy
2
3
#Regex validation
4
def is_valid_regex(regex):
5
return valid_brackets(regex) and valid_operations(regex)
6
7
8
def valid_brackets(regex):
9
opened_brackets = 0
10
for c in regex:
11
if c == '(':
12
opened_brackets += 1
13
if c == ')':
14
opened_brackets -= 1
15
if opened_brackets < 0:
16
print('ERROR missing bracket')
17
return False
18
if opened_brackets == 0:
19
return True
20
print('ERROR unclosed brackets')
21
return False
22
23
24
def valid_operations(regex):
25
for i, c in enumerate(regex):
26
if c == '*':
27
if i == 0:
28
print('ERROR * with no argument at', i)
29
return False
30
if regex[i - 1] in '(|':
31
print('ERROR * with no argument at', i)
32
return False
33
if c == '|':
34
if i == 0 or i == len(regex) - 1:
35
print('ERROR | with missing argument at', i)
36
return False
37
if regex[i - 1] in '(|':
38
print('ERROR | with missing argument at', i)
39
return False
40
if regex[i + 1] in ')|':
41
print('ERROR | with missing argument at', i)
42
return False
43
return True
44
45
class RegexNode:
46
47
@staticmethod
48
def trim_brackets(regex):
49
while regex[0] == '(' and regex[-1] == ')' and is_valid_regex(regex[1:-1]):
50
regex = regex[1:-1]
51
return regex
52
53
@staticmethod
54
def is_concat(c):
55
return c == '(' or RegexNode.is_letter(c)
56
57
@staticmethod
58
def is_letter(c):
59
return c in alphabet
60
61
def __init__(self, regex):
62
self.nullable = None
63
self.firstpos = []
64
self.lastpos = []
65
self.item = None
66
self.position = None
67
self.children = []
68
69
if DEBUG:
70
print('Current : '+regex)
71
#Check if it is leaf
72
if len(regex) == 1 and self.is_letter(regex):
73
#Leaf
74
self.item = regex
75
#Lambda checking
76
if use_lambda:
77
if self.item == lambda_symbol:
78
self.nullable = True
79
else:
80
self.nullable = False
81
else:
82
self.nullable = False
83
return
84
85
#It is an internal node
86
#Finding the leftmost operators in all three
87
kleene = -1
88
or_operator = -1
89
concatenation = -1
90
i = 0
91
92
#Getting the rest of terms
93
while i < len(regex):
94
if regex[i] == '(':
95
#Composed block
96
bracketing_level = 1
97
#Skipping the entire term
98
i+=1
99
while bracketing_level != 0 and i < len(regex):
100
if regex[i] == '(':
101
bracketing_level += 1
102
if regex[i] == ')':
103
bracketing_level -= 1
104
i+=1
105
else:
106
#Going to the next char
107
i+=1
108
109
#Found a concatenation in previous iteration
110
#And also it was the last element check if breaking
111
if i == len(regex):
112
break
113
114
#Testing if concatenation
115
if self.is_concat(regex[i]):
116
if concatenation == -1:
117
concatenation = i
118
continue
119
#Testing for kleene
120
if regex[i] == '*':
121
if kleene == -1:
122
kleene = i
123
continue
124
#Testing for or operator
125
if regex[i] == '|':
126
if or_operator == -1:
127
or_operator = i
128
129
#Setting the current operation by priority
130
if or_operator != -1:
131
#Found an or operation
132
self.item = '|'
133
self.children.append(RegexNode(self.trim_brackets(regex[:or_operator])))
134
self.children.append(RegexNode(self.trim_brackets(regex[(or_operator+1):])))
135
elif concatenation != -1:
136
#Found a concatenation
137
self.item = '.'
138
self.children.append(RegexNode(self.trim_brackets(regex[:concatenation])))
139
self.children.append(RegexNode(self.trim_brackets(regex[concatenation:])))
140
elif kleene != -1:
141
#Found a kleene
142
self.item = '*'
143
self.children.append(RegexNode(self.trim_brackets(regex[:kleene])))
144
145
def calc_functions(self, pos, followpos):
146
if self.is_letter(self.item):
147
#Is a leaf
148
self.firstpos = [pos]
149
self.lastpos = [pos]
150
self.position = pos
151
#Add the position in the followpos list
152
followpos.append([self.item,[]])
153
return pos+1
154
#Is an internal node
155
for child in self.children:
156
pos = child.calc_functions(pos, followpos)
157
#Calculate current functions
158
159
if self.item == '.':
160
#Is concatenation
161
#Firstpos
162
if self.children[0].nullable:
163
self.firstpos = sorted(list(set(self.children[0].firstpos + self.children[1].firstpos)))
164
else:
165
self.firstpos = deepcopy(self.children[0].firstpos)
166
#Lastpos
167
if self.children[1].nullable:
168
self.lastpos = sorted(list(set(self.children[0].lastpos + self.children[1].lastpos)))
169
else:
170
self.lastpos = deepcopy(self.children[1].lastpos)
171
#Nullable
172
self.nullable = self.children[0].nullable and self.children[1].nullable
173
#Followpos
174
for i in self.children[0].lastpos:
175
for j in self.children[1].firstpos:
176
if j not in followpos[i][1]:
177
followpos[i][1] = sorted(followpos[i][1] + [j])
178
179
elif self.item == '|':
180
#Is or operator
181
#Firstpos
182
self.firstpos = sorted(list(set(self.children[0].firstpos + self.children[1].firstpos)))
183
#Lastpos
184
self.lastpos = sorted(list(set(self.children[0].lastpos + self.children[1].lastpos)))
185
#Nullable
186
self.nullable = self.children[0].nullable or self.children[1].nullable
187
188
elif self.item == '*':
189
#Is kleene
190
#Firstpos
191
self.firstpos = deepcopy(self.children[0].firstpos)
192
#Lastpos
193
self.lastpos = deepcopy(self.children[0].lastpos)
194
#Nullable
195
self.nullable = True
196
#Followpos
197
for i in self.children[0].lastpos:
198
for j in self.children[0].firstpos:
199
if j not in followpos[i][1]:
200
followpos[i][1] = sorted(followpos[i][1] + [j])
201
202
return pos
203
204
def write_level(self, level):
205
print(str(level) + ' ' + self.item, self.firstpos, self.lastpos, self.nullable, '' if self.position == None else self.position)
206
for child in self.children:
207
child.write_level(level+1)
208
209
class RegexTree:
210
211
def __init__(self, regex):
212
self.root = RegexNode(regex)
213
self.followpos = []
214
self.functions()
215
216
def write(self):
217
self.root.write_level(0)
218
219
def functions(self):
220
positions = self.root.calc_functions(0, self.followpos)
221
if DEBUG == True:
222
print(self.followpos)
223
224
def toDfa(self):
225
226
def contains_hashtag(q):
227
for i in q:
228
if self.followpos[i][0] == '#':
229
return True
230
return False
231
232
M = [] #Marked states
233
Q = [] #States list in the followpos form ( array of positions )
234
V = alphabet - {'#', lambda_symbol if use_lambda else ''} #Automata alphabet
235
d = [] #Delta function, an array of dictionaries d[q] = {x1:q1, x2:q2 ..} where d(q,x1) = q1, d(q,x2) = q2..
236
F = [] #FInal states list in the form of indexes (int)
237
q0 = self.root.firstpos
238
239
Q.append(q0)
240
if contains_hashtag(q0):
241
F.append(Q.index(q0))
242
243
while len(Q) - len(M) > 0:
244
#There exists one unmarked
245
#We take one of those
246
q = [i for i in Q if i not in M][0]
247
#Generating the delta dictionary for the new state
248
d.append({})
249
#We mark it
250
M.append(q)
251
#For each letter in the automata's alphabet
252
for a in V:
253
# Compute destination state ( d(q,a) = U )
254
U = []
255
#Compute U
256
#foreach position in state
257
for i in q:
258
#if i has label a
259
if self.followpos[i][0] == a:
260
#We add the position to U's composition
261
U = U + self.followpos[i][1]
262
U = sorted(list(set(U)))
263
#Checking if this is a valid state
264
if len(U) == 0:
265
#No positions, skipping, it won't produce any new states ( also won't be final )
266
continue
267
if U not in Q:
268
Q.append(U)
269
if contains_hashtag(U):
270
F.append(Q.index(U))
271
#d(q,a) = U
272
d[Q.index(q)][a] = Q.index(U)
273
274
return Dfa(Q,V,d,Q.index(q0),F)
275
276
277
class Dfa:
278
279
def __init__(self,Q,V,d,q0,F):
280
self.Q = Q
281
self.V = V
282
self.d = d
283
self.q0 = q0
284
self.F = F
285
286
def run(self, text):
287
#Checking if the input is in the current alphabet
288
if len(set(text) - self.V) != 0:
289
#Not all the characters are in the language
290
print('ERROR characters',(set(text)-self.V),'are not in the automata\'s alphabet')
291
exit(0)
292
293
#Running the automata
294
q = self.q0
295
for i in text:
296
#Check if transition exists
297
if q >= len(self.d):
298
print('Message NOT accepted, state has no transitions')
299
exit(0)
300
if i not in self.d[q].keys():
301
print('Message NOT accepted, state has no transitions with the character')
302
exit(0)
303
#Execute transition
304
q = self.d[q][i]
305
306
if q in self.F:
307
print('Message accepted!')
308
else:
309
print('Message NOT accepted, stopped in an unfinal state')
310
311
def write(self):
312
for i in range(len(self.Q)):
313
#Printing index, the delta fuunction for that transition and if it's final state
314
print(i,self.d[i],'F' if i in self.F else '')
315
316
#Preprocessing Functions
317
def preprocess(regex):
318
regex = clean_kleene(regex)
319
regex = regex.replace(' ','')
320
regex = '(' + regex + ')' + '#'
321
while '()' in regex:
322
regex = regex.replace('()','')
323
return regex
324
325
def clean_kleene(regex):
326
for i in range(0, len(regex) - 1):
327
while i < len(regex) - 1 and regex[i + 1] == regex[i] and regex[i] == '*':
328
regex = regex[:i] + regex[i + 1:]
329
return regex
330
331
def gen_alphabet(regex):
332
return set(regex) - set('()|*')
333
334
#Settings
335
DEBUG = False
336
use_lambda = False
337
lambda_symbol = '_'
338
alphabet = None
339
340
#Main
341
regex = '(0)*1(0|10)'
342
343
#Check
344
if not is_valid_regex(regex):
345
exit(0)
346
347
#Preprocess regex and generate the alphabet
348
p_regex = preprocess(regex)
349
alphabet = gen_alphabet(p_regex)
350
#add optional letters that don't appear in the expression
351
extra = ''
352
alphabet = alphabet.union(set(extra))
353
354
#Construct
355
tree = RegexTree(p_regex)
356
if DEBUG:
357
tree.write()
358
dfa = tree.toDfa()
359
360
#Test
361
message = '000110'
362
print('This is the regex : ' + regex)
363
print('This is the alphabet : ' + ''.join(sorted(alphabet)))
364
print('This is the automata : \n')
365
dfa.write()
366
print('\nTesting for : "'+message+'" : ')
367
dfa.run(message)
368