-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnfa-builder.py
More file actions
435 lines (412 loc) · 16.3 KB
/
nfa-builder.py
File metadata and controls
435 lines (412 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
import itertools
import codecs
from utility import *
alphabet = []
cl = {}
proposition_combination = []
last_pointer = []
def delta(state, action_effect):
if AND_STATE_SEPARATOR in state:
result_set = []
split = state.split(AND_STATE_SEPARATOR)
# This portion of code is used in order to implement the 'and' between n delta functions
for elem in split:
d = delta(elem, action_effect)
if d == FALSE:
return FALSE
if d != TRUE:
append = True
for r in result_set:
if d == r or d + AND_STATE_SEPARATOR in r:
append = False
if append:
result_set.append(d)
if len(result_set) < 1:
return TRUE
else:
# Auxiliary method for computing the and between delta, avoiding duplicates
print("Problem in compute_tf")
return compute_tf_and(result_set)
formula_type = cl[state]
if formula_type == LIT:
split = state.replace(" ", ",").replace("not,", "not ").split()
if len(split) > 1: # negative literal
if split[1] not in action_effect:
return TRUE
else:
return FALSE
else:
if state in action_effect:
return TRUE
else:
return FALSE
elif formula_type == EVENTUALLY:
alpha = find_alpha(formula_type, state)
d1 = delta(alpha, action_effect)
if d1 == TRUE:
return TRUE
else:
beta = NEXT + " (" + state + ")"
d2 = delta(beta, action_effect)
if d2 == TRUE:
return TRUE
if d1 == FALSE and d2 == FALSE:
return FALSE
if d1 == FALSE:
return d2
if d2 == FALSE:
return d1
return d1 + OR_STATE_SEPARATOR + d2
elif formula_type == NEXT:
if LAST in action_effect:
return FALSE
else:
return find_alpha(formula_type, state)
elif formula_type == WEAK_NEXT:
if LAST in action_effect:
return TRUE
else:
return find_alpha(formula_type, state)
elif formula_type == GLOBALLY:
alpha = find_alpha(formula_type, state)
d1 = delta(alpha, action_effect)
if d1 == FALSE:
return FALSE
d2 = delta(WEAK_NEXT + " (" + state + ")", action_effect)
if d2 == FALSE:
return FALSE
if d1 == TRUE and d2 == TRUE:
return TRUE
if d1 == TRUE:
return d2
if d2 == TRUE:
return d1
if OR_STATE_SEPARATOR in d1:
split = d1.split(OR_STATE_SEPARATOR)
return_value = split[0] + AND_STATE_SEPARATOR + d2
for state in split[1:]:
return_value += OR_STATE_SEPARATOR + state + AND_STATE_SEPARATOR + d2
return return_value
else:
return d1 + AND_STATE_SEPARATOR + d2
elif formula_type == UNTIL:
alpha, beta = find_alpha_beta(state, formula_type)
d1 = delta(beta, action_effect)
if d1 == TRUE:
return TRUE
d2 = delta(alpha, action_effect)
if d2 == FALSE:
return FALSE
d3 = delta(NEXT + " (" + state + ")", action_effect)
if d3 == FALSE:
return FALSE
if d2 == TRUE and d3 == TRUE:
return TRUE
if d1 == FALSE:
if d2 == TRUE:
return d3
if d3 == TRUE:
return d2
else:
return d2 + AND_STATE_SEPARATOR + d3
if d2 == TRUE:
return d1 + OR_STATE_SEPARATOR + d3
if d3 == TRUE:
return d1 + OR_STATE_SEPARATOR + d2
return d1 + OR_STATE_SEPARATOR + d2 + AND_STATE_SEPARATOR + d3
elif formula_type == RELEASE:
alpha, beta = find_alpha_beta(state, formula_type)
d1 = delta(beta, action_effect)
if d1 == FALSE:
return FALSE
d2 = delta(alpha, action_effect)
d3 = delta(WEAK_NEXT + " (" + state + ")", action_effect)
if d2 == TRUE and d1 == TRUE:
return TRUE
if d3 == TRUE and d1 == TRUE:
return TRUE
if d2 == FALSE and d3 == FALSE:
return FALSE
if d1 == TRUE:
if d2 == FALSE:
return d3
if d3 == FALSE:
return d2
else:
if d2 == FALSE:
return d1 + AND_STATE_SEPARATOR + d3
if d3 == FALSE:
return d1 + AND_STATE_SEPARATOR + d2
return d1 + AND_STATE_SEPARATOR + d2 + OR_STATE_SEPARATOR + d1 + AND_STATE_SEPARATOR + d3
elif formula_type == AND:
alpha, beta = find_alpha_beta(state, formula_type)
d1 = delta(alpha, action_effect)
if d1 == FALSE:
return FALSE
d2 = delta(beta, action_effect)
if d2 == FALSE:
return FALSE
if d1 == TRUE and d2 == TRUE:
return TRUE
if d1 == TRUE:
return d2
if d2 == TRUE:
return d1
if d1 == d2:
return d1
return d1 + AND_STATE_SEPARATOR + d2
elif formula_type == OR:
alpha, beta = find_alpha_beta(state, formula_type)
d1 = delta(alpha, action_effect)
if d1 == TRUE:
return TRUE
d2 = delta(beta, action_effect)
if d2 == TRUE:
return TRUE
if d1 == FALSE and d2 == FALSE:
return FALSE
if d1 == FALSE:
return d2
if d2 == FALSE:
return d1
if d1 == d2:
return d1
return d1 + OR_STATE_SEPARATOR + d2
# Used for obtaining all the possible combinations of fluents
def create_proposition_combination(result):
for i in range(len(alphabet)+1):
comb = itertools.combinations(alphabet, i)
for tup in comb:
result.append(tup)
def sort_and_state(elem):
and_state_list = sorted(elem.split(AND_STATE_SEPARATOR))
and_state = ""
for sub_elem in and_state_list:
if len(and_state) < 1:
and_state = sub_elem
else:
and_state += AND_STATE_SEPARATOR + sub_elem
return and_state
# This is the real nfa builder method: for each fluents combination, it applies the delta for each state until
# no more states are added. It returns the set of states s and the transition function
def ltlf_2_nfa(propositions, nnf):
s0 = nnf
s_before = set([])
s = {s0}
transition_function = {}
first = True
while s_before != s: # Until we stop adding states
if first:
diff = s.copy()
first = False
else:
diff = s.difference(s_before)
s_before = s.copy()
for state in diff:
if state != TRUE and state != FALSE and state != ENDED:
for prop in propositions:
new_state = delta(state, prop)
tup = (state, prop)
if OR_STATE_SEPARATOR in new_state: # is an or of states
split = new_state.split(OR_STATE_SEPARATOR)
new_state = ""
for elem in split:
if AND_STATE_SEPARATOR in elem:
and_state = sort_and_state(elem)
if len(new_state) < 1:
new_state = and_state
else:
new_state += OR_STATE_SEPARATOR + and_state
if and_state not in s:
s.add(and_state)
else:
if len(new_state) < 1:
new_state = elem
else:
new_state += OR_STATE_SEPARATOR + elem
if elem not in s:
s.add(elem)
elif AND_STATE_SEPARATOR in new_state:
new_state = sort_and_state(new_state)
if new_state not in s:
s.add(new_state)
elif new_state not in s:
s.add(new_state)
transition_function[tup] = new_state
return s, transition_function
# This method is used for removing the special proposition 'last' from the sequence of fluents
def remove_last(transition_function):
exist_ended_state = False
exist_false_state = False
exist_true_state = False
new_transition_function = {}
for key in transition_function.keys():
fluents = key[1]
if LAST not in fluents:
if transition_function[key] == FALSE:
exist_false_state = True
elif transition_function[key] == TRUE:
exist_true_state = True
# We copy the transition function value but we may have already the ended state, so if we have it
# we add the new_state with the OR_STATE_SEPARATOR, otherwise not
if key in new_transition_function.keys() and transition_function[key] != TRUE:
new_transition_function[key] = new_transition_function[key] + \
OR_STATE_SEPARATOR + transition_function[key]
exist_ended_state = True
else:
new_transition_function[key] = transition_function[key]
elif transition_function[key] == TRUE:
# Here we have to add the ended state
new_tuple = ()
state = key[0]
for elem in fluents:
if elem != LAST:
new_tuple += (elem,)
new_key = (state, new_tuple)
if transition_function[new_key] == TRUE:
new_transition_function[new_key] = TRUE
elif new_key in new_transition_function.keys():
# same stuff as above
exist_ended_state = True
new_transition_function[new_key] = new_transition_function[new_key] + OR_STATE_SEPARATOR + ENDED
else:
new_transition_function[new_key] = ENDED
exist_ended_state = True
return new_transition_function, exist_ended_state, exist_false_state, exist_true_state
def print_nfa(s0, s, transition_function):
print("\n\n----------------------------------------------\n")
print("Alphabet: " + str(alphabet) + "\n")
print("States: " + str(s) + "\n")
print("Initial state: " + s0 + "\n")
f = set([])
if TRUE in s:
f.add(TRUE)
if ENDED in s:
f.add(ENDED)
print("Final states: " + str(f) + "\n")
print("Transition function:")
for key in transition_function.keys():
state = key[0]
fluents = key[1]
print("\tState: " + state)
fluents_to_print = ""
for elem in fluents:
if len(elem) > 1:
clause = "(" + str(elem[0])
for e in elem[1:]:
clause += " and " + str(e)
if fluents_to_print == "":
fluents_to_print += clause + ")"
else:
fluents_to_print += " or " + clause + ")"
else:
if fluents_to_print == "":
fluents_to_print += elem[0]
else:
fluents_to_print += " or " + elem[0]
print("\tFluents: " + fluents_to_print)
print("\tNew states: {" + transition_function[key] + "}\n")
print("\n----------------------------------------------\n")
def run_nfa(sequence, s0, transition_function):
current_state = s0
for elem in sequence:
if OR_STATE_SEPARATOR in current_state:
split = current_state.split(OR_STATE_SEPARATOR)
new_state = ""
new_states = set([])
for c_state in split:
if c_state != ENDED:
n_state = transition_function[(c_state, elem)]
if n_state == TRUE:
new_state = TRUE
break
if n_state != FALSE:
if OR_STATE_SEPARATOR in n_state:
for ns in n_state.split(OR_STATE_SEPARATOR):
if ns not in new_states:
new_states.add(ns)
if len(new_states) == 1:
new_state = ns
else:
new_state += OR_STATE_SEPARATOR + ns
elif n_state not in new_states:
new_states.add(n_state)
if len(new_states) == 1:
new_state = n_state
else:
new_state += OR_STATE_SEPARATOR + n_state
if new_state == "":
new_state = FALSE
else:
new_state = transition_function[(current_state, elem)]
print("States: {" + current_state + "}\nFluents: " + str(elem) + "\nNew states: {" + new_state + "}\n")
if new_state == TRUE:
return True
if new_state == FALSE:
return False
current_state = new_state
if ENDED in current_state:
return True
return False
def main():
if len(sys.argv) < 2:
print("Correct usage: python .\\nfa-builder.py alphabet_file")
exit(-1)
alphabet_file = sys.argv[1]
ltlf_formula = input("Insert the LTLf formula\n")
nnf = convert_to_nnf(ltlf_formula)
# This method is used for building the dictionary of subformulas (i.e. the cl of the AFW)
# The dictionary cl will be used in the delta function for understanding what kind of formula is it and what
# recursive rule it has to follow
sigma(nnf, cl)
with codecs.open(alphabet_file, 'r') as file_handle:
for line in file_handle:
line = line.replace("\n", "")
alphabet.append(line)
alphabet.append(LAST)
create_proposition_combination(proposition_combination)
s, transition_function = ltlf_2_nfa(proposition_combination, nnf)
new_transition_function, exist_ended_state, exist_false_state, exist_true_state = remove_last(transition_function)
prop_without_last = []
for prop in proposition_combination:
if LAST not in prop:
prop_without_last.append(prop)
alphabet.remove(LAST)
compact_transition_function = compact_fluents_notation(new_transition_function, s,
alphabet, prop_without_last)
if not exist_true_state and TRUE in s:
s.remove(TRUE)
if exist_ended_state:
s.add(ENDED)
if not exist_false_state and FALSE in s:
s.remove(FALSE)
print_nfa(nnf, s, compact_transition_function)
done = False
while not done:
response = input("Do you want to provide a sequence of fluents for simulating a run? (y, n)\n")
while response not in ["y", "n"]:
print("Not valid response. Type 'y' if you want to continue or 'n' if you want to exit")
response = input()
if response == "y":
sequence = []
sequence_file_name = input("Insert the name of the file containing the sequence of fluents\n")
with codecs.open(sequence_file_name, 'r') as file_handle:
for line in file_handle:
line = line.replace("\n", "")
if line == "":
sequence.append(())
else:
split = line.split(",")
tuple_res = ()
for elem in split:
tuple_res += (elem,)
sequence.append(tuple_res)
print("\n-----------------------------------------------------------\n")
final_state_reached = run_nfa(sequence, nnf, new_transition_function)
if final_state_reached:
print("The trace satisfies the LTLf formula\n")
else:
print("The trace does not satisfy the LTLf formula\n")
else:
done = True
main()