L3

Sign in or create your account | Project List | Help

L3 Commit Details

Date:2009-10-11 13:11:22 (10 months 26 days ago)
Author:Antoine Millet
Commit:5b19c4400ad7e2a1ab1964d83723edc43f37e008
Message:Ajout du programme sur les automates en langages formels

Files: LangagesFormels/Automates/automatelib.py (1 diff)
LangagesFormels/Automates/automates.py (1 diff)

Change Details

LangagesFormels/Automates/automatelib.py
1#!/usr/bin/env python
2#coding=utf8
3
4'''
5Lala
6
7'''
8
9from copy import copy
10
11class State(object):
12    ''' Représente un état de l'automate. '''
13
14    def __init__(self, name):
15        self.name = name
16
17    def __repr__(self):
18        return u'<State %s>' % self.name
19
20class Transition(object):
21    ''' Représente une transition générique. '''
22
23    __name__ = 'Transition'
24
25    def __init__(self, state_from, state_to):
26        self.state_from = state_from
27        self.state_to = state_to
28
29    def __repr__(self):
30        return u'<%s-- %s -> %s-->' % (self.__name__,
31                self.state_from, self.state_to)
32
33
34class NormalTransition(Transition):
35    ''' Représente une transition normale de l'automate. '''
36
37    __name__ = 'NormalTransition'
38
39    def __init__(self, state_from, state_to, labels):
40        Transition.__init__(self, state_from, state_to)
41        self.labels = labels
42
43
44class EpsilonTransition(Transition):
45    ''' Représente une epsilon-transition de l'automate. '''
46
47    __name__ = 'EpsilonTransition'
48
49
50class Automaton(object):
51    ''' Représente un automate à états finis de Mealy. '''
52
53    def __init__(self, alphabet, states=(), initial_states=(),
54                                    final_states=(), transitions=()):
55
56        # L'alphabet Σ de l'automate :
57        self.alphabet = set(alphabet)
58
59        # Ses états Q (objets State) :
60        self.states = set(states)
61
62        # Ses états finaux Q_f (objets State) :
63        self.final_states = set(final_states)
64
65        # Et ses états initiaux Q_0 (objets State) :
66        self.initial_states = set(initial_states)
67
68        # Et ses transition δ (objets Transision) :
69        self.transitions = set(transitions)
70
71    def add_state(self, st):
72        ''' Ajouter un état à l'automate. '''
73        self.states.add(st)
74
75    def add_initial_state(self, st):
76        ''' Ajouter un état initial à l'automate. '''
77        self.initial_states.add(st)
78
79    def add_final_state(self, st):
80        ''' Ajouter un état final à l'automate. '''
81        self.final_states.add(st)
82
83    def add_transition(self, tr):
84        ''' Ajouter une transition à l'automate. '''
85        self.transitions.add(tr)
86
87    def leave(self, st):
88        ''' Méthode qui retourne toutes les transisions qui
89            partent de l'état state '''
90        return set([t for t in self.transitions if t.state_from is st])
91
92    def get_epsilons_states(self, etr):
93        ''' Retourne tous les états joints à une série
94            d'epsilon-transitions dont la première est passée en
95            paramètres. '''
96
97        st_to_travel = set((etr.state_to,)) # Les états à parcourir
98        st_travelled = set() # Les états parcourus
99
100        while st_to_travel:
101            # On récupère un état que l'on a pas encore traité :
102            state = st_to_travel.pop()
103
104            # Par rapport à cet état, on récupère toutes les
105            # epsilons transition qui partent de cet état :
106            tr_to_travel = set([tr for tr in self.leave(state)
107                                    if type(tr) is EpsilonTransition])
108
109            # On ajoute tous les nouveaux états trouvés dans la liste
110            # des états que l'on doit encore parcourir :
111            st_to_travel |= (set([tr.state_to for tr in tr_to_travel])
112                                                        - st_travelled)
113
114            # Enfin, on ajout l'état que l'on vient de parcourir dans
115            # l'ensemble des états que nous avons parcouru :
116            st_travelled.add(state)
117
118        # On retourne tous les états qui ont été parcourus lors du
119        # passage par les epsilons transitions :
120        return st_travelled
121
122    def acceptance(self, word):
123        ''' Retourne True si l'automate accepte le mot "word" passé en
124            paramètre de la méthode. '''
125
126        # Ensemble des états ou l'automate pouvait se retrouver lors de
127        # la dernière itération sur le mot d'entrée. On initialise cet
128        # ensemble avec l'ensemble des états initiaux pour commencer :
129        states = self.initial_states
130
131        # Itération sur chaque lettre du mot d'entrée :
132        for f in word:
133
134            # Initialisation de l'ensemble qui contiendra les états
135            # parcourus pendant l'itération courante :
136            next_states = set()
137
138            # On parcourt les transitions de chaque état trouvé lors de
139            # la dernière itération :
140            for s in states:
141                for tr in self.leave(s):
142
143                    # La transition peut être utilisée pour l'itération
144                    # courante du mot :
145                    if type(tr) == NormalTransition and f in tr.labels:
146
147                        # Dans ce cas on ajoute l'état pointé par cette
148                        # transition dans l'ensemble des états à
149                        # parcourir à la prochaine itération :
150                        next_states.add(tr.state_to)
151
152                        # On ajoute aussi les états suivants pointés par
153                        # des epsilons-transitions :
154                        for etr in [t for t in self.leave(tr.state_to)
155                                       if type(t) is EpsilonTransition]:
156                            next_states |= self.get_epsilons_states(etr)
157                            # !!! En Python, | est l'opérateur d'union
158                            # des ensembles. (a |= b <-> a = a | b)
159
160            # A la fin de chaque itération, on remplace l'ensemble des
161            # états pour l'itération suivante :
162            states = next_states
163
164        # Quand on a terminé d'itérer sur le mot, on retourne True si
165        # des états sur lesquels nous sommes actuellement sont des états
166        # finaux :
167        return bool(states & self.final_states)
168        # !!! En Python, & est l'opérateur d'intersection des ensembles.
169
170    def empty_language(self, initial_states=None):
171        ''' Retourne True si l'automate possède un langage vide.
172
173            On parcourt l'automate en largeur pour vérifier que les
174            états finaux sont atteignables.
175
176            L'argument initial_states sert à indiquer des états initiaux
177            à utiliser pour la recherche au lieu des états initiaux de
178            l'automate (utilisé par la méthode infinite_language()). '''
179
180        fifo = [] # La file du parcours en largeur
181        st_travelled = set() # Ensemble des états déjà atteints
182
183        # On initialise la file avec les états initiaux
184        if initial_states:
185            fifo += initial_states
186        else:
187            fifo += self.initial_states
188
189        while fifo:
190            state = fifo.pop(0) # On prend un élément de la file
191
192            # Ajout de tous les états pointés par les transitions qui
193            # quittent l'état courant si ils ne sont pas déjà dans la
194            # liste des états à parcourir (pour éviter les boucles) :
195            fifo += [tr.state_to for tr in self.leave(state)
196                                    if tr.state_to not in st_travelled]
197
198            st_travelled.add(state)
199
200        # Retourne True si les états qui nous avons parcourus ne sont
201        # pas dans l'ensemble des états finaux :
202        return not bool(st_travelled & self.final_states)
203
204    def infinite_language(self, path=[], initials=[]):
205        ''' Retourne True si l'automate possède un langage infini.
206
207            La méthode recherche des boucles dans le parcours du
208            langage. Pour cela, elle parcourt le graphe en profondeur
209            et recherche un chemin dont un état a déjà été parcouru. Si
210            elle tombe sur un état qui pointe vers plusieurs autres,
211            elle s'appelle récursivement afin d'inspecter indépendamment
212            tous les chemins possibles.
213
214            La recherche s'arrete quand il n'y a plus d'états à
215            parcourir ou si une boucle a été trouvée. '''
216
217        loop = False # Drapeau "une boucle a été trouvée"
218
219        # Initialisation des états initiaux dans la pile selon que la
220        # méthode est été appelée récursivement ou non :
221        if initials:
222            lifo = initials
223        else:
224            lifo = list(self.initial_states)
225
226        while lifo and not loop:
227            state = lifo.pop() # On prend le dernier état (dépilement)
228            path.append(state) # On ajoute l'état au chemin parcouru
229
230            # Récupération des transitions sortantes :
231            outgoing = self.leave(state)
232
233            # Recherche d'une boucle avec les états pointés vers chaque
234            # transition sortante :
235            for t in outgoing:
236                if t.state_to in path: # Boucle !
237
238                    # Vérification qu'il existe un chemin entre la
239                    # boucle et un état sortant :
240                    if not self.empty_language([t.state_to]):
241                        loop = True
242
243            # Si aucune boucle n'est trouvée, on continue la recherche :
244            if not loop:
245
246                # Un seul état pointé, on continue l'exécution normale
247                if len(outgoing) == 1 and \
248                                list(outgoing)[0].state_to not in path:
249
250                    lifo.append(list(outgoing)[0].state_to)
251
252                # Plusieurs états pointés, on appelle récursivement
253                # la méthode pour inspecter indépendamment chaque
254                # chemin :
255                elif len(outgoing) > 1:
256                    for t in outgoing:
257                        loop |= self.infinite_language(
258                            path = copy(path),
259                            # !!! On utilise copy pour recopier la liste
260                            # et non passer une référence vers
261                            # celle-ci (chemins *indépendants*)
262                            initial_states = [t.state_to]
263                        )
264
265        return loop
266
267
268    def render(self, filename):
269        ''' Générer une représentation du graphe de l'automate avec
270            l'outil Graphviz (necessite d'installer pygraphviz). '''
271
272        try:
273            import pygraphviz as gv
274        except ImportError:
275            print ('Il faut installer pygraphviz pour utiliser les '
276                    'méthodes de visualisation de l\'automate : '
277                    'easy_install pygraphviz')
278        else:
279            graph = gv.AGraph(directed=True)
280            graph.add_nodes_from([n.name for n in self.states])
281            graph.graph_attr['rankdir'] = 'LR'
282            for st in self.final_states:
283                n = graph.get_node(st.name)
284                n.attr['shape'] = 'doublecircle'
285            for st in self.initial_states:
286                n = graph.get_node(st.name)
287                n.attr['style'] = 'filled'
288                n.attr['fillcolor'] = '#DDDDDD'
289            for tr in self.transitions:
290                if type(tr) is NormalTransition:
291                    graph.add_edge(
292                        tr.state_from.name,
293                        tr.state_to.name,
294                        label=', '.join(tr.labels)
295                    )
296                elif type(tr) is EpsilonTransition:
297                    graph.add_edge(tr.state_from.name, tr.state_to.name)
298            graph.draw(filename, prog='dot')
299
300    def __repr__(self):
301        return u'<Automaton with %s states and %s transition>' % (
302                len(self.states),
303                len(self.transitions)
304            )
LangagesFormels/Automates/automates.py
1#!/usr/bin/env python
2#coding=utf8
3
4from automatelib import State, NormalTransition, Automaton, EpsilonTransition
5
6
7if __name__ == '__main__':
8    # Automate de test acceptant l'alphabet {a, b, c} et dont les mots
9    # commencent et finissent forcément par a.
10    #~ s1 = State('1')
11    #~ s2 = State('2')
12    #~ s3 = State('3')
13    #~ s4 = State('4')
14    #~ s5 = State('5')
15    #~ s6 = State('6')
16    #~ s7 = State('7')
17    #~ s8 = State('8')
18    #~ s9 = State('9')
19    #~ s10 = State('10')
20    #~ a = Automaton('abc',
21        #~ states=(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10),
22        #~ initial_states=(s1, s2, s3),
23        #~ final_states=(s7, s8, s9)
24    #~ )
25    #~
26    #~ a.add_transition(Transition(s1, s1, 'abc'))
27    #~ a.add_transition(Transition(s1, s4, 'a'))
28    #~ a.add_transition(Transition(s4, s4, 'abc'))
29    #~ a.add_transition(Transition(s4, s7, 'a'))
30    #~
31    #~ a.add_transition(Transition(s2, s2, 'abc'))
32    #~ a.add_transition(Transition(s2, s5, 'b'))
33    #~ a.add_transition(Transition(s5, s5, 'abc'))
34    #~ a.add_transition(Transition(s5, s8, 'b'))
35#~
36    #~ a.add_transition(Transition(s3, s3, 'abc'))
37    #~ a.add_transition(Transition(s3, s6, 'c'))
38    #~ a.add_transition(Transition(s6, s6, 'abc'))
39    #~ a.add_transition(Transition(s6, s9, 'c'))
40#~
41    #~ a.add_transition(EpsilonTransition(s1, s10))
42    #~ a.add_transition(EpsilonTransition(s10, s7))
43
44    #~ a = State('a')
45    #~ a2 = State('a2')
46    #~
47    #~ b1 = State('b1')
48    #~ b2 = State('b2')
49    #~ b3 = State('b3')
50    #~
51    #~ found = State('found')
52    #~ deuxieme = State('deuxieme')
53    #~
54    #~ da1 = State('da1')
55    #~ da11 = State('da11')
56    #~ da12 = State('da12')
57    #~ da13 = State('da13')
58    #~
59    #~ done = State('done')
60    #~ anothera = State('anothera')
61    #~
62    #~ automaton = Automaton('ab',
63        #~ states = (a, a2, b1, b2, b3, found, deuxieme, da1, da11, da12, da13, done, anothera),
64        #~ initial_states = (a,),
65        #~ final_states = (found, )
66    #~ )
67    #~ automaton.add_transition(NormalTransition(a, a, 'ab'))
68#~
69    #~ automaton.add_transition(NormalTransition(a, b1, 'a'))
70    #~ automaton.add_transition(NormalTransition(b1, b2, 'b'))
71    #~ automaton.add_transition(NormalTransition(b2, b3, 'b'))
72    #~ automaton.add_transition(NormalTransition(b3, a2, 'a'))
73    #~ automaton.add_transition(NormalTransition(a2, found, 'a'))
74#~
75    #~ automaton.add_transition(NormalTransition(found, found, 'b'))
76    #~ automaton.add_transition(EpsilonTransition(found, deuxieme))
77#~
78    #~ automaton.add_transition(NormalTransition(deuxieme, da1, 'a'))
79    #~ automaton.add_transition(EpsilonTransition(da1, da11))
80    #~ automaton.add_transition(EpsilonTransition(da11, da12))
81    #~ automaton.add_transition(EpsilonTransition(da12, da13))
82    #~ automaton.add_transition(EpsilonTransition(da1, da13))
83    #~ automaton.add_transition(NormalTransition(da13, done, 'a'))
84    #~ automaton.add_transition(NormalTransition(da12, found, 'a'))
85#~
86    #~ automaton.add_transition(NormalTransition(done, anothera, 'a'))
87#~
88    #~ automaton.add_transition(EpsilonTransition(anothera, a2))
89    #~ automaton.add_transition(NormalTransition(anothera, da12, 'a'))
90    #~ print automaton.il()
91    s1 = State('1')
92    s2 = State('2')
93    s3 = State('3')
94    s4 = State('4')
95    a = Automaton('a',
96        states=(s1, s2, s3, s4),
97        initial_states=(s1,),
98        final_states=(s2,)
99    )
100
101    a.add_transition(NormalTransition(s1, s2, 'a'))
102    a.add_transition(NormalTransition(s1, s3, 'a'))
103    a.add_transition(NormalTransition(s3, s4, 'a'))
104    a.add_transition(NormalTransition(s4, s3, 'a'))
105    #a.add_transition(NormalTransition(s4, s1, 'a'))
106    print a.infinite_language()

Archive Download the corresponding diff file

Branches:
master