Ticket #15078: trac_15078_fsm_automata_transducers.2.patch

File trac_15078_fsm_automata_transducers.2.patch, 138.4 KB (added by dkrenn, 6 years ago)
  • doc/en/reference/combinat/index.rst

    # HG changeset patch
    # User Daniel Krenn <math+sage@danielkrenn.at>
    # Date 1378196266 -7200
    # Node ID bfde6ebfd6ca0551d164802af4fc9b0e880d1e7f
    # Parent  8c4989a7e8d20a16ad1c464620ee616c4cde719a
    Trac 15078: finite state machines, automata, transducers
    
    diff --git a/doc/en/reference/combinat/index.rst b/doc/en/reference/combinat/index.rst
    a b  
    8484   sage/combinat/misc
    8585   sage/combinat/combinatorial_map
    8686
     87   sage/combinat/finite_state_machine
    8788
    8889.. include:: ../footer.txt
  • sage/combinat/all.py

    diff --git a/sage/combinat/all.py b/sage/combinat/all.py
    a b  
    143143# Gelfand-Tsetlin patterns
    144144from gelfand_tsetlin_patterns import GelfandTsetlinPattern, GelfandTsetlinPatterns
    145145
     146# Finite State Machines (Automaton, Transducer)
     147from finite_state_machine import Automaton, Transducer, FiniteStateMachine, FSMState, FSMTransition, FSMProcessIterator
  • new file sage/combinat/finite_state_machine.py

    diff --git a/sage/combinat/finite_state_machine.py b/sage/combinat/finite_state_machine.py
    new file mode 100644
    - +  
     1# -*- coding: utf-8 -*-
     2"""
     3Finite State Machines, Automata, Transducers
     4
     5This module adds support for finite state machines, automata and
     6transducers. See class :class:`FiniteStateMachine` and the examples
     7below for details creating one.
     8
     9Examples
     10========
     11
     12
     13A simple finite state machine
     14-----------------------------
     15
     16We can easily create a finite state machine by
     17
     18::
     19
     20    sage: fsm = FiniteStateMachine()
     21    sage: fsm
     22    finite state machine with 0 states
     23
     24By default this is the empty finite state machine, so not very
     25interesting. Let's create some states and transitions::
     26
     27    sage: day = FSMState('day')
     28    sage: night = FSMState('night')
     29    sage: sunrise = FSMTransition(night, day)
     30    sage: sunset = FSMTransition(day, night)
     31
     32And now let's add those states and transitions to our finite state machine::
     33
     34    sage: fsm.add_transition(sunrise)
     35    Transition from State 'night' to State 'day': -|-
     36    sage: fsm.add_transition(sunset)
     37    Transition from State 'day' to State 'night': -|-
     38
     39Note that the states are added automatically, since they are present
     40in the transitions. We could add the states manually by
     41
     42::
     43
     44    sage: fsm.add_state(day)
     45    State 'day'
     46    sage: fsm.add_state(night)
     47    State 'night'
     48
     49Anyhow, we got the following finite state machine::
     50
     51    sage: fsm
     52    finite state machine with 2 states
     53
     54We can also visualize it as a graph by
     55
     56::
     57
     58    sage: fsm.graph()
     59    Digraph on 2 vertices
     60
     61Alternatively, we could have created the finite state machine above
     62simply by
     63
     64::
     65
     66    sage: FiniteStateMachine([('night', 'day'), ('day', 'night')])
     67    finite state machine with 2 states
     68
     69
     70A simple Automaton (recognizing NAFs)
     71---------------------------------------
     72
     73We want to build an automaton which recognizes non-adjacent forms
     74(NAFs), i.e., sequences which have no adjacent non-zeros.
     75We use `0`, `1`, and `-1` as digits::
     76
     77    sage: NAF = Automaton(
     78    ....:     {'A': [('A', 0), ('B', 1), ('B', -1)], 'B': [('A', 0)]})
     79    sage: NAF.state('A').is_initial = True
     80    sage: NAF.state('A').is_final = True
     81    sage: NAF.state('B').is_final = True
     82    sage: NAF
     83    finite state machine with 2 states
     84
     85Of course, we could have specified the initial and final states
     86directly in the definition of ``NAF`` by ``initial_states=['A']`` and
     87``final_states=['A', 'B']``.
     88
     89So let's test the automaton with some input::
     90
     91    sage: NAF([0])[0]
     92    True
     93    sage: NAF([0, 1])[0]
     94    True
     95    sage: NAF([1, -1])[0]
     96    False
     97    sage: NAF([0, -1, 0, 1])[0]
     98    True
     99    sage: NAF([0, -1, -1, -1, 0])[0]
     100    False
     101    sage: NAF([-1, 0, 0, 1, 1])[0]
     102    False
     103
     104Alternatively, we could call that by
     105
     106::
     107
     108    sage: NAF.process([-1, 0, 0, 1, 1])[0]
     109    False
     110
     111A simple transducer (binary inverter)
     112-------------------------------------
     113
     114Let's build a simple transducer, which rewrites a binary word by
     115iverting each bit::
     116
     117    sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     118    ....:     initial_states=['A'], final_states=['A'])
     119
     120We can look at the states and transitions::
     121
     122    sage: inverter.states()
     123    [State 'A']
     124    sage: for t in inverter.transitions():
     125    ....:     print t
     126    Transition from State 'A' to State 'A': 0|1
     127    Transition from State 'A' to State 'A': 1|0
     128
     129Now we apply a word to it and see what the transducer does::
     130
     131    sage: inverter([0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1])
     132    (True, State 'A', [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0])
     133
     134``True`` means, that we landed in a final state, that state is labeled
     135``'A'``, and we also got an output.
     136
     137
     138A transducer which performs division by `3` in binary
     139-----------------------------------------------------
     140
     141Now we build a transducer, which divides a binary number by 3.
     142The labels of the states are the remainder of the division.
     143The transition function is
     144
     145::
     146
     147    sage: def f(state_from, read):
     148    ....:     if state_from + read <= 1:
     149    ....:         state_to = 2*state_from + read
     150    ....:         write = 0
     151    ....:     else:
     152    ....:         state_to = 2*state_from + read - 3
     153    ....:         write = 1
     154    ....:     return (state_to, write)
     155
     156We get the transducer with
     157
     158::
     159
     160    sage: D = Transducer(f, initial_states=[0], final_states=[0],
     161    ....:                input_alphabet=[0, 1])
     162
     163Now we want to divide 13 by 3::
     164
     165    sage: D([1, 1, 0, 1])
     166    (False, State 1, [0, 1, 0, 0])
     167
     168So we have 13 : 3 = 4 and the reminder is 1. ``False`` means 13 is not
     169divisible by 3.
     170
     171
     172Using the hook-functions
     173------------------------
     174
     175Let's use the previous example "divison by `3`" to demonstrate the
     176optional state and transition parameters ``hook``.
     177
     178First, we define, what those functions should do. In our case, this is
     179just saying in which state we are and which transition we take
     180
     181::
     182
     183    sage: def state_hook(state, process):
     184    ....:     print "We are now in State %s." % (state.label(),)
     185    sage: from sage.combinat.finite_state_machine import FSMWordSymbol
     186    sage: def transition_hook(transition, process):
     187    ....:     print ("Currently we go from %s to %s, "
     188    ....:            "reading %s and writing %s." % (
     189    ....:                transition.from_state, transition.to_state,
     190    ....:                FSMWordSymbol(transition.word_in),
     191    ....:                FSMWordSymbol(transition.word_out)))
     192
     193Now, let's add these hook-functions to the existing transducer::
     194
     195    sage: for s in D.iter_states():
     196    ....:     s.hook = state_hook
     197    sage: for t in D.iter_transitions():
     198    ....:     t.hook = transition_hook
     199
     200Rerunning the process again now gives the following output::
     201
     202    sage: D.process([1, 1, 0, 1])
     203    We are now in State 0.
     204    Currently we go from State 0 to State 1, reading 1 and writing 0.
     205    We are now in State 1.
     206    Currently we go from State 1 to State 0, reading 1 and writing 1.
     207    We are now in State 0.
     208    Currently we go from State 0 to State 0, reading 0 and writing 0.
     209    We are now in State 0.
     210    Currently we go from State 0 to State 1, reading 1 and writing 0.
     211    We are now in State 1.
     212    (False, State 1, [0, 1, 0, 0])
     213
     214The example above just explains the basic idea of using
     215hook-functions. In the following, we will use those hooks more seriously.
     216
     217
     218Detecting sequences with same number of `0` and `1`
     219---------------------------------------------------
     220
     221Suppose we have a binary input and want to accept all sequences with
     222the same number of `0` and `1`. This cannot be done with a finite
     223automaton. Anyhow, we can make usage of the hook functions to extend
     224our finite automaton by a counter::
     225
     226    sage: C = Automaton()
     227    sage: def update_counter(state, process):
     228    ....:     l = process.read_letter()
     229    ....:     process.fsm.counter += 1 if l == 1 else -1
     230    ....:     if process.fsm.counter > 0:
     231    ....:         next_state = 'positive'
     232    ....:     elif process.fsm.counter < 0:
     233    ....:         next_state = 'negative'
     234    ....:     else:
     235    ....:         next_state = 'zero'
     236    ....:     return FSMTransition(state, process.fsm.state(next_state),
     237    ....:                          l, process.fsm.counter)
     238    sage: C.add_state(FSMState('zero', hook=update_counter,
     239    ....:             is_initial=True, is_final=True))
     240    State 'zero'
     241    sage: C.add_state(FSMState('positive', hook=update_counter))
     242    State 'positive'
     243    sage: C.add_state(FSMState('negative', hook=update_counter))
     244    State 'negative'
     245
     246Now, let's input some sequence::
     247
     248    sage: C.counter = 0; C([1, 1, 1, 1, 0, 0])
     249    (False, State 'positive', [1, 2, 3, 4, 3, 2])
     250
     251The result is False, since there are four `1` but only two `0`. We
     252land in the state ``positive`` and we can also see the values of the
     253counter in each step.
     254
     255Let's try some other examples::
     256
     257    sage: C.counter = 0; C([1, 1, 0, 0])
     258    (True, State 'zero', [1, 2, 1, 0])
     259    sage: C.counter = 0; C([0, 1, 0, 0])
     260    (False, State 'negative', [-1, 0, -1, -2])
     261
     262
     263AUTHORS:
     264
     265- Daniel Krenn (2012-03-27): initial version
     266- Clemens Heuberger (2012-04-05): initial version
     267- Sara Kropf (2012-04-17): initial version
     268- Clemens Heuberger (2013-08-21): release candidate for Sage patch
     269- Daniel Krenn (2013-08-21): release candidate for Sage patch
     270- Sara Kropf (2013-08-21): release candidate for Sage patch
     271
     272
     273ACKNOWLEDGEMENT:
     274
     275- Daniel Krenn, Clemens Heuberger and Sara Kropf are supported by the
     276  Austrian Science Fund (FWF): P 24644-N26.
     277
     278"""
     279
     280#*****************************************************************************
     281#  Copyright (C) 2012, 2013 Daniel Krenn <math+sage@danielkrenn.at>
     282#                2012, 2013 Clemens Heuberger <clemens.heuberger@aau.at>
     283#                2012, 2013 Sara Kropf <sara.kropf@aau.at>
     284#
     285#  Distributed under the terms of the GNU General Public License (GPL)
     286#  as published by the Free Software Foundation; either version 2 of
     287#  the License, or (at your option) any later version.
     288#                http://www.gnu.org/licenses/
     289#*****************************************************************************
     290
     291from sage.structure.sage_object import SageObject
     292from sage.graphs.digraph import DiGraph
     293from sage.symbolic.ring import SR
     294from sage.matrix.constructor import matrix
     295from sage.rings.integer_ring import ZZ
     296from sage.calculus.var import var
     297from sage.misc.latex import latex
     298from sage.functions.trig import cos, sin, atan2
     299from sage.symbolic.constants import pi
     300
     301from copy import copy
     302from copy import deepcopy
     303
     304import itertools
     305
     306#*****************************************************************************
     307
     308def conditional_iterator(iterator, condition):
     309    """
     310    Returns a conditional iterator.
     311
     312    Input
     313    - ``iterator``
     314    - ``condition``: function
     315
     316    EXAMPLES::
     317
     318        sage: L = range(10)
     319        sage: from sage.combinat.finite_state_machine import conditional_iterator
     320        sage: it = conditional_iterator(L, lambda x: mod(x, 3) != 0)
     321        sage: [i for i in it]
     322        [1, 2, 4, 5, 7, 8]
     323    """
     324    for i in iterator:
     325        if condition(i):
     326            yield i
     327
     328#*****************************************************************************
     329
     330FSMEmptyWordSymbol = '-'
     331
     332def FSMLetterSymbol(letter):
     333    """
     334    If ``letter`` is ``None`` the symbol for the empty word
     335    ``FSMEmptyWordSymbol`` is returned, otherwise the string associated
     336    to the letter.
     337
     338    EXAMPLES::
     339
     340        sage: from sage.combinat.finite_state_machine import FSMLetterSymbol
     341        sage: FSMLetterSymbol(0)
     342        '0'
     343        sage: FSMLetterSymbol(None)
     344        '-'
     345    """
     346    return FSMEmptyWordSymbol if letter is None else repr(letter)
     347
     348
     349def FSMWordSymbol(word):
     350    """
     351    Returns a string of ``word``. It may returns the symbol of the
     352    empty word ``FSMEmptyWordSymbol``.
     353
     354    EXAMPLES::
     355
     356        sage: from sage.combinat.finite_state_machine import FSMWordSymbol
     357        sage: FSMWordSymbol([0, 1, 1])
     358        '0,1,1'
     359    """
     360    if not isinstance(word, list):
     361        return FSMLetterSymbol(word)
     362    if len(word) == 0:
     363        return FSMEmptyWordSymbol
     364    s = ''
     365    for letter in word:
     366        s += (',' if len(s) > 0 else '') + FSMLetterSymbol(letter)
     367    return s
     368
     369
     370#*****************************************************************************
     371
     372
     373def Automaton(*args, **kwargs):
     374    """
     375    A FiniteStateMachine with ``mode='automaton'``
     376    See class :class:`FiniteStateMachine` for more information.
     377
     378    TESTS::
     379
     380        sage: Automaton()
     381        finite state machine with 0 states
     382    """
     383    return FiniteStateMachine(mode='automaton', *args, **kwargs)
     384
     385
     386def Transducer(*args, **kwargs):
     387    """
     388    A FiniteStateMachine with ``mode='transducer'``
     389    See class :class:`FiniteStateMachine` for more information.
     390
     391    TESTS::
     392
     393        sage: Transducer()
     394        finite state machine with 0 states
     395    """
     396    return FiniteStateMachine(mode='transducer', *args, **kwargs)
     397
     398
     399#*****************************************************************************
     400
     401
     402def is_FSMState(S):
     403    """
     404    Tests whether or not ``S`` inherits from :class:`FSMState`.
     405
     406    TESTS::
     407
     408        sage: from sage.combinat.finite_state_machine import is_FSMState
     409        sage: is_FSMState(FSMState('A'))
     410        True
     411    """
     412    return isinstance(S, FSMState)
     413
     414
     415class FSMState(SageObject):
     416    """
     417    Class for a state of a finite state machine.
     418
     419    EXAMPLES::
     420
     421        sage: A = FSMState('state 1', word_out=0, is_initial=True)
     422        sage: A
     423        State 'state 1'
     424        sage: A.label()
     425        'state 1'
     426        sage: B = FSMState('state 2')
     427        sage: A == B
     428        False
     429
     430    """
     431    def __init__(self, label, word_out=None,
     432                 is_initial=False, is_final=False,
     433                 hook=None):
     434        """
     435        See :class:`FSMState` for more information.
     436
     437        EXAMPLES::
     438
     439            sage: FSMState('final', is_final=True)
     440            State 'final'
     441        """
     442        if label is None or label == "":
     443            raise ValueError, "You have to specify a label for the state."
     444        self._label_ = label
     445
     446        if isinstance(word_out, list):
     447            self.word_out = word_out
     448        elif word_out is not None:
     449            self.word_out = [word_out]
     450        else:
     451            self.word_out = []
     452
     453        self.is_initial = is_initial
     454        self.is_final = is_final
     455        if hook is not None:
     456            if hasattr(hook, '__call__'):
     457                self.hook = hook
     458            else:
     459                raise TypeError, 'Wrong argument for hook.'
     460
     461
     462    def label(self):
     463        """
     464        Returns the label of the state.
     465
     466        EXAMPLES::
     467
     468            sage: A = FSMState('state')
     469            sage: A.label()
     470            'state'
     471        """
     472        return self._label_
     473
     474
     475    def __copy__(self):
     476        """
     477        Returns a (shallow) copy of the state.
     478
     479        EXAMPLES::
     480
     481            sage: A = FSMState('A')
     482            sage: copy(A)
     483            State 'A'
     484        """
     485        new = FSMState(self.label(), self.word_out,
     486                       self.is_initial, self.is_final)
     487        if hasattr(self, 'hook'):
     488            new.hook = self.hook
     489        return new
     490
     491
     492    copy = __copy__
     493
     494
     495    def __deepcopy__(self, memo):
     496        """
     497        Returns a deep copy of the state.
     498
     499        EXAMPLES::
     500
     501            sage: A = FSMState('A')
     502            sage: deepcopy(A)
     503            State 'A'
     504        """
     505        try:
     506            label = self._deepcopy_relabel_
     507        except AttributeError:
     508            label = self.label()
     509        new = FSMState(deepcopy(label, memo), deepcopy(self.word_out, memo),
     510                       self.is_initial, self.is_final)
     511        if hasattr(self, 'hook'):
     512            new.hook = deepcopy(self.hook, memo)
     513        return new
     514
     515
     516    def deepcopy(self, memo=None, label=None):
     517        """
     518        Returns a deep copy of the state.
     519
     520        EXAMPLES::
     521
     522            sage: A = FSMState('A')
     523            sage: deepcopy(A)
     524            State 'A'
     525        """
     526        return deepcopy(self, memo)
     527
     528    def relabeled(self, label, memo=None):
     529        """
     530        Returns a deep copy of the state with a new label.
     531
     532        EXAMPLES::
     533
     534            sage: A = FSMState('A')
     535            sage: A.relabeled('B')
     536            State 'B'
     537
     538        """
     539        self._deepcopy_relabel_ = label
     540        new = deepcopy(self, memo)
     541        del self._deepcopy_relabel_
     542        return new
     543
     544
     545    def __hash__(self):
     546        """
     547        Returns a hash value for the object.
     548
     549        TESTS::
     550
     551            sage: A = FSMState('A')
     552            sage: hash(A) #random
     553            -269909568
     554        """
     555        return hash(self.label())
     556
     557
     558    def _repr_(self):
     559        """
     560        Returns the string "State label".
     561
     562        TESTS:
     563
     564            sage: FSMState('A')._repr_()
     565            "State 'A'"
     566        """
     567        return "State %s" % (repr(self.label()))
     568
     569
     570    def __eq__(left, right):
     571        """
     572        Returns True if two states are the same, i.e., if they have
     573        the same labels.
     574
     575        Note that the hooks and whether the states are initial or
     576        final are not checked.
     577
     578        EXAMPLES::
     579
     580            sage: A = FSMState('A')
     581            sage: B = FSMState('A', is_initial=True)
     582            sage: A == B
     583            True
     584        """
     585        if not is_FSMState(right):
     586            return False
     587        return left.label() == right.label()
     588
     589
     590    def __ne__(left, right):
     591        """
     592        Tests for inequality, complement of __eq__.
     593
     594        EXAMPLES::
     595
     596            sage: A = FSMState('A', is_initial=True)
     597            sage: B = FSMState('A', is_final=True)
     598            sage: A != B
     599            False
     600        """
     601        return (not (left == right))
     602
     603
     604    def __nonzero__(self):
     605        """
     606        Returns True.
     607
     608        TESTS::
     609
     610            sage: FSMState('A').__nonzero__()
     611            True
     612        """
     613        return True  # A state cannot be zero (see __init__)
     614
     615
     616#*****************************************************************************
     617
     618
     619def is_FSMTransition(T):
     620    """
     621    Tests whether or not ``T`` inherits from :class:`FSMTransition`.
     622
     623    TESTS::
     624
     625        sage: from sage.combinat.finite_state_machine import is_FSMTransition
     626        sage: is_FSMTransition(FSMTransition('A', 'B'))
     627        True
     628    """
     629    return isinstance(T, FSMTransition)
     630
     631
     632class FSMTransition(SageObject):
     633    """
     634    Class for a transition of a finite state machine.
     635
     636    EXAMPLES::
     637
     638        sage: A = FSMState('A')
     639        sage: B = FSMState('B')
     640        sage: S = FSMTransition(A, B, 0, 1)
     641        sage: T = FSMTransition('A', 'B', 0, 1)
     642        sage: T == S
     643        True
     644        sage: U = FSMTransition('A', 'B', 0)
     645        sage: U == T
     646        False
     647
     648    """
     649    def __init__(self, from_state, to_state,
     650                 word_in=None, word_out=None,
     651                 hook=None):
     652        """
     653        See :class:`FSMTransition` for more information.
     654
     655        EXAMPLES::
     656
     657            sage: FSMTransition('A', 'B', 0, 1)
     658            Transition from State 'A' to State 'B': 0|1
     659        """
     660        if is_FSMState(from_state):
     661            self.from_state = from_state
     662        else:
     663            self.from_state = FSMState(from_state)
     664        if is_FSMState(to_state):
     665            self.to_state = to_state
     666        else:
     667            self.to_state = FSMState(to_state)
     668
     669        if isinstance(word_in, list):
     670            self.word_in = word_in
     671        elif word_in is not None:
     672            self.word_in = [word_in]
     673        else:
     674            self.word_in = []
     675
     676        if isinstance(word_out, list):
     677            self.word_out = word_out
     678        elif word_out is not None:
     679            self.word_out = [word_out]
     680        else:
     681            self.word_out = []
     682
     683        if hook is not None:
     684            if hasattr(hook, '__call__'):
     685                self.hook = hook
     686            else:
     687                raise TypeError, 'Wrong argument for hook.'
     688
     689
     690    def __copy__(self):
     691        """
     692        Returns a (shallow) copy of the transition.
     693
     694        EXAMPLES::
     695
     696            sage: t = FSMTransition('A', 'B', 0)
     697            sage: copy(t)
     698            Transition from State 'A' to State 'B': 0|-
     699        """
     700        new = FSMTransition(self.from_state, self.to_state,
     701                            self.word_in, self.word_out)
     702        if hasattr(self, 'hook'):
     703            new.hook = self.hook
     704        return new
     705
     706
     707    copy = __copy__
     708
     709    def __deepcopy__(self, memo):
     710        """
     711        Returns a deep copy of the transition.
     712
     713        EXAMPLES::
     714
     715            sage: t = FSMTransition('A', 'B', 0)
     716            sage: deepcopy(t)
     717            Transition from State 'A' to State 'B': 0|-
     718        """
     719        new = FSMTransition(deepcopy(self.from_state, memo),
     720                            deepcopy(self.to_state, memo),
     721                            deepcopy(self.word_in, memo),
     722                            deepcopy(self.word_out, memo))
     723        if hasattr(self, 'hook'):
     724            new.hook = deepcopy(self.hook, memo)
     725        return new
     726
     727
     728    def deepcopy(self, memo=None):
     729        """
     730        Returns a deep copy of the transition.
     731
     732        EXAMPLES::
     733
     734            sage: t = FSMTransition('A', 'B', 0)
     735            sage: deepcopy(t)
     736            Transition from State 'A' to State 'B': 0|-
     737        """
     738        return deepcopy(self, memo)
     739
     740
     741    def __hash__(self):
     742        """
     743        Since transitions are mutable, they should not be hashable, so
     744        we return a type error.
     745
     746        EXAMPLES::
     747
     748            sage: hash(FSMTransition('A', 'B'))
     749            Traceback (most recent call last):
     750            ...
     751            TypeError: Transitions are mutable, and thus not hashable.
     752
     753        """
     754        raise TypeError, "Transitions are mutable, and thus not hashable."
     755
     756
     757    def _repr_(self):
     758        """
     759        Represents a transitions as from state to state and input, output.
     760
     761        EXAMPLES::
     762
     763            sage: FSMTransition('A', 'B', 0, 0)._repr_()
     764            "Transition from State 'A' to State 'B': 0|0"
     765
     766        """
     767        return "Transition from %s to %s: %s" % (repr(self.from_state),
     768                                                 repr(self.to_state),
     769                                                 self._in_out_label_())
     770
     771
     772    def _in_out_label_(self):
     773        """
     774        Returns the input and output of a transition as
     775        "word_in|word_out".
     776
     777        EXAMPLES::
     778
     779            sage: FSMTransition('A', 'B', 0, 1)._in_out_label_()
     780            '0|1'
     781        """
     782        return "%s|%s" % (FSMWordSymbol(self.word_in),
     783                          FSMWordSymbol(self.word_out))
     784
     785
     786    def __eq__(left, right):
     787        """
     788        Returns True if the two transitions are the same, i.e., if the
     789        both go from the same states to the same states and read and
     790        write the same words.
     791
     792        Note that the hooks are not checked.
     793
     794        EXAMPLES::
     795
     796            sage: A = FSMState('A', is_initial=True)
     797            sage: t1 = FSMTransition('A', 'B', 0, 1)
     798            sage: t2 = FSMTransition(A, 'B', 0, 1)
     799            sage: t1 == t2
     800            True
     801        """
     802        if not is_FSMTransition(right):
     803            raise TypeError, 'Only instances of FSMTransition ' \
     804                'can be compared.'
     805        return left.from_state == right.from_state \
     806            and left.to_state == right.to_state \
     807            and left.word_in == right.word_in \
     808            and left.word_out == right.word_out
     809
     810
     811    def __ne__(left, right):
     812        """
     813        Tests for inequality, complement of __eq__.
     814
     815        EXAMPLES::
     816
     817            sage: A = FSMState('A', is_initial=True)
     818            sage: t1 = FSMTransition('A', 'B', 0, 1)
     819            sage: t2 = FSMTransition(A, 'B', 0, 1)
     820            sage: t1!=t2
     821            False
     822        """
     823        return (not (left == right))
     824
     825
     826    def __nonzero__(self):
     827        """
     828        Returns True.
     829
     830        EXAMPLES::
     831
     832            sage: FSMTransition('A', 'B', 0).__nonzero__()
     833            True
     834        """
     835        return True  # A transition cannot be zero (see __init__)
     836
     837
     838#*****************************************************************************
     839
     840
     841def is_FiniteStateMachine(FSM):
     842    """
     843    Tests whether or not ``FSM`` inherits from :class:`FiniteStateMachine`.
     844
     845    TESTS::
     846
     847        sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine
     848        sage: is_FiniteStateMachine(FiniteStateMachine())
     849        True
     850        sage: is_FiniteStateMachine(Automaton())
     851        True
     852        sage: is_FiniteStateMachine(Transducer())
     853        True
     854    """
     855    return isinstance(FSM, FiniteStateMachine)
     856
     857
     858class FiniteStateMachine(SageObject):
     859    """
     860    Class for a finite state machine.
     861
     862    A finite state machine is a finite set of states connected by transistions.
     863
     864
     865    INPUT:
     866
     867    - ``data`` -- can be any of the following:
     868
     869      - ``{A:{B:{word_in=0, word_out=1}, C:{word_in=1, word_out=1}, ...}``
     870      - ``{A:{B:(0, 1), C:(1, 1), ...}``
     871      - ``{A:{B:FSMTransition(A, B, 0, 1), C:FSMTransition(A, C, 1, 1), ...}``
     872      - ``{A:[(B, 0, 1), (C, 1, 1)], ...}``
     873      - ``{A:[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)], ...}``
     874      - ``[{from_state:A, to_state:B, word_in:0, word_out:1}, \
     875            from_state:A, to_state:C, word_in:1, word_out:1}, ...]``
     876      - ``[(A, B, 0, 1), (A, C, 1, 1), ...]``
     877      - ``[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1), ...]``
     878
     879      If ``A``, ``B`` and ``C`` are not instances of :class:`FSMState`,
     880      they are seen as labels of states. Note that the labels of a finite
     881      state machine have to be unique.
     882
     883      Every tuple can be replaced by a list and vice versa. In fact,
     884      everything that is iterable can be used.
     885
     886      Other arguments, which :class:`FSMTransition` accepts, can be added,
     887      too. Do this after the argument ``word_out``.
     888
     889      Further the following things are accepted for ``data``:
     890
     891      - a function (acting as transition function): The input of that
     892        function is the label of a state (the from-state) and a letter
     893        of the alphabet. It should output a tuple consisting of a
     894        label of a state (the to-state) and a word (the word-out). It
     895        may also output a list of such tuples if several transitions
     896        from the from-state and the input letter exist (this means
     897        that the finite state machine is non-deterministic). If the
     898        transition does not exist, the function should raise a
     899        LookUpError or return an empty list.  Some ``initial_states``
     900        and an ``input_alphabet`` have to be specified.
     901
     902      - an other instance of a finite state machine
     903
     904    - ``mode`` -- can be any of the following:
     905
     906      #.  None
     907      #.  'automaton'
     908      #.  'transducer'
     909
     910    - ``initial_states`` and ``final_states`` -- the initial and
     911      finial states if this machine
     912
     913    - ``input_alphabet`` and ``output_alphabet`` -- the input and
     914      output alphabets of this machine
     915
     916    - ``determine_alphabets`` -- If True, then the function
     917      ``determine_alphabets()`` is called after ``data`` was read and
     918      processed, if False, then not. If it is None, then it is decided
     919      during the construction of the finite state machine whether
     920      ``determine_alphabets()`` should be called.
     921
     922    - ``store_states_dict`` -- If True, then additionally the states
     923      are stored in an interal dictionary for speed up.
     924
     925
     926    EXAMPLES:
     927
     928    See documentation for more examples.
     929
     930    We illustrate the different input formats:
     931
     932    #.  A dictionary of lists, where keys are states and list-elements are
     933        states::
     934
     935            sage: a = FSMState('S_a', 'a')
     936            sage: b = FSMState('S_b', 'b')
     937            sage: c = FSMState('S_c', 'c')
     938            sage: d = FSMState('S_d', 'd')
     939            sage: FiniteStateMachine({a:[b, c], b:[b, c, d],
     940            ....:                     c:[a, b], d:[a, c]})
     941            finite state machine with 4 states
     942
     943    #.  A list of transitions::
     944
     945            sage: a = FSMState('S_a', 'a')
     946            sage: b = FSMState('S_b', 'b')
     947            sage: c = FSMState('S_c', 'c')
     948            sage: d = FSMState('S_d', 'd')
     949            sage: t1 = FSMTransition(a, b)
     950            sage: t2 = FSMTransition(b, c)
     951            sage: t3 = FSMTransition(b, d)
     952            sage: t4 = FSMTransition(c, d)
     953            sage: FiniteStateMachine([t1, t2, t3, t4])
     954            finite state machine with 4 states
     955
     956        It is possible to skip ``FSMTransition`` in the example above.
     957
     958    #.  A function:
     959
     960        In this case some inital states and an input alphabet have to
     961        be specified. ::
     962
     963            sage: def f(state_from, read):
     964            ....:     if int(state_from) + read <= 2:
     965            ....:         state_to = 2*int(state_from)+read
     966            ....:         write = 0
     967            ....:     else:
     968            ....:         state_to = 2*int(state_from) + read - 5
     969            ....:         write = 1
     970            ....:     return (str(state_to), write)
     971            sage: F = FiniteStateMachine(f, input_alphabet=[0, 1],
     972            ....:                        initial_states=['0'],
     973            ....:                        final_states=['0'])
     974            sage: F([1, 0, 1])
     975            (True, State '0', [0, 0, 1])
     976
     977        ::
     978
     979            sage: A = FSMState('A')
     980            sage: B = FSMState('B')
     981            sage: C = FSMState('C')
     982            sage: FSM1 = FiniteStateMachine(
     983            ....:  {A:{B:{'word_in':0, 'word_out':1},
     984            ....:   C:{'word_in':1, 'word_out':1}}})
     985            sage: FSM2 = FiniteStateMachine({A:{B:(0, 1), C:(1, 1)}})
     986            sage: FSM3 = FiniteStateMachine(
     987            ....:  {A:{B:FSMTransition(A, B, 0, 1),
     988            ....:      C:FSMTransition(A, C, 1, 1)}})
     989            sage: FSM4 = FiniteStateMachine({A:[(B, 0, 1), (C, 1, 1)]})
     990            sage: FSM5 = FiniteStateMachine(
     991            ....:  {A:[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)]})
     992            sage: FSM6 = FiniteStateMachine(
     993            ....:  [{'from_state':A, 'to_state':B, 'word_in':0, 'word_out':1},
     994            ....:   {'from_state':A, 'to_state':C, 'word_in':1, 'word_out':1}])
     995            sage: FSM7 = FiniteStateMachine([(A, B, 0, 1), (A, C, 1, 1)])
     996            sage: FSM8 = FiniteStateMachine(
     997            ....:  [FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)])
     998
     999            sage: FSM1 == FSM2 == FSM3 == FSM4 == FSM5 == FSM6 == FSM7 == FSM8
     1000            True
     1001
     1002        It is possible to skip ``FSMTransition`` in the example above.
     1003
     1004    #.  A dictionary of dictionaries, where
     1005
     1006        - the keys of the outer dictionary are state-labels (from-state of
     1007          transition),
     1008        - the keys of the inner dictionaries are state-labels (to-state of
     1009          transition),
     1010        - the values of the inner dictionaries are lists, where the first
     1011          entry is an input label and the second entry is an output label.
     1012
     1013        ::
     1014
     1015            sage: FiniteStateMachine({'a':{'a':[0, 0], 'b':[1, 1]},
     1016            ....:                     'b':{'b':[1, 0]}})
     1017            finite state machine with 2 states
     1018    """
     1019
     1020    #*************************************************************************
     1021    # init
     1022    #*************************************************************************
     1023
     1024
     1025    def __init__(self,
     1026                 data=None,
     1027                 mode=None,
     1028                 initial_states=None, final_states=None,
     1029                 input_alphabet=None, output_alphabet=None,
     1030                 determine_alphabets=None,
     1031                 store_states_dict=True):
     1032        """
     1033        See :class:`FiniteStateMachine` for more information.
     1034
     1035        TEST::
     1036
     1037            sage: FiniteStateMachine()
     1038            finite state machine with 0 states
     1039        """
     1040        self._states_ = []  # List of states in the finite state
     1041                            # machine.  Each state stores a list of
     1042                            # outgoing transitions.
     1043        if store_states_dict:
     1044            self._states_dict_ = {}
     1045
     1046        if initial_states is not None:
     1047            if not hasattr(initial_states, '__iter__'):
     1048                raise TypeError, 'Initial states must be iterable ' \
     1049                    '(e.g. a list of states).'
     1050            for s in initial_states:
     1051                state = self.add_state(s)
     1052                state.is_initial = True
     1053
     1054        if final_states is not None:
     1055            if not hasattr(final_states, '__iter__'):
     1056                raise TypeError, 'Final states must be iterable ' \
     1057                    '(e.g. a list of states).'
     1058            for s in final_states:
     1059                state = self.add_state(s)
     1060                state.is_final = True
     1061
     1062        self.mode = mode
     1063        self.input_alphabet = input_alphabet
     1064        self.output_alphabet = output_alphabet
     1065
     1066        if data is None:
     1067            pass
     1068        elif is_FiniteStateMachine(data):
     1069            raise NotImplementedError
     1070        elif hasattr(data, 'iteritems'):
     1071            # data is a dict (or something similar),
     1072            # format: key = from_state, value = iterator of transitions
     1073            for (sf, iter_transitions) in data.iteritems():
     1074                from_state = self.add_state(sf)
     1075                if hasattr(iter_transitions, 'iteritems'):
     1076                    for (st, transition) in iter_transitions.iteritems():
     1077                        to_state = self.add_state(st)
     1078                        if is_FSMTransition(transition):
     1079                            self.add_transition(transition)
     1080                        elif hasattr(transition, 'iteritems'):
     1081                            self.add_transition(sf, st, **transition)
     1082                        elif hasattr(transition, '__iter__'):
     1083                            self.add_transition(sf, st, *transition)
     1084                        else:
     1085                            self.add_transition(sf, st, transition)
     1086                elif hasattr(iter_transitions, '__iter__'):
     1087                    for transition in iter_transitions:
     1088                        if hasattr(transition, '__iter__'):
     1089                            L = [sf]
     1090                            L.extend(transition)
     1091                        elif is_FSMTransition(transition):
     1092                            L = transition
     1093                        else:
     1094                            L = [sf, transition]
     1095                        self.add_transition(L)
     1096                else:
     1097                    raise TypeError, 'Wrong input data for transition.'
     1098            if determine_alphabets is None and input_alphabet is None \
     1099                    and output_alphabet is None:
     1100                determine_alphabets = True
     1101        elif hasattr(data, '__iter__'):
     1102            # data is a something that is iterable,
     1103            # items are transitions
     1104            for transition in data:
     1105                if is_FSMTransition(transition):
     1106                    self.add_transition(transition)
     1107                elif hasattr(transition, 'iteritems'):
     1108                    self.add_transition(transition)
     1109                elif hasattr(transition, '__iter__'):
     1110                    self.add_transition(transition)
     1111                else:
     1112                    raise TypeError, 'Wrong input data for transition.'
     1113            if determine_alphabets is None and input_alphabet is None \
     1114                    and output_alphabet is None:
     1115                determine_alphabets = True
     1116        elif hasattr(data, '__call__'):
     1117            self.add_from_transition_function(data,
     1118                                              ignore_existing_states=True)
     1119        else:
     1120            raise TypeError, 'Cannot decide what to do with data.'
     1121
     1122        if determine_alphabets:
     1123            self.determine_alphabets()
     1124
     1125
     1126    #*************************************************************************
     1127    # copy and hash
     1128    #*************************************************************************
     1129
     1130
     1131    def __copy__(self):
     1132        """
     1133        Returns a (shallow) copy of the finite state machine.
     1134
     1135        TESTS::
     1136
     1137            sage: copy(FiniteStateMachine())
     1138            Traceback (most recent call last):
     1139            ...
     1140            NotImplementedError
     1141        """
     1142        raise NotImplementedError
     1143
     1144
     1145    copy = __copy__
     1146
     1147
     1148    def __deepcopy__(self, memo):
     1149        """
     1150        Returns a deep copy of the finite state machine.
     1151
     1152        EXAMPLES::
     1153
     1154            sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)])
     1155            sage: deepcopy(F)
     1156            finite state machine with 1 states
     1157        """
     1158        relabel = hasattr(self, '_deepcopy_relabel_')
     1159        new = FiniteStateMachine()
     1160        import itertools
     1161        relabel_iter = itertools.count(0)
     1162        for state in self.iter_states():
     1163            if relabel:
     1164                state._deepcopy_relabel_ = relabel_iter.next()
     1165            s = deepcopy(state, memo)
     1166            if relabel:
     1167                del state._deepcopy_relabel_
     1168            new.add_state(s)
     1169        for transition in self.iter_transitions():
     1170            new.add_transition(deepcopy(transition, memo))
     1171        new.mode = deepcopy(self.mode, memo)
     1172        new.input_alphabet = deepcopy(self.input_alphabet, memo)
     1173        new.output_alphabet = deepcopy(self.output_alphabet, memo)
     1174        return new
     1175
     1176
     1177    def deepcopy(self, memo=None):
     1178        """
     1179        Returns a deep copy of the finite state machine.
     1180
     1181        EXAMPLES::
     1182
     1183            sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)])
     1184            sage: deepcopy(F)
     1185            finite state machine with 1 states
     1186        """
     1187        return deepcopy(self, memo)
     1188
     1189
     1190    def relabeled(self, memo=None):
     1191        """
     1192        Returns a deep copy of the finite state machine, but the
     1193        states are relabeled by integers starting with 0.
     1194
     1195        EXAMPLES::
     1196
     1197            sage: FSM1 = FiniteStateMachine([('A', 'B'), ('B', 'C'), ('C', 'A')])
     1198            sage: FSM1.states()
     1199            [State 'A', State 'B', State 'C']
     1200            sage: FSM2 = FSM1.relabeled()
     1201            sage: FSM2.states()
     1202            [State 0, State 1, State 2]
     1203        """
     1204        self._deepcopy_relabel_ = True
     1205        new = deepcopy(self, memo)
     1206        del self._deepcopy_relabel_
     1207        return new
     1208
     1209
     1210    def __hash__(self):
     1211        """
     1212        Since finite state machines are mutable, they should not be
     1213        hashable, so we return a type error.
     1214
     1215        EXAMPLES::
     1216
     1217            sage: hash(FiniteStateMachine())
     1218            Traceback (most recent call last):
     1219            ...
     1220            TypeError: Finite state machines are mutable, and thus not hashable.
     1221        """
     1222        if getattr(self, "_immutable", False):
     1223            return hash((tuple(self.states()), tuple(self.transitions())))
     1224        raise TypeError, "Finite state machines are mutable, " \
     1225            "and thus not hashable."
     1226
     1227
     1228    #*************************************************************************
     1229    # operators
     1230    #*************************************************************************
     1231
     1232
     1233    def __add__(self, other):
     1234        """
     1235        Returns the disjoint union of the finite state machines self and other.
     1236
     1237        TESTS::
     1238
     1239            sage: FiniteStateMachine() + FiniteStateMachine([('A', 'B')])
     1240            Traceback (most recent call last):
     1241            ...
     1242            NotImplementedError
     1243        """
     1244        if is_FiniteStateMachine(other):
     1245            return self.disjoint_union(other)
     1246
     1247
     1248    def __iadd__(self, other):
     1249        """
     1250        TESTS::
     1251
     1252            sage: F = FiniteStateMachine()
     1253            sage: F += FiniteStateMachine()
     1254            Traceback (most recent call last):
     1255            ...
     1256            NotImplementedError
     1257        """
     1258        raise NotImplementedError
     1259
     1260
     1261    def __mul__(self, other):
     1262        """
     1263        TESTS::
     1264
     1265            sage: FiniteStateMachine() * FiniteStateMachine([('A', 'B')])
     1266            Traceback (most recent call last):
     1267            ...
     1268            NotImplementedError
     1269        """
     1270        if is_FiniteStateMachine(other):
     1271            return self.intersection(other)
     1272
     1273
     1274    def __imul__(self, other):
     1275        """
     1276        TESTS::
     1277
     1278            sage: F = FiniteStateMachine()
     1279            sage: F *= FiniteStateMachine()
     1280            Traceback (most recent call last):
     1281            ...
     1282            NotImplementedError
     1283        """
     1284        raise NotImplementedError
     1285
     1286
     1287    def __call__(self, *args, **kwargs):
     1288        """
     1289        Calls either method ``composition`` or ``process``.
     1290
     1291        EXAMPLES::
     1292
     1293            sage: A = FSMState('A', is_initial=True, is_final=True)
     1294            sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]})
     1295            sage: binary_inverter([0, 1, 0, 0, 1, 1])
     1296            (True, State 'A', [1, 0, 1, 1, 0, 0])
     1297
     1298        ::
     1299
     1300            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'B', 1, 1),
     1301            ....:                 ('B', 'B', 0, 0)],
     1302            ....:                initial_states=['A'], final_states=['B'])
     1303            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     1304            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     1305            ....:                initial_states=[1], final_states=[1])
     1306            sage: H = G(F)
     1307            sage: H.states()
     1308            [State ('A', 1), State ('B', 1), State ('B', 2)]
     1309        """
     1310        if len(args) == 0:
     1311            raise TypeError, "Called with too few arguments."
     1312        if is_FiniteStateMachine(args[0]):
     1313            return self.composition(*args, **kwargs)
     1314        if hasattr(args[0], '__iter__'):
     1315            return self.process(*args, **kwargs)
     1316        raise TypeError, "Do not know what to do with that arguments."
     1317
     1318
     1319    #*************************************************************************
     1320    # tests
     1321    #*************************************************************************
     1322
     1323
     1324    def __nonzero__(self):
     1325        """
     1326        Returns True if the finite state machine consists of at least
     1327        one state.
     1328
     1329        TESTS::
     1330
     1331            sage: FiniteStateMachine().__nonzero__()
     1332            False
     1333        """
     1334        return len(self._states_) > 0
     1335
     1336
     1337    def __eq__(left, right):
     1338        """
     1339        Returns True if the two finite state machines are equal, i.e.,
     1340        if they have the same states and the same transitions.
     1341
     1342        EXAMPLES::
     1343
     1344            sage: F = FiniteStateMachine([('A', 'B', 1)])
     1345            sage: F == FiniteStateMachine()
     1346            False
     1347        """
     1348        if not is_FiniteStateMachine(right):
     1349            raise TypeError, 'Only instances of FiniteStateMachine ' \
     1350                'can be compared.'
     1351        if len(left._states_) != len(right._states_):
     1352            return False
     1353        for state in left.iter_states():
     1354            if state not in right._states_:
     1355                return False
     1356            left_transitions = state.transitions
     1357            right_transitions = right.state(state).transitions
     1358            if len(left_transitions) != len(right_transitions):
     1359                return False
     1360            for t in left_transitions:
     1361                if t not in right_transitions:
     1362                    return False
     1363        return True
     1364
     1365
     1366    def __ne__(left, right):
     1367        """
     1368        Tests for inequality, complement of :meth:`.__eq__`.
     1369
     1370        EXAMPLES::
     1371
     1372            sage: E = FiniteStateMachine([('A', 'B', 0)])
     1373            sage: F = Automaton([('A', 'B', 0)])
     1374            sage: G = Transducer([('A', 'B', 0, 1)])
     1375            sage: E == F
     1376            True
     1377            sage: E == G
     1378            False
     1379        """
     1380        return (not (left == right))
     1381
     1382
     1383    def __contains__(self, item):
     1384        """
     1385        Returns true, if the finite state machine contains the
     1386        state or transition item. Note that only the labels of the
     1387        states and the input and output words are tested.
     1388
     1389        EXAMPLES::
     1390
     1391            sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)])
     1392            sage: FSMState('A', is_initial=True) in F
     1393            True
     1394            sage: 'A' in F
     1395            False
     1396            sage: FSMTransition('A', 'B', 0) in F
     1397            True
     1398        """
     1399        if is_FSMState(item):
     1400            return self.has_state(item)
     1401        if is_FSMTransition(item):
     1402            return self.has_transition(item)
     1403        return False
     1404
     1405
     1406    #*************************************************************************
     1407    # representations / LaTeX
     1408    #*************************************************************************
     1409
     1410
     1411    def _repr_(self):
     1412        """
     1413        Represents the finite state machine as "finite state machine
     1414        with n states" where n is the number of states.
     1415
     1416        EXAMPLES::
     1417
     1418            sage: FiniteStateMachine()._repr_()
     1419            'finite state machine with 0 states'
     1420        """
     1421        return "finite state machine with %s states" % len(self._states_)
     1422
     1423
     1424    def _latex_(self):
     1425        r"""
     1426        Returns a LaTex code for the graph of the finite state machine.
     1427
     1428        EXAMPLES::
     1429
     1430            sage: F = FiniteStateMachine([('A', 'B', 1)])
     1431            sage: F._latex_()
     1432            '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\verb|A|}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\verb|B|}\n;\\path[->] (v0) edge node {$\\left[1\\right] \\mid \\left[\\right]$} (v1);\n\\end{tikzpicture}'
     1433        """
     1434        result = "\\begin{tikzpicture}[auto]\n"
     1435        j = 0;
     1436        for vertex in self.states():
     1437            if not hasattr(vertex, "coordinates"):
     1438                vertex.coordinates = (3*cos(2*pi*j/len(self.states())),
     1439                                      3*sin(2*pi*j/len(self.states())))
     1440            options = ""
     1441            if vertex in self.final_states():
     1442                options += ",accepting"
     1443            if hasattr(vertex, "format_label"):
     1444                label = vertex.format_label()
     1445            elif hasattr(self, "format_state_label"):
     1446                label = self.format_state_label(vertex)
     1447            else:
     1448                label=latex(vertex.label())
     1449            result += "\\node[state%s] (v%d) at (%f,%f) {%s}\n;" % (
     1450                options, j, vertex.coordinates[0],
     1451                vertex.coordinates[1], label)
     1452            vertex._number_ = j
     1453            j += 1
     1454        adjacent = {}
     1455        for source in self.states():
     1456            for target in self.states():
     1457                transitions = filter(lambda transition: \
     1458                                         transition.to_state == target,
     1459                                     source.transitions)
     1460                adjacent[source, target] = transitions
     1461
     1462        for ((source, target), transitions) in adjacent.iteritems():
     1463            if len(transitions) > 0:
     1464                labels = []
     1465                for transition in transitions:
     1466                    if hasattr(transition, "format_label"):
     1467                        labels.append(transition.format_label())
     1468                        continue
     1469                    elif hasattr(self, "format_transition_label"):
     1470                        format_transition_label = self.format_transition_label
     1471                    else:
     1472                        format_transition_label = latex
     1473                    if self.mode == 'automaton':
     1474                        labels.append(format_transition_label(
     1475                                transition.word_in))
     1476                    else:
     1477                        labels.append(format_transition_label(
     1478                            transition.word_in) + "\\mid" + \
     1479                                format_transition_label(transition.word_out))
     1480                label = ", ".join(labels)
     1481                if source != target:
     1482                    if len(adjacent[target, source]) > 0:
     1483                        angle = atan2(
     1484                            target.coordinates[1] - source.coordinates[1],
     1485                            target.coordinates[0]-source.coordinates[0])*180/pi
     1486                        angle_source = ".%.2f" % ((angle+5).n(),)
     1487                        angle_target = ".%.2f" % ((angle+175).n(),)
     1488                    else:
     1489                        angle_source = ""
     1490                        angle_target = ""
     1491                    result += "\\path[->] (v%d%s) edge node {$%s$} (v%d%s);\n" % (
     1492                        source._number_, angle_source, label,
     1493                        target._number_, angle_target)
     1494                else:
     1495                    result += "\\path[->] (v%d) edge[loop above] node {$%s$} ();\n" % (
     1496                        source._number_, label)
     1497
     1498        result += "\\end{tikzpicture}"
     1499        return result
     1500
     1501
     1502    #*************************************************************************
     1503    # other
     1504    #*************************************************************************
     1505
     1506
     1507    def _matrix_(self, R=None):
     1508        """
     1509        Returns the adjacency matrix of the finite state machine.
     1510        See adjacency_matrix for more information.
     1511
     1512        EXAMPLES::
     1513
     1514            sage: B = FiniteStateMachine({0: {0: (0, 0), 'a': (1, 0)},
     1515            ....:                         'a': {2: (0, 0), 3: (1, 0)},
     1516            ....:                         2:{0:(1, 1), 4:(0, 0)},
     1517            ....:                         3:{'a':(0, 1), 2:(1, 1)},
     1518            ....:                         4:{4:(1, 1), 3:(0, 1)}},
     1519            ....:                        initial_states=[0])
     1520            sage: B._matrix_()
     1521            [1 1 0 0 0]
     1522            [0 0 1 1 0]
     1523            [x 0 0 0 1]
     1524            [0 x x 0 0]
     1525            [0 0 0 x x]
     1526        """
     1527        return self.adjacency_matrix()
     1528
     1529
     1530    def adjacency_matrix(self, input=None, entry=(lambda transition:var('x')**transition.word_out[0])):
     1531        """
     1532        Returns the adjacency matrix of the underlying graph where
     1533        only transitions with input label ``input`` are respected.
     1534
     1535        The function ``entry`` takes a transition and the return value
     1536        is written in the matrix as the entry
     1537        ``(transition.from_state, transition.to_state)``. If any
     1538        label of a state is not an integer, the finite state machine
     1539        is relabeled at the beginning.  If there are more than one
     1540        transitions between two states, then the different return
     1541        values of ``entry`` are added up.
     1542
     1543        The default value of entry takes the variable ``x`` to the
     1544        power of the output word of the transition.
     1545
     1546        EXAMPLES::
     1547
     1548            sage: B = FiniteStateMachine({0:{0:(0, 0), 'a':(1, 0)},
     1549            ....:                         'a':{2:(0, 0), 3:(1, 0)},
     1550            ....:                         2:{0:(1, 1), 4:(0, 0)},
     1551            ....:                         3:{'a':(0, 1), 2:(1, 1)},
     1552            ....:                         4:{4:(1, 1), 3:(0, 1)}},
     1553            ....:                        initial_states=[0])
     1554            sage: B.adjacency_matrix()
     1555            [1 1 0 0 0]
     1556            [0 0 1 1 0]
     1557            [x 0 0 0 1]
     1558            [0 x x 0 0]
     1559            [0 0 0 x x]
     1560            sage: B.adjacency_matrix(entry=(lambda transition: 1))
     1561            [1 1 0 0 0]
     1562            [0 0 1 1 0]
     1563            [1 0 0 0 1]
     1564            [0 1 1 0 0]
     1565            [0 0 0 1 1]
     1566            sage: B.adjacency_matrix(1, entry=(lambda transition:
     1567            ....:     exp(I*transition.word_out[0]*var('t'))))
     1568            [      0       1       0       0       0]
     1569            [      0       0       0       1       0]
     1570            [e^(I*t)       0       0       0       0]
     1571            [      0       0 e^(I*t)       0       0]
     1572            [      0       0       0       0 e^(I*t)]
     1573
     1574        """
     1575        relabeledFSM = self
     1576        l = len(relabeledFSM.states())
     1577        for state in self.states():
     1578            if state.label() not in ZZ or state.label() >= l:
     1579                relabeledFSM = self.relabeled()
     1580                break
     1581        dictionary = {}
     1582        for transition in relabeledFSM.iter_transitions():
     1583            if input is None or transition.word_in == [input]:
     1584                if (transition.from_state.label(), transition.to_state.label()) in dictionary:
     1585                    dictionary[(transition.from_state.label(), transition.to_state.label())] += entry(transition)
     1586                else:
     1587                    dictionary[(transition.from_state.label(), transition.to_state.label())] = entry(transition)
     1588        return matrix(len(relabeledFSM.states()), dictionary)
     1589
     1590
     1591    def determine_alphabets(self, reset=True):
     1592        """
     1593        Determines the input and output alphabet according to the
     1594        transitions in self.
     1595
     1596        After this operation the input alphabet and the output
     1597        alphabet of self are a list of letters.
     1598
     1599        - If reset is True, then the existing input alphabet is
     1600          erased, otherwise new letters are appended to the existing
     1601          alphabet.
     1602
     1603        EXAMPLES::
     1604
     1605            sage: T = Transducer([(1, 1, 1, 0), (1, 2, 2, 1),
     1606            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     1607            ....:                determine_alphabets=False)
     1608            sage: (T.input_alphabet, T.output_alphabet)
     1609            (None, None)
     1610            sage: T.determine_alphabets()
     1611            sage: (T.input_alphabet, T.output_alphabet)
     1612            ([0, 1, 2], [0, 1])
     1613       """
     1614        if reset:
     1615            ain = set()
     1616            aout = set()
     1617        else:
     1618            ain = set(self.input_alphabet)
     1619            aout = set(self.output_alphabet)
     1620
     1621        for t in self.iter_transitions():
     1622            for letter in t.word_in:
     1623                ain.add(letter)
     1624            for letter in t.word_out:
     1625                aout.add(letter)
     1626        self.input_alphabet = list(ain)
     1627        self.output_alphabet = list(aout)
     1628
     1629
     1630    #*************************************************************************
     1631    # get states and transitions
     1632    #*************************************************************************
     1633
     1634
     1635    def states(self):
     1636        """
     1637        Returns the states of the finite state machine as list.
     1638
     1639        EXAMPLES::
     1640
     1641            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1642            sage: FSM.states()
     1643            [State '1', State '2']
     1644
     1645        """
     1646        return copy(self._states_)
     1647
     1648
     1649    def iter_states(self):
     1650        """
     1651        Returns an iterator of the states.
     1652
     1653        EXAMPLES::
     1654
     1655            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1656            sage: [s.label() for s in FSM.iter_states()]
     1657            ['1', '2']
     1658        """
     1659        return iter(self._states_)
     1660
     1661
     1662    def transitions(self, from_state=None):
     1663        """
     1664        Returns a list of all transitions. If ``from_state`` is given,
     1665        then a list of transitions starting there is given.
     1666
     1667        EXAMPLES::
     1668
     1669            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1670            sage: FSM.transitions()
     1671            [Transition from State '1' to State '2': 1|-,
     1672             Transition from State '2' to State '2': 0|-]
     1673        """
     1674        return list(self.iter_transitions(from_state))
     1675
     1676
     1677    def iter_transitions(self, from_state=None):
     1678        """
     1679        Returns an iterator of all transitions. If ``from_state`` is
     1680        given, then an iterator of transitions starting there is given.
     1681
     1682        EXAMPLES::
     1683
     1684            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1685            sage: [(t.from_state.label(), t.to_state.label())
     1686            ....:     for t in FSM.iter_transitions('1')]
     1687            [('1', '2')]
     1688            sage: [(t.from_state.label(), t.to_state.label())
     1689            ....:     for t in FSM.iter_transitions('2')]
     1690            [('2', '2')]
     1691            sage: [(t.from_state.label(), t.to_state.label())
     1692            ....:     for t in FSM.iter_transitions()]
     1693            [('1', '2'), ('2', '2')]
     1694        """
     1695        if from_state is None:
     1696            return self._iter_transitions_all_()
     1697        else:
     1698            return iter(self.state(from_state).transitions)
     1699
     1700
     1701    def _iter_transitions_all_(self):
     1702        """
     1703        Returns an iterator over all transitions.
     1704
     1705        EXAMPLES::
     1706
     1707            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1708            sage: [(t.from_state.label(), t.to_state.label())
     1709            ....:     for t in FSM._iter_transitions_all_()]
     1710            [('1', '2'), ('2', '2')]
     1711        """
     1712        for state in self.iter_states():
     1713            for t in state.transitions:
     1714                yield t
     1715
     1716
     1717    def initial_states(self):
     1718        """
     1719        Returns a list of all initial states.
     1720
     1721        EXAMPLES::
     1722
     1723            sage: A = FSMState('A', is_initial=True)
     1724            sage: B = FSMState('B')
     1725            sage: F = FiniteStateMachine([(A, B, 1, 0)])
     1726            sage: F.initial_states()
     1727            [State 'A']
     1728        """
     1729        return list(self.iter_initial_states())
     1730
     1731
     1732    def iter_initial_states(self):
     1733        """
     1734        Returns an iterator of the initial states.
     1735
     1736        EXAMPLES::
     1737
     1738            sage: A = FSMState('A', is_initial=True)
     1739            sage: B = FSMState('B')
     1740            sage: F = FiniteStateMachine([(A, B, 1, 0)])
     1741            sage: [s.label() for s in F.iter_initial_states()]
     1742            ['A']
     1743        """
     1744        return conditional_iterator(self.iter_states(),
     1745                                    lambda s:s.is_initial)
     1746
     1747
     1748    def final_states(self):
     1749        """
     1750        Returns a list of all final states.
     1751
     1752        EXAMPLES::
     1753
     1754            sage: A = FSMState('A', is_final=True)
     1755            sage: B = FSMState('B', is_initial=True)
     1756            sage: C = FSMState('C', is_final=True)
     1757            sage: F = FiniteStateMachine([(A, B), (A, C)])
     1758            sage: F.final_states()
     1759            [State 'A', State 'C']
     1760        """
     1761        return list(self.iter_final_states())
     1762
     1763
     1764    def iter_final_states(self):
     1765        """
     1766        Returns an iterator of the final states.
     1767
     1768        EXAMPLES::
     1769
     1770            sage: A = FSMState('A', is_final=True)
     1771            sage: B = FSMState('B', is_initial=True)
     1772            sage: C = FSMState('C', is_final=True)
     1773            sage: F = FiniteStateMachine([(A, B), (A, C)])
     1774            sage: [s.label() for s in F.iter_final_states()]
     1775            ['A', 'C']
     1776        """
     1777        return conditional_iterator(self.iter_states(),
     1778                                    lambda s:s.is_final)
     1779
     1780
     1781    def state(self, state):
     1782        """
     1783        Returns the state of the finite state machine corresponding to
     1784        ``state``. If ``state`` is not an instance of
     1785        :class:`FSMState`, then it is assumed that it is the label of
     1786        a state.
     1787
     1788        If no state is found, then a ``LookupError`` is thrown.
     1789
     1790        EXAMPLES::
     1791
     1792            sage: A = FSMState('A')
     1793            sage: FSM = FiniteStateMachine([(A, 'B'), ('C', A)])
     1794            sage: FSM.state('A') == A
     1795            True
     1796            sage: FSM.state('xyz')
     1797            Traceback (most recent call last):
     1798            ...
     1799            LookupError: No state with label xyz found.
     1800        """
     1801        def what(s, switch):
     1802            if switch:
     1803                return s.label()
     1804            else:
     1805                return s
     1806        switch = is_FSMState(state)
     1807
     1808        try:
     1809            return self._states_dict_[what(state, switch)]
     1810        except AttributeError:
     1811            for s in self.iter_states():
     1812                if what(s, not switch) == state:
     1813                    return s
     1814        except KeyError:
     1815            pass
     1816        raise LookupError, \
     1817            "No state with label %s found." % (what(state, switch),)
     1818
     1819
     1820    def transition(self, transition):
     1821        """
     1822        Returns the transition of the finite state machine
     1823        corresponding to ``transition``. If ``transition`` is not an
     1824        instance of :class:`FSMTransition`, then it is assumed that it
     1825        is a tuple ``(from_state, to_state, word_in, word_out)``.
     1826
     1827        If no transition is found, then a ``LookupError`` is thrown.
     1828
     1829        EXAMPLES::
     1830
     1831            sage: t = FSMTransition('A', 'B', 0)
     1832            sage: F = FiniteStateMachine([t])
     1833            sage: F.transition(('A', 'B', 0))
     1834            Transition from State 'A' to State 'B': 0|-
     1835            sage: id(t) == id(F.transition(('A', 'B', 0)))
     1836            True
     1837       """
     1838        if not is_FSMTransition(transition):
     1839            transition = FSMTransition(*transition)
     1840        for s in self.iter_transitions(transition.from_state):
     1841            if s == transition:
     1842                return s
     1843        raise LookupError, "No transition found."
     1844
     1845
     1846    #*************************************************************************
     1847    # properties (state and transitions)
     1848    #*************************************************************************
     1849
     1850
     1851    def has_state(self, state):
     1852        """
     1853        Returns whether ``state`` is one of the states of the finite
     1854        state machine. ``state`` can be a :class:`FSMState` or a label
     1855        of a state.
     1856
     1857        EXAMPLES::
     1858
     1859            sage: FiniteStateMachine().has_state('A')
     1860            False
     1861        """
     1862        try:
     1863            self.state(state)
     1864            return True
     1865        except LookupError:
     1866            return False
     1867
     1868
     1869    def has_transition(self, transition):
     1870        """
     1871        Returns whether ``transition`` is one of the transitions of
     1872        the finite state machine. ``transition`` has to be a
     1873        :class:`FSMTransition`.
     1874
     1875        EXAMPLES::
     1876
     1877            sage: t = FSMTransition('A', 'A', 0, 1)
     1878            sage: FiniteStateMachine().has_transition(t)
     1879            False
     1880            sage: FiniteStateMachine().has_transition(('A', 'A', 0, 1))
     1881            Traceback (most recent call last):
     1882            ...
     1883            TypeError: Transition is not an instance of FSMTransition.
     1884        """
     1885        if is_FSMTransition(transition):
     1886            return transition in self.iter_transitions()
     1887        raise TypeError, "Transition is not an instance of FSMTransition."
     1888
     1889
     1890    def has_initial_state(self, state):
     1891        """
     1892        Returns whether ``state`` is one of the initial states of the
     1893        finite state machine. ``state`` can be a :class:`FSMState` or
     1894        a label.
     1895
     1896        EXAMPLES::
     1897
     1898            sage: F = FiniteStateMachine([('A', 'A')], initial_states=['A'])
     1899            sage: F.has_initial_state('A')
     1900            True
     1901        """
     1902        try:
     1903            return self.state(state).is_initial
     1904        except LookupError:
     1905            return False
     1906
     1907
     1908    def has_initial_states(self):
     1909        """
     1910        Returns whether the finite state machine has an initial state.
     1911
     1912        EXAMPLES::
     1913
     1914            sage: FiniteStateMachine().has_initial_states()
     1915            False
     1916        """
     1917        return len(self.initial_states()) > 0
     1918
     1919
     1920    def has_final_state(self, state):
     1921        """
     1922        Returns whether ``state`` is one of the final states of the
     1923        finite state machine.
     1924
     1925        EXAMPLES::
     1926
     1927            sage: FiniteStateMachine(final_states=['A']).has_final_state('A')
     1928            True
     1929        """
     1930        try:
     1931            return self.state(state).is_final
     1932        except LookupError:
     1933            return False
     1934
     1935
     1936    def has_final_states(self):
     1937        """
     1938        Returns whether the finite state machine has a final state.
     1939
     1940        EXAMPLES::
     1941
     1942            sage: FiniteStateMachine().has_final_states()
     1943            False
     1944        """
     1945        return len(self.final_states()) > 0
     1946
     1947
     1948    #*************************************************************************
     1949    # properties
     1950    #*************************************************************************
     1951
     1952
     1953    def is_deterministic(self):
     1954        """
     1955        Returns whether the finite finite state machine is deterministic.
     1956
     1957        A finite state machine is considered to be deterministic if
     1958        each transition has input label of length one and for each
     1959        pair `(q,a)` where `q` is a state and `a` is an element of the
     1960        input alphabet, there is at most one transition from `q` with
     1961        input label `a`.
     1962
     1963        TESTS::
     1964
     1965            sage: fsm = FiniteStateMachine()
     1966            sage: fsm.add_transition( ('A','B',0,[]))
     1967            Transition from State 'A' to State 'B': 0|-
     1968            sage: fsm.is_deterministic()
     1969            True
     1970            sage: fsm.add_transition( ('A','C',0,[]))
     1971            Transition from State 'A' to State 'C': 0|-
     1972            sage: fsm.is_deterministic()
     1973            False
     1974            sage: fsm.add_transition( ('A','B',[0,1],[]))
     1975            Transition from State 'A' to State 'B': 0,1|-
     1976            sage: fsm.is_deterministic()
     1977            False
     1978        """
     1979        for state in self.states():
     1980            for transition in state.transitions:
     1981                if len(transition.word_in)!=1:
     1982                    return False
     1983
     1984            transition_classes_by_word_in=itertools.groupby(
     1985                sorted(state.transitions, key=lambda t: t.word_in),
     1986                key=lambda t:t.word_in)
     1987
     1988            for key,transition_class in transition_classes_by_word_in:
     1989                if len(list(transition_class))>1:
     1990                    return False
     1991        return True
     1992
     1993
     1994    def is_connected(self):
     1995        """
     1996        TESTS::
     1997
     1998            sage: FiniteStateMachine().is_connected()
     1999            Traceback (most recent call last):
     2000            ...
     2001            NotImplementedError
     2002        """
     2003        raise NotImplementedError
     2004
     2005
     2006    #*************************************************************************
     2007    # let the finite state machine work
     2008    #*************************************************************************
     2009
     2010
     2011    def process(self, *args, **kwargs):
     2012        """
     2013        Returns whether the finite state machine accepts the input, the state
     2014        where the computation stops and which output is generated. The input
     2015        can be a list with entries from the input alphabet.
     2016
     2017        EXAMPLES::
     2018
     2019            sage: A = FSMState('A', is_initial = True, is_final = True)
     2020            sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]})
     2021            sage: binary_inverter.process([0, 1, 0, 0, 1, 1])
     2022            (True, State 'A', [1, 0, 1, 1, 0, 0])
     2023
     2024        Alternatively, we can invoke this function by::
     2025
     2026            sage: binary_inverter([0, 1, 0, 0, 1, 1])
     2027            (True, State 'A', [1, 0, 1, 1, 0, 0])
     2028
     2029        ::
     2030
     2031            sage: NAF_ = FSMState('_', is_initial = True, is_final = True)
     2032            sage: NAF1 = FSMState('1', is_final = True)
     2033            sage: NAF = Automaton(
     2034            ....:     {NAF_: [(NAF_, 0), (NAF1, 1)], NAF1: [(NAF_, 0)]})
     2035            sage: [NAF.process(w)[0] for w in [[0], [0, 1], [1, 1], [0, 1, 0, 1],
     2036            ....: [0, 1, 1, 1, 0], [1, 0, 0, 1, 1]]]
     2037            [True, True, False, True, False, False]
     2038
     2039        """
     2040        it = self.iter_process(*args, **kwargs)
     2041        for _ in it:
     2042            pass
     2043        return (it.accept_input, it.current_state, it.output_tape)
     2044
     2045
     2046    def iter_process(self, input_tape=None, initial_state=None):
     2047        """
     2048        See `process` for more informations.
     2049
     2050        EXAMPLES::
     2051
     2052            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     2053            ....:     initial_states=['A'], final_states=['A'])
     2054            sage: it = inverter.iter_process(input_tape=[0, 1, 1])
     2055            sage: for _ in it:
     2056            ....:     pass
     2057            sage: it.output_tape
     2058            [1, 0, 0]
     2059        """
     2060        return FSMProcessIterator(self, input_tape, initial_state)
     2061
     2062
     2063    #*************************************************************************
     2064    # change finite state machine (add/remove state/transitions)
     2065    #*************************************************************************
     2066
     2067
     2068    def add_state(self, state):
     2069        """
     2070        Adds a state to the finite state machine and returns the new
     2071        state. If the state already exists, that existing state is
     2072        returned.
     2073
     2074        ``state`` is either an instance of FSMState or, otherwise, a
     2075        label of a state.
     2076
     2077        EXAMPLES::
     2078
     2079            sage: F = FiniteStateMachine()
     2080            sage: A = FSMState('A', is_initial=True)
     2081            sage: F.add_state(A)
     2082            State 'A'
     2083        """
     2084        try:
     2085            return self.state(state)
     2086        except LookupError:
     2087            pass
     2088        # at this point we know that we have a new state
     2089        if is_FSMState(state):
     2090            s = state
     2091        else:
     2092            s = FSMState(state)
     2093        s.transitions = list()
     2094        self._states_.append(s)
     2095        try:
     2096            self._states_dict_[s.label()] = s
     2097        except AttributeError:
     2098            pass
     2099        return s
     2100
     2101
     2102    def add_states(self, states):
     2103        """
     2104        Adds several states. See add_state for more information.
     2105
     2106        EXAMPLES::
     2107
     2108            sage: F = FiniteStateMachine()
     2109            sage: F.add_states(['A', 'B'])
     2110            sage: F.states()
     2111            [State 'A', State 'B']
     2112        """
     2113        for state in states:
     2114            self.add_state(state)
     2115
     2116
     2117    def add_transition(self, *args, **kwargs):
     2118        """
     2119        Adds a transition to the finite state machine and returns the
     2120        new transition. If the transition already exists, that
     2121        existing transition is returned.
     2122
     2123        INPUT:
     2124
     2125        The following forms are all accepted:
     2126
     2127        ::
     2128
     2129            sage: A = FSMState('A')
     2130            sage: B = FSMState('B')
     2131
     2132            sage: FSM = FiniteStateMachine()
     2133            sage: FSM.add_transition(FSMTransition(A, B, 0, 1))
     2134            Transition from State 'A' to State 'B': 0|1
     2135
     2136            sage: FSM = FiniteStateMachine()
     2137            sage: FSM.add_transition(A, B, 0, 1)
     2138            Transition from State 'A' to State 'B': 0|1
     2139
     2140            sage: FSM = FiniteStateMachine()
     2141            sage: FSM.add_transition(A, B, word_in=0, word_out=1)
     2142            Transition from State 'A' to State 'B': 0|1
     2143
     2144            sage: FSM = FiniteStateMachine()
     2145            sage: FSM.add_transition('A', 'B', {'word_in': 0, 'word_out': 1})
     2146            Transition from State 'A' to State 'B': {'word_in': 0, 'word_out': 1}|-
     2147
     2148            sage: FSM = FiniteStateMachine()
     2149            sage: FSM.add_transition(from_state=A, to_state=B,
     2150            ....:                    word_in=0, word_out=1)
     2151            Transition from State 'A' to State 'B': 0|1
     2152
     2153            sage: FSM = FiniteStateMachine()
     2154            sage: FSM.add_transition({'from_state': A, 'to_state': B,
     2155            ....:                    'word_in': 0, 'word_out': 1})
     2156            Transition from State 'A' to State 'B': 0|1
     2157
     2158            sage: FSM = FiniteStateMachine()
     2159            sage: FSM.add_transition((A, B, 0, 1))
     2160            Transition from State 'A' to State 'B': 0|1
     2161
     2162            sage: FSM = FiniteStateMachine()
     2163            sage: FSM.add_transition([A, B, 0, 1])
     2164            Transition from State 'A' to State 'B': 0|1
     2165
     2166        If the states ``A`` and ``B`` are not instances of FSMState, then
     2167        it is assumed that they are labels of states.
     2168        """
     2169        if len(args) + len(kwargs) == 0:
     2170            return
     2171        if len(args) + len(kwargs) == 1:
     2172            if len(args) == 1:
     2173                d = args[0]
     2174                if is_FSMTransition(d):
     2175                    return self._add_fsm_transition_(d)
     2176            else:
     2177                d = kwargs.itervalues().next()
     2178            if hasattr(d, 'iteritems'):
     2179                args = []
     2180                kwargs = d
     2181            elif hasattr(d, '__iter__'):
     2182                args = d
     2183                kwargs = {}
     2184            else:
     2185                raise TypeError, "Cannot decide what to do with input."
     2186
     2187        data = dict(zip(
     2188                ('from_state', 'to_state', 'word_in', 'word_out', 'hook'),
     2189                args))
     2190        data.update(kwargs)
     2191
     2192        data['from_state'] = self.add_state(data['from_state'])
     2193        data['to_state'] = self.add_state(data['to_state'])
     2194
     2195        return self._add_fsm_transition_(FSMTransition(**data))
     2196
     2197
     2198    def _add_fsm_transition_(self, t):
     2199        """
     2200        Adds a transition ``t``, which is an instance of ``FSMTransition``.
     2201
     2202        TESTS::
     2203
     2204            sage: F = FiniteStateMachine()
     2205            sage: F._add_fsm_transition_(FSMTransition('A', 'B'))
     2206            Transition from State 'A' to State 'B': -|-
     2207        """
     2208        try:
     2209            return self.transition(t)
     2210        except LookupError:
     2211            pass
     2212        from_state = self.add_state(t.from_state)
     2213        to_state = self.add_state(t.to_state)
     2214        from_state.transitions.append(t)
     2215        return t
     2216
     2217
     2218    def add_from_transition_function(self, function, initial_states=None,
     2219                                     ignore_existing_states=False):
     2220        """
     2221        Constructs a finite state machine from a transition function.
     2222
     2223        - ``function`` may return a tuple (new_state, output_word) or a
     2224          list of such tuples.
     2225        - If no initial states are given, the already existing initial
     2226          states of self are taken.
     2227        - If ``ignore_existing_states`` is True, then already existing states
     2228          in self (e.g. already given final states) won't be processed.
     2229
     2230        EXAMPLES::
     2231
     2232            sage: F = FiniteStateMachine(initial_states=['A'],
     2233            ....:                        input_alphabet=[0, 1])
     2234            sage: def f(state, input):
     2235            ....:     return [('A', input), ('B', 1-input)]
     2236            sage: F.add_from_transition_function(f)
     2237            sage: F.transitions()
     2238            [Transition from State 'A' to State 'A': 0|0,
     2239            Transition from State 'A' to State 'B': 0|1,
     2240            Transition from State 'A' to State 'A': 1|1,
     2241            Transition from State 'A' to State 'B': 1|0,
     2242            Transition from State 'B' to State 'A': 0|0,
     2243            Transition from State 'B' to State 'B': 0|1,
     2244            Transition from State 'B' to State 'A': 1|1,
     2245            Transition from State 'B' to State 'B': 1|0]
     2246
     2247        TEST::
     2248
     2249            sage: F = FiniteStateMachine(initial_states=['A'])
     2250            sage: def f(state, input):
     2251            ....:     return [('A', input), ('B', 1-input)]
     2252            sage: F.add_from_transition_function(f)
     2253            Traceback (most recent call last):
     2254            ...
     2255            ValueError: No input alphabet is given.
     2256            Try calling determine_alphabets().
     2257        """
     2258        if self.input_alphabet is None:
     2259            raise ValueError, ("No input alphabet is given. "
     2260                               "Try calling determine_alphabets().")
     2261
     2262        if initial_states is None:
     2263            not_done = self.initial_states()
     2264        else:
     2265            not_done = copy(initial_states)
     2266        if len(not_done) == 0:
     2267            raise ValueError, "No state is initial."
     2268        if ignore_existing_states:
     2269            ignore_done = self.states()
     2270            for s in not_done:
     2271                try:
     2272                    ignore_done.remove(s)
     2273                except ValueError:
     2274                    pass
     2275        else:
     2276            ignore_done = []
     2277        while len(not_done) > 0:
     2278            s = not_done.pop(0)
     2279            for letter in self.input_alphabet:
     2280                try:
     2281                    return_value = function(s.label(), letter)
     2282                except LookupError:
     2283                    continue
     2284                if not hasattr(return_value, "pop"):
     2285                    return_value = [return_value]
     2286                try:
     2287                    for (st_label, word) in return_value:
     2288                        if not self.has_state(st_label):
     2289                            not_done.append(self.add_state(st_label))
     2290                        elif len(ignore_done) > 0:
     2291                            u = self.state(st_label)
     2292                            if u in ignore_done:
     2293                                not_done.append(u)
     2294                                ignore_done.remove(u)
     2295                        self.add_transition(s, st_label,
     2296                                            word_in=letter, word_out=word)
     2297                except TypeError:
     2298                    raise ValueError("The callback function for add_from_transition is expected to return a pair (new_state, output_label) or a list of such pairs. For the state %s and the input letter %s, it however returned %s, which is not acceptable." % (s.label(), letter, return_value))
     2299
     2300
     2301    def add_transitions_from_function(self, function, labels_as_input=True):
     2302        """
     2303        Adds a transition if ``function(state, state)`` says that there is one.
     2304
     2305        EXAMPLES::
     2306
     2307            sage: F = FiniteStateMachine()
     2308            sage: F.add_states(['A', 'B', 'C'])
     2309            sage: def f(state1, state2):
     2310            ....:     if state1 == 'C':
     2311            ....:         return None
     2312            ....:     return [0, 1]
     2313            sage: F.add_transitions_from_function(f)
     2314            sage: len(F.transitions())
     2315            6
     2316        """
     2317        for s_from in self.iter_states():
     2318            for s_to in self.iter_states():
     2319                if labels_as_input:
     2320                    t = function(s_from.label(), s_to.label())
     2321                else:
     2322                    t = function(s_from, s_to)
     2323                if hasattr(t, '__getitem__'):
     2324                    label_in = t[0]
     2325                    try:
     2326                        label_out = t[1]
     2327                    except LookupError:
     2328                        label_out = None
     2329                    self.add_transition(s_from, s_to, label_in, label_out)
     2330
     2331
     2332    def delete_transition(self, t):
     2333        """
     2334        Deletes a transition by removing it from the list of transitions of
     2335        the state, where the transition starts.
     2336
     2337        EXAMPLES::
     2338
     2339            sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)])
     2340            sage: F.delete_transition(('A', 'B', 0))
     2341            sage: F.transitions()
     2342            [Transition from State 'B' to State 'A': 1|-]
     2343        """
     2344        transition = self.transition(t)
     2345        transition.from_state.transitions.remove(transition)
     2346
     2347
     2348    def delete_state(self, s):
     2349        """
     2350        Deletes a state and all transitions coming or going to this state.
     2351        s has to be a label of a state or :class:`FSMState`.
     2352
     2353        EXAMPLES::
     2354
     2355            sage: t1 = FSMTransition('A', 'B', 0)
     2356            sage: t2 = FSMTransition('B', 'B', 1)
     2357            sage: F = FiniteStateMachine([t1, t2])
     2358            sage: F.delete_state('A')
     2359            sage: F. transitions()
     2360            [Transition from State 'B' to State 'B': 1|-]
     2361        """
     2362        state = self.state(s)
     2363        for transition in self.transitions():
     2364            if transition.to_state == state:
     2365                self.delete_transition(transition)
     2366        self._states_.remove(state)
     2367
     2368
     2369    def remove_epsilon_transitions(self):
     2370        """
     2371        TESTS::
     2372
     2373            sage: FiniteStateMachine().remove_epsilon_transitions()
     2374            Traceback (most recent call last):
     2375            ...
     2376            NotImplementedError
     2377        """
     2378        raise NotImplementedError
     2379
     2380
     2381    def accessible_components(self):
     2382        """
     2383        Returns a new finite state machine with the accessible states
     2384        of self and all transitions between those states.
     2385
     2386        A state is accessible if there is a directed path from an
     2387        initial state to the state. If self has no initial states then
     2388        a copy of the finite state machine self is returned.
     2389
     2390        EXAMPLES::
     2391
     2392            sage: F = Automaton([(0, 0, 1), (0, 0, 1), (1, 1, 0), (1, 0, 1)],
     2393            ....:               initial_states=[0])
     2394            sage: F.accessible_components()
     2395            finite state machine with 1 states
     2396        """
     2397        if len(self.initial_states()) == 0:
     2398            return deepcopy(self)
     2399
     2400        memo = {}
     2401        def accessible(sf, read):
     2402            trans = filter(lambda x: x.word_in[0] == read,
     2403                           self.transitions(sf))
     2404            return map(lambda x: (deepcopy(x.to_state, memo), x.word_out),
     2405                       trans)
     2406
     2407        return FiniteStateMachine(
     2408            accessible,
     2409            initial_states=map(lambda x: deepcopy(x, memo),
     2410                               self.initial_states()),
     2411            final_states=map(lambda x: deepcopy(x, memo),
     2412                             self.final_states()),
     2413            input_alphabet=deepcopy(self.input_alphabet, memo))
     2414
     2415
     2416    # *************************************************************************
     2417    # creating new finite state machines
     2418    # *************************************************************************
     2419
     2420
     2421    def disjoint_union(self, other):
     2422        """
     2423        TESTS::
     2424
     2425            sage: F = FiniteStateMachine([('A', 'A')])
     2426            sage: FiniteStateMachine().disjoint_union(F)
     2427            Traceback (most recent call last):
     2428            ...
     2429            NotImplementedError
     2430        """
     2431        raise NotImplementedError
     2432
     2433
     2434    def concatenation(self, other):
     2435        """
     2436        TESTS::
     2437
     2438            sage: F = FiniteStateMachine([('A', 'A')])
     2439            sage: FiniteStateMachine().concatenation(F)
     2440            Traceback (most recent call last):
     2441            ...
     2442            NotImplementedError
     2443        """
     2444        raise NotImplementedError
     2445
     2446
     2447    def Kleene_closure(self):
     2448        """
     2449        TESTS::
     2450
     2451            sage: FiniteStateMachine().Kleene_closure()
     2452            Traceback (most recent call last):
     2453            ...
     2454            NotImplementedError
     2455        """
     2456        raise NotImplementedError
     2457
     2458
     2459    def intersection(self, other):
     2460        """
     2461        TESTS::
     2462
     2463            sage: F = FiniteStateMachine([('A', 'A')])
     2464            sage: FiniteStateMachine().intersection(F)
     2465            Traceback (most recent call last):
     2466            ...
     2467            NotImplementedError
     2468        """
     2469        raise NotImplementedError
     2470
     2471
     2472    def product_FiniteStateMachine(self, other, function,
     2473                                   new_input_alphabet=None,
     2474                                   only_accessible_components=True):
     2475        """
     2476        Returns a new finite state machine whose states are
     2477        pairs of states of the original finite state machines.
     2478        The labels of the transitions are defined by ``function``.
     2479
     2480        ``function`` has to accept two transitions from `A` to `B` and
     2481        `C` to `D` and returns a pair ``(word_in, word_out)`` which is
     2482        the label of the transition `(A, C)` to `(B, D)`. If there is
     2483        no transition from `(A, C)` to `(B, D)`, then ``function``
     2484        raises a ``LookupError``.
     2485
     2486        EXAMPLES::
     2487
     2488            sage: F = Automaton([('A', 'B', 1), ('A', 'A', 0), ('B', 'A', 2)],
     2489            ....:               initial_states=['A'], final_states=['B'],
     2490            ....:               determine_alphabets=True)
     2491            sage: G = Automaton([(1, 1, 1)], initial_states=[1], final_states=[1])
     2492            sage: def addition(transition1, transition2):
     2493            ....:     return (transition1.word_in[0] + transition2.word_in[0],
     2494            ....:             None)
     2495            sage: H = F.product_FiniteStateMachine(G, addition, [0, 1, 2, 3])
     2496            sage: H.transitions()
     2497            [Transition from State ('A', 1) to State ('A', 1): 1|-,
     2498             Transition from State ('A', 1) to State ('B', 1): 2|-,
     2499             Transition from State ('B', 1) to State ('A', 1): 3|-]
     2500        """
     2501        if new_input_alphabet is None:
     2502            new_input_alphabet = self.input_alphabet
     2503
     2504        result = FiniteStateMachine(input_alphabet=new_input_alphabet)
     2505
     2506        for transition1 in self.transitions():
     2507            for transition2 in other.transitions():
     2508                try:
     2509                    word = function(transition1, transition2)
     2510                except LookupError:
     2511                    continue
     2512                result.add_transition((transition1.from_state.label(),
     2513                                       transition2.from_state.label()),
     2514                                      (transition1.to_state.label(),
     2515                                       transition2.to_state.label()),
     2516                                      word[0], word[1])
     2517
     2518        for state1 in self.initial_states():
     2519            for state2 in other.initial_states():
     2520                result.state((state1.label(), state2.label())).is_initial = True
     2521
     2522        for state1 in self.final_states():
     2523            for state2 in other.final_states():
     2524                result.state((state1.label(), state2.label())).is_final = True
     2525
     2526        if only_accessible_components:
     2527            return result.accessible_components()
     2528        else:
     2529            return result
     2530
     2531
     2532    def cartesian_product(self, other, only_accessible_components=True):
     2533        """
     2534        Returns a new finite state machine, which is the cartesian
     2535        product of self and other.
     2536
     2537        The set of states of the new automaton is the cartesian
     2538        product of the set of states of both given automata. There is
     2539        a transition `((A, B), (C, D), a)` in the new automaton if
     2540        there are transitions `(A, C, a)` and `(B, C, a)` in the old
     2541        automata.
     2542
     2543        EXAMPLES::
     2544
     2545            sage: aut1 = Automaton([('1', '2', 1), ('2', '2', 1), ('2', '2', 0)],
     2546            ....:                initial_states=['1'], final_states=['2'],
     2547            ....:                determine_alphabets=True)
     2548            sage: aut2 = Automaton([('A', 'A', 1), ('A', 'B', 1),
     2549            ....:                 ('B', 'B', 0), ('B', 'A', 0)],
     2550            ....:                initial_states=['A'], final_states=['B'],
     2551            ....:                determine_alphabets=True)
     2552            sage: res = aut1.cartesian_product(aut2)
     2553            sage: res.transitions()
     2554            [Transition from State ('1', 'A') to State ('2', 'A'): 1|-,
     2555             Transition from State ('1', 'A') to State ('2', 'B'): 1|-,
     2556             Transition from State ('2', 'B') to State ('2', 'B'): 0|-,
     2557             Transition from State ('2', 'B') to State ('2', 'A'): 0|-,
     2558             Transition from State ('2', 'A') to State ('2', 'A'): 1|-,
     2559             Transition from State ('2', 'A') to State ('2', 'B'): 1|-]
     2560        """
     2561        def function(transition1, transition2):
     2562            if transition1.word_in == transition2.word_in \
     2563                    and transition1.word_out == transition2.word_out:
     2564                return (transition1.word_in, transition1.word_out)
     2565            else:
     2566                raise LookupError
     2567
     2568        return self.product_FiniteStateMachine(
     2569            other, function,
     2570            only_accessible_components = only_accessible_components)
     2571
     2572
     2573    def composition(self, other, algorithm=None,
     2574                    only_accessible_components=True):
     2575        """
     2576        Returns a new transducer which is the composition of self and
     2577        other.
     2578
     2579        INPUT:
     2580
     2581        - ``algorithm`` -- can be one of the following
     2582
     2583          - ``direct`` -- The composition is calculated directly.
     2584
     2585            There can be arbitrarily many initial and final states,
     2586            but the input and output labels must have length 1.
     2587
     2588            WARNING: The output of other is fed into self.
     2589
     2590          - ``explorative`` -- An explorative algorithm is used.
     2591
     2592            At least the following restrictions apply, but are not
     2593            checked:
     2594            - both self and other have exactly one initial state
     2595            - all input labels of transitions have length exactly 1
     2596
     2597            The input alphabet of self has to be specified.
     2598
     2599            This is a very limited implementation of composition.
     2600            WARNING: The output of ``other`` is fed into ``self``.
     2601
     2602          If algorithm is ``None``, then the algorithm is chosen
     2603          automatically (at the moment always ``direct``).
     2604
     2605
     2606        EXAMPLES::
     2607
     2608            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     2609            ....:                initial_states=['A', 'B'], final_states=['B'],
     2610            ....:                determine_alphabets=True)
     2611            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     2612            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2613            ....:                initial_states=[1], final_states=[2],
     2614            ....:                determine_alphabets=True)
     2615            sage: Hd = F.composition(G, algorithm='direct')
     2616            sage: Hd.initial_states()
     2617            [State (1, 'B'), State (1, 'A')]
     2618            sage: Hd.transitions()
     2619            [Transition from State (1, 'B') to State (1, 'A'): 1|1,
     2620             Transition from State (1, 'A') to State (2, 'B'): 0|0,
     2621             Transition from State (2, 'B') to State (2, 'A'): 0|1,
     2622             Transition from State (2, 'A') to State (2, 'B'): 1|0]
     2623
     2624        ::
     2625
     2626            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     2627            ....:                 ('B', 'B', 0, 0)],
     2628            ....:                initial_states=['A'], final_states=['B'])
     2629            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     2630            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     2631            ....:                initial_states=[1], final_states=[1])
     2632            sage: He = G.composition(F, algorithm='explorative')
     2633            sage: He.transitions()
     2634            [Transition from State ('A', 1) to State (None, None): 0|-,
     2635             Transition from State ('A', 1) to State ('B', 2): 1|0,1,
     2636             Transition from State (None, None) to State (None, None): 0|-,
     2637             Transition from State (None, None) to State (None, None): 1|-,
     2638             Transition from State ('B', 2) to State ('B', 2): 0|1,
     2639             Transition from State ('B', 2) to State ('B', 1): 1|1,
     2640             Transition from State ('B', 1) to State ('B', 1): 0|0,
     2641             Transition from State ('B', 1) to State ('B', 2): 1|0]
     2642
     2643        TESTS:
     2644
     2645        Due to the limitations of the two algorithms the following
     2646        (examples from above, but different algorithm used) does not
     2647        give a full answer or does not work
     2648
     2649        ::
     2650
     2651            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     2652            ....:                initial_states=['A', 'B'], final_states=['B'],
     2653            ....:                determine_alphabets=True)
     2654            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     2655            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2656            ....:                initial_states=[1], final_states=[2],
     2657            ....:                determine_alphabets=True)
     2658            sage: He = F.composition(G, algorithm='explorative')
     2659            sage: He.initial_states()
     2660            [State (1, 'A')]
     2661            sage: He.transitions()
     2662            [Transition from State (1, 'A') to State (2, 'B'): 0|0,
     2663             Transition from State (1, 'A') to State (None, None): 1|-,
     2664             Transition from State (2, 'B') to State (2, 'A'): 0|1,
     2665             Transition from State (2, 'B') to State (None, None): 1|-,
     2666             Transition from State (None, None) to State (None, None): 0|-,
     2667             Transition from State (None, None) to State (None, None): 1|-,
     2668             Transition from State (2, 'A') to State (None, None): 0|-,
     2669             Transition from State (2, 'A') to State (2, 'B'): 1|0]
     2670
     2671        ::
     2672
     2673            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     2674            ....:                 ('B', 'B', 0, 0)],
     2675            ....:                initial_states=['A'], final_states=['B'])
     2676            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     2677            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     2678            ....:                initial_states=[1], final_states=[1])
     2679            sage: Hd = G.composition(F, algorithm='direct')
     2680            Traceback (most recent call last):
     2681            ...
     2682            LookupError: No state with label ('A', 1) found.
     2683        """
     2684        if algorithm==None:
     2685            algorithm = 'direct'
     2686        if algorithm=='direct':
     2687            return self._composition_direct_(other, only_accessible_components)
     2688        elif algorithm=='explorative':
     2689            return self._composition_explorative_(other)
     2690        else:
     2691            raise ValueError, "Unknown algorithm %s." % (algorithm,)
     2692
     2693
     2694    def _composition_direct_(self, other, only_accessible_components=True):
     2695        """
     2696        See :meth:`.composition` for details.
     2697
     2698        TESTS::
     2699
     2700            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     2701            ....:                initial_states=['A', 'B'], final_states=['B'],
     2702            ....:                determine_alphabets=True)
     2703            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     2704            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2705            ....:                initial_states=[1], final_states=[2],
     2706            ....:                determine_alphabets=True)
     2707            sage: Hd = F._composition_direct_(G)
     2708            sage: Hd.initial_states()
     2709            [State (1, 'B'), State (1, 'A')]
     2710            sage: Hd.transitions()
     2711            [Transition from State (1, 'B') to State (1, 'A'): 1|1,
     2712             Transition from State (1, 'A') to State (2, 'B'): 0|0,
     2713             Transition from State (2, 'B') to State (2, 'A'): 0|1,
     2714             Transition from State (2, 'A') to State (2, 'B'): 1|0]
     2715
     2716        """
     2717        def function(transition1, transition2):
     2718            if transition1.word_out == transition2.word_in:
     2719                return (transition1.word_in, transition2.word_out)
     2720            else:
     2721                raise LookupError
     2722
     2723        return other.product_FiniteStateMachine(
     2724            self, function,
     2725            only_accessible_components = only_accessible_components)
     2726
     2727
     2728    def _composition_explorative_(self, other):
     2729        """
     2730        See :meth:`.composition` for details.
     2731
     2732        TESTS::
     2733
     2734            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     2735            ....:                 ('B', 'B', 0, 0)],
     2736            ....:                initial_states=['A'], final_states=['B'])
     2737            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     2738            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     2739            ....:                initial_states=[1], final_states=[1])
     2740            sage: He = G._composition_explorative_(F)
     2741            sage: He.transitions()
     2742            [Transition from State ('A', 1) to State (None, None): 0|-,
     2743             Transition from State ('A', 1) to State ('B', 2): 1|0,1,
     2744             Transition from State (None, None) to State (None, None): 0|-,
     2745             Transition from State (None, None) to State (None, None): 1|-,
     2746             Transition from State ('B', 2) to State ('B', 2): 0|1,
     2747             Transition from State ('B', 2) to State ('B', 1): 1|1,
     2748             Transition from State ('B', 1) to State ('B', 1): 0|0,
     2749             Transition from State ('B', 1) to State ('B', 2): 1|0]
     2750
     2751        TODO:
     2752
     2753        The explorative algorithm should be re-implemented using the
     2754        process iterators of both finite state machines.
     2755        """
     2756        def composition_transition(state, input):
     2757            (state1, state2) = state
     2758            if state1 is None and state2 is None:
     2759                return ((None, None), [])
     2760            transition1 = None
     2761            for transition in other.iter_transitions(state1):
     2762                if transition.word_in == [input]:
     2763                    transition1 = transition
     2764                    break
     2765            if transition1 is None:
     2766                return((None, None), [])
     2767            new_state1 = transition1.to_state.label()
     2768            new_state2 = state2
     2769            output = []
     2770            for o in transition1.word_out:
     2771                transition2 = None
     2772                for transition in self.iter_transitions(new_state2):
     2773                    if transition.word_in == [o]:
     2774                        transition2 = transition
     2775                        break
     2776                if transition2 is None:
     2777                    return((None, None), [])
     2778                new_state2 = transition2.to_state.label()
     2779                output += transition2.word_out
     2780            return ((new_state1, new_state2), output)
     2781
     2782        F = FiniteStateMachine(input_alphabet=other.input_alphabet,
     2783                               data=composition_transition,
     2784                               initial_states=[(other.initial_states()[0].label(), self.initial_states()[0].label())])
     2785
     2786        for state1 in other.final_states():
     2787            for state2 in self.final_states():
     2788                F.state((state1.label(), state2.label())).is_final = True
     2789        return F
     2790
     2791
     2792    def input_projection(self):
     2793        """
     2794        Returns an automaton where the output of each transition of
     2795        self is deleted.
     2796
     2797        EXAMPLES::
     2798
     2799            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     2800            ....:                         ('B', 'B', 1, 0)])
     2801            sage: G = F.input_projection()
     2802            sage: G.transitions()
     2803            [Transition from State 'A' to State 'B': 0|-,
     2804             Transition from State 'A' to State 'A': 1|-,
     2805             Transition from State 'B' to State 'B': 1|-]
     2806        """
     2807        return self.projection(what='input')
     2808
     2809
     2810    def output_projection(self):
     2811        """
     2812        Returns a automaton where the input of each transition of self
     2813        is deleted and the new input is the original output.
     2814
     2815        EXAMPLES::
     2816
     2817            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     2818            ....:                         ('B', 'B', 1, 0)])
     2819            sage: G = F.output_projection()
     2820            sage: G.transitions()
     2821            [Transition from State 'A' to State 'B': 1|-,
     2822             Transition from State 'A' to State 'A': 1|-,
     2823             Transition from State 'B' to State 'B': 0|-]
     2824        """
     2825        return self.projection(what='output')
     2826
     2827
     2828    def projection(self, what='input'):
     2829        """
     2830        Returns an Automaton which transition labels are the projection
     2831        of the transition labels of the input.
     2832
     2833        EXAMPLES::
     2834
     2835            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     2836            ....:                         ('B', 'B', 1, 0)])
     2837            sage: G = F.projection(what='output')
     2838            sage: G.transitions()
     2839            [Transition from State 'A' to State 'B': 1|-,
     2840             Transition from State 'A' to State 'A': 1|-,
     2841             Transition from State 'B' to State 'B': 0|-]
     2842        """
     2843        new = Automaton()
     2844
     2845        state_mapping = {}
     2846        for state in self.iter_states():
     2847            state_mapping[state] = new.add_state(deepcopy(state))
     2848        for transition in self.iter_transitions():
     2849            if what == 'input':
     2850                new_word_in = transition.word_in
     2851            elif what == 'output':
     2852                new_word_in = transition.word_out
     2853            else:
     2854                raise NotImplementedError
     2855            new.add_transition((state_mapping[transition.from_state],
     2856                                state_mapping[transition.to_state],
     2857                                new_word_in, None))
     2858        if what == 'input':
     2859            new.input_alphabet = self.input_alphabet
     2860        elif what == 'output':
     2861            new.input_alphabet = self.output_alphabet
     2862        else:
     2863            raise NotImplementedError
     2864        return new
     2865
     2866
     2867    def determinisation(self):
     2868        """
     2869        Returns a deterministic automaton which accepts the same input
     2870        words as the original one.
     2871
     2872        The input alphabet must be specified. It is restricted to nice
     2873        cases: only ``mode=automaton`` is considered, input words have to
     2874        have length at most `1`.
     2875
     2876        EXAMPLES::
     2877
     2878            sage: aut = Automaton([('A', 'A', 0), ('A', 'B', 1), ('B', 'B', 1)],
     2879            ....:                 initial_states=['A'], final_states=['B'])
     2880            sage: aut.determinisation().transitions()
     2881            [Transition from State frozenset(['A'])
     2882                          to State frozenset(['A']): 0|-,
     2883            Transition from State frozenset(['A'])
     2884                          to State frozenset(['B']): 1|-,
     2885             Transition from State frozenset(['B'])
     2886                          to State frozenset([]): 0|-,
     2887             Transition from State frozenset(['B'])
     2888                          to State frozenset(['B']): 1|-,
     2889             Transition from State frozenset([])
     2890                          to State frozenset([]): 0|-,
     2891             Transition from State frozenset([])
     2892                          to State frozenset([]): 1|-]
     2893
     2894        ::
     2895
     2896            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     2897            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     2898            ....:               initial_states=['A'], final_states=['C'])
     2899            sage: A.determinisation().states()
     2900            [State frozenset(['A']), State frozenset(['A', 'B']),
     2901            State frozenset(['A', 'C']), State frozenset(['A', 'C', 'B'])]
     2902        """
     2903        assert self.mode == 'automaton'
     2904        for transition in self.transitions():
     2905            assert len(transition.word_in) <= 1, "%s has input label of length > 1, which we cannot handle" % (transition,)
     2906
     2907        epsilon_successors = {}
     2908        direct_epsilon_successors = {}
     2909        for state in self.states():
     2910            direct_epsilon_successors[state.label()] = set(map(lambda t:t.to_state.label(), filter(lambda transition: len(transition.word_in) == 0, self.transitions(state))))
     2911            epsilon_successors[state.label()] = set([state.label()])
     2912
     2913        old_count_epsilon_successors = 0
     2914        count_epsilon_successors = len(epsilon_successors)
     2915
     2916        while old_count_epsilon_successors < count_epsilon_successors:
     2917            old_count_epsilon_successors = count_epsilon_successors
     2918            count_epsilon_successors = 0
     2919            for state in self.states():
     2920                for direct_successor in direct_epsilon_successors[state.label()]:
     2921                    epsilon_successors[state.label()] = epsilon_successors[state.label()].union(epsilon_successors[direct_successor])
     2922                count_epsilon_successors += len(epsilon_successors[state.label()])
     2923
     2924
     2925        def set_transition(states, letter):
     2926            result = set()
     2927            for state in states:
     2928                for transition in self.transitions(state):
     2929                    if transition.word_in == [letter]:
     2930                        result.add(transition.to_state.label())
     2931            result = result.union(*map(lambda s:epsilon_successors[s], result))
     2932            return (frozenset(result), [])
     2933
     2934        result = Automaton(input_alphabet=self.input_alphabet,
     2935                           initial_states=[ frozenset([state.label() for state in self.initial_states()])],
     2936                          data=set_transition )
     2937
     2938        for state in result.states():
     2939            if state.label().intersection(map(lambda s: s.label(), self.final_states())):
     2940                state.is_final = True
     2941
     2942        return result
     2943
     2944
     2945    def minimization(self, algorithm=None):
     2946        """
     2947        Returns the minimization of the input automaton as a new automaton.
     2948
     2949        The resulting automaton is deterministic and has a minimal
     2950        number of states. Either Moore's algorithm is used (default or
     2951        ``algorithm='Moore'``), or Brzozowski's algorithm when
     2952        ``algorithm='Brzozowski'``.  Only ``mode=automaton`` is
     2953        accepted. Use :meth:`.simplification` for transducers.
     2954
     2955        EXAMPLES::
     2956
     2957            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     2958            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     2959            ....:               initial_states=['A'], final_states=['C'])
     2960            sage: B = A.minimization(algorithm='Brzozowski')
     2961            sage: B.transitions(B.states()[1])
     2962            [Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2963            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2964            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2965            frozenset(['A', 'C']), frozenset(['C'])]): 0|-,
     2966            Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2967            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2968            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2969            frozenset(['A', 'C'])]): 1|-]
     2970            sage: len(B.states())
     2971            3
     2972            sage: C = A.minimization(algorithm='Brzozowski')
     2973            sage: C.transitions(C.states()[1])
     2974            [Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2975            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2976            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2977            frozenset(['A', 'C']), frozenset(['C'])]): 0|-,
     2978            Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2979            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2980            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2981            frozenset(['A', 'C'])]): 1|-]
     2982            sage: len(C.states())
     2983            3
     2984
     2985        ::
     2986
     2987            sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'),
     2988            ....:                  ('3', '2', 'a'), ('2', '1', 'b'),
     2989            ....:                  ('3', '4', 'a'), ('4', '3', 'b')],
     2990            ....:                  initial_states=['1'], final_states=['1'])
     2991            sage: min = aut.minimization(algorithm='Brzozowski')
     2992            sage: [len(min.states()), len(aut.states())]
     2993            [3, 4]
     2994            sage: min = aut.minimization(algorithm='Moore')
     2995            Traceback (most recent call last):
     2996            ...
     2997            NotImplementedError: Minimization via Moore's Algorithm is only
     2998            implemented for deterministic finite state machines
     2999        """
     3000        if self.mode is None:
     3001            raise NotImplementedError, "The mode attribute must be set."
     3002        if self.mode == 'transducer':
     3003            raise NotImplementedError, "Minimization for Transducer is not implemented. Try the simplification method."
     3004        if not self.mode == 'automaton':
     3005            raise NotImplementedError
     3006
     3007        if algorithm is None or algorithm == "Moore":
     3008            return self._minimization_Moore_()
     3009        elif algorithm == "Brzozowski":
     3010            return self._minimization_Brzozowski_()
     3011        else:
     3012            raise NotImplementedError, "Algorithm '%s' is not implemented. Choose 'Moore' or 'Brzozowski'" % algorithm
     3013
     3014
     3015    def _minimization_Brzozowski_(self):
     3016        """
     3017        Returns a minimized automaton by using Brzozowski's algorithm.
     3018
     3019        See also :meth:`.minimization`.
     3020
     3021        TESTS::
     3022
     3023            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     3024            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     3025            ....:               initial_states=['A'], final_states=['C'])
     3026            sage: B = A._minimization_Brzozowski_()
     3027            sage: len(B.states())
     3028            3
     3029        """
     3030        return self.transposition().determinisation().transposition().determinisation()
     3031
     3032
     3033    def _minimization_Moore_(self):
     3034        """
     3035        Returns a minimized automaton by using Brzozowski's algorithm.
     3036
     3037        See also :meth:`.minimization`.
     3038
     3039        TESTS::
     3040
     3041            sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'),
     3042            ....:                  ('3', '2', 'a'), ('2', '1', 'b'),
     3043            ....:                  ('3', '4', 'a'), ('4', '3', 'b')],
     3044            ....:                  initial_states=['1'], final_states=['1'])
     3045            sage: min = aut._minimization_Moore_()
     3046            Traceback (most recent call last):
     3047            ...
     3048            NotImplementedError: Minimization via Moore's Algorithm is only
     3049            implemented for deterministic finite state machines
     3050        """
     3051        return self.quotient(self.equivalence_classes())
     3052
     3053
     3054    def transposition(self):
     3055        """
     3056        Returns a new finite state machine, where all transitions of the
     3057        input finite state machine are reversed.
     3058
     3059        EXAMPLES::
     3060
     3061            sage: aut=Automaton([('A', 'A', 0), ('A', 'A', 1), ('A', 'B', 0)],
     3062            ....:               initial_states=['A'], final_states=['B'])
     3063            sage: aut.transposition().transitions('B')
     3064            [Transition from State 'B' to State 'A': 0|-]
     3065
     3066        ::
     3067
     3068            sage: aut=Automaton([('1', '1', 1), ('1', '2', 0), ('2', '2', 0)],
     3069            ....:               initial_states=['1'], final_states=['1', '2'])
     3070            sage: aut.transposition().initial_states()
     3071            [State '1', State '2']
     3072        """
     3073        transposition=FiniteStateMachine(input_alphabet=self.input_alphabet,
     3074                                         mode=self.mode)
     3075        for state in self.states():
     3076            transposition.add_state(deepcopy(state))
     3077
     3078        for transition in self.transitions():
     3079            transposition.add_transition(
     3080                transition.to_state.label(), transition.from_state.label(),
     3081                transition.word_in, transition.word_out)
     3082
     3083        for initial in self.initial_states():
     3084            state=transposition.state(initial.label())
     3085            if not initial.is_final:
     3086                state.is_final=True
     3087                state.is_initial=False
     3088
     3089        for final in self.final_states():
     3090            state=transposition.state(final.label())
     3091            if not final.is_initial:
     3092                state.is_final=False
     3093                state.is_initial=True
     3094
     3095        return transposition
     3096
     3097
     3098    def split_transitions(self):
     3099        """
     3100        Returns a new transducer, where all transitions in self with input
     3101        labels consisting of more than one letter
     3102        are replaced by a path of the corresponding length.
     3103
     3104        EXAMPLES::
     3105
     3106            sage: A=Transducer([('A', 'B', [1, 2, 3], 0)],
     3107            ....:              initial_states=['A'], final_states=['B'])
     3108            sage: A.split_transitions().states()
     3109            [State (State 'A', ()), State (State 'B', ()),
     3110             State (State 'A', (1,)), State (State 'A', (1, 2))]
     3111        """
     3112        new=FiniteStateMachine(mode=self.mode,
     3113                               input_alphabet=self.input_alphabet,
     3114                               output_alphabet=self.output_alphabet)
     3115        for state in self.states():
     3116            new.add_state(FSMState((state, ()), is_initial=state.is_initial,
     3117                                   is_final=state.is_final))
     3118        for transition in self.transitions():
     3119            for j in range(len(transition.word_in)-1):
     3120                new.add_transition( (
     3121                        (transition.from_state, tuple(transition.word_in[:j])),
     3122                        (transition.from_state, tuple(transition.word_in[:j+1])),
     3123                        transition.word_in[j],
     3124                        []))
     3125            new.add_transition((
     3126                    (transition.from_state, tuple(transition.word_in[:-1])),
     3127                    (transition.to_state, ()),
     3128                    transition.word_in[-1:],
     3129                    transition.word_out))
     3130        return new
     3131
     3132
     3133    # *************************************************************************
     3134    # simplifications
     3135    # *************************************************************************
     3136
     3137
     3138    def prepone_output(self):
     3139        """
     3140        Apply the following to each state `s` (except initial and
     3141        final states) of the finite state machine as often as
     3142        possible:
     3143
     3144        If the letter a is prefix of the output label of all
     3145        transitions from `s`, then remove it from all these labels and
     3146        append it to all output labels of all transitions leading to
     3147        `s`.
     3148
     3149        We assume that the states have no output labels.
     3150
     3151        EXAMPLES::
     3152
     3153            sage: A = Transducer([('A', 'B', 1, 1), ('B', 'B', 0, 0), ('B', 'C', 1, 0)],
     3154            ....:                initial_states=['A'], final_states=['C'])
     3155            sage: A.prepone_output()
     3156            sage: A.transitions()
     3157            [Transition from State 'A' to State 'B': 1|1,0,
     3158             Transition from State 'B' to State 'B': 0|0,
     3159             Transition from State 'B' to State 'C': 1|-]
     3160
     3161        ::
     3162
     3163            sage: B = Transducer([('A', 'B', 0, 1), ('B', 'C', 1, [1, 1]), ('B', 'C', 0, 1)],
     3164            ....:                initial_states=['A'], final_states=['C'])
     3165            sage: B.prepone_output()
     3166            sage: B.transitions()
     3167            [Transition from State 'A' to State 'B': 0|1,1,
     3168             Transition from State 'B' to State 'C': 1|1,
     3169             Transition from State 'B' to State 'C': 0|-]
     3170        """
     3171        def find_common_output(state):
     3172            if len(filter(lambda transition: len(transition.word_out) == 0, self.transitions(state))) > 0:
     3173                return ()
     3174            first_letters = set(map(lambda transition: transition.word_out[0], self.transitions(state)))
     3175            if len(first_letters) == 1:
     3176                return (first_letters.pop(),)
     3177            return ()
     3178
     3179        changed = 1
     3180        iteration = 0
     3181        while changed > 0:
     3182            changed = 0
     3183            iteration += 1
     3184            for state in self.states():
     3185                if state.is_initial or state.is_final:
     3186                    continue
     3187                assert len(state.word_out) == 0, \
     3188                    "prepone_output assumes that all states have empty output word, but state %s has output word %s" % \
     3189                    (state, state.word_out)
     3190                common_output = find_common_output(state)
     3191                if len(common_output) > 0:
     3192                    changed += 1
     3193                    for transition in self.transitions(state):
     3194                        assert transition.word_out[0] == common_output[0]
     3195                        transition.word_out = transition.word_out[1:]
     3196                    for transition in self.transitions():
     3197                        if transition.to_state == state:
     3198                            transition.word_out.append(common_output[0])
     3199
     3200
     3201    def equivalence_classes(self):
     3202        """
     3203        Two states `a` and `b` are equivalent, if and only if for each
     3204        input label word_in the following holds:
     3205
     3206        For paths `p_a` from `a` to `a'` with input label ``word_in``
     3207        and output label ``word_out_a`` and `p_b` from `b` to `b'`
     3208        with input label ``word_in`` and output label ``word_out_b``,
     3209        we have ``word_out_a=word_out_b``, `a'` and `b'` have the same
     3210        output label and are both final or both non-final.
     3211
     3212        The function :meth:`.equivalence_classes` returns a list of
     3213        the equivalence classes to this equivalence relation.
     3214
     3215        This is one step of Moore's minimization algorithm.
     3216
     3217        .. SEEALSO::
     3218
     3219            :meth:`.minimization`
     3220
     3221        EXAMPLES::
     3222
     3223            sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0),
     3224            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     3225            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     3226            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     3227            sage: fsm.equivalence_classes()
     3228            [[State 'B', State 'D'], [State 'A', State 'C']]
     3229        """
     3230
     3231        # Two states a and b are said to be 0-equivalent, if their output
     3232        # labels agree and if they are both final or non-final.
     3233        #
     3234        # For some j >= 1, two states a and b are said to be j-equivalent, if
     3235        # they are j-1 equivalent and if for each element letter letter_in of
     3236        # the input alphabet and transitions t_a from a with input label
     3237        # letter_in, output label word_out_a to a' and t_b from b with input
     3238        # label letter_in, output label word_out_b to b', we have
     3239        # word_out_a=word_out_b and a' and b' are j-1 equivalent.
     3240
     3241        # If for some j the relations j-1 equivalent and j-equivalent
     3242        # coincide, then they are equal to the equivalence relation described
     3243        # in the docstring.
     3244
     3245        # classes_current holds the equivalence classes of j-equivalence,
     3246        # classes_previous holds the equivalence classes of j-1 equivalence.
     3247
     3248        if not self.is_deterministic():
     3249            raise NotImplementedError, "Minimization via Moore's Algorithm is only implemented for deterministic finite state machines"
     3250
     3251        # initialize with 0-equivalence
     3252        classes_previous=[]
     3253        key_0=lambda state: (state.is_final, state.word_out)
     3254        states_sorted=sorted( self.states(), key=key_0)
     3255        states_grouped= itertools.groupby( states_sorted, key=key_0)
     3256        classes_current=[ list(equivalence_class) for
     3257                          (key,equivalence_class) in states_grouped ]
     3258
     3259        while len(classes_current)!=len(classes_previous):
     3260            class_of={}
     3261            classes_previous=classes_current
     3262            classes_current=[]
     3263
     3264            for k in range(len(classes_previous)):
     3265                for state in classes_previous[k]:
     3266                    class_of[state]=k
     3267
     3268            key_current=lambda state: sorted(
     3269                [ (transition.word_in,
     3270                   transition.word_out,
     3271                   class_of[transition.to_state])
     3272                  for transition in state.transitions ]
     3273                )
     3274
     3275            for class_previous in classes_previous:
     3276                states_sorted = sorted( class_previous, key=key_current)
     3277                states_grouped = itertools.groupby( states_sorted, key=key_current)
     3278                classes_current.extend([ list(equivalence_class) for
     3279                                       (key,equivalence_class) in states_grouped ])
     3280
     3281        return classes_current
     3282
     3283
     3284    def quotient(self, classes):
     3285        """
     3286        Constructs the quotient with respect to the equivalence
     3287        classes.
     3288
     3289        ``classes`` is a list of equivalence classes of states.
     3290
     3291        Assume that `c` is a class and `s`, `s'` are states in `c`. If
     3292        there is a transition from `s` to some `t` with input label
     3293        ``word_in`` and output label ``word_out``, then there has to
     3294        be a transition from `s'` to some `t'` with input label
     3295        ``word_in`` and output label ``word_out`` such that `s'` and
     3296        `t'` are states of the same class `c'`. Then there is a
     3297        transition from `c` to `c'` in the quotient with input label
     3298        ``word_in`` and output label ``word_out``.
     3299
     3300        Non-initial states may be merged with initial states, the
     3301        resulting state is an initial state.
     3302
     3303        All states in a class must have the same ``is_final`` and
     3304        ``word_out`` values.
     3305
     3306        EXAMPLES::
     3307
     3308            sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0),
     3309            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     3310            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     3311            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     3312            sage: fsmq = fsm.quotient([[fsm.state("A"), fsm.state("C")],
     3313            ....:                      [fsm.state("B"), fsm.state("D")]])
     3314            sage: fsmq.transitions()
     3315            [Transition from State (State 'A', State 'C')
     3316                          to State (State 'B', State 'D'): 0|1,
     3317             Transition from State (State 'A', State 'C')
     3318                          to State (State 'B', State 'D'): 1|0,
     3319             Transition from State (State 'B', State 'D')
     3320                          to State (State 'A', State 'C'): 0|0,
     3321             Transition from State (State 'B', State 'D')
     3322                          to State (State 'A', State 'C'): 1|1]
     3323            sage: fsmq.relabeled().transitions()
     3324            [Transition from State 0 to State 1: 0|1,
     3325             Transition from State 0 to State 1: 1|0,
     3326             Transition from State 1 to State 0: 0|0,
     3327             Transition from State 1 to State 0: 1|1]
     3328            sage: fsmq1 = fsm.quotient(fsm.equivalence_classes())
     3329            sage: fsmq1 == fsmq
     3330            True
     3331            sage: fsm.quotient([[fsm.state("A"), fsm.state("B"), fsm.state("C"), fsm.state("D")]])
     3332            Traceback (most recent call last):
     3333                ...
     3334            ValueError: There is a transition Transition from State 'B' to State 'C': 0|0 in the original transducer, but no corresponding transition in the new transducer.
     3335        """
     3336        new = FiniteStateMachine()
     3337        new.mode = self.mode
     3338        new.input_alphabet = self.input_alphabet
     3339        new.output_alphabet = self.output_alphabet
     3340        state_mapping = {}
     3341
     3342        # Create new states and build state_mapping
     3343        for c in classes:
     3344            new_state = new.add_state(tuple(c))
     3345            for state in c:
     3346                state_mapping[state] = new_state
     3347
     3348        # Copy data from old transducer
     3349        for c in classes:
     3350            new_state = state_mapping[c[0]]
     3351            # copy all data from first class member
     3352            new_state.is_initial = c[0].is_initial
     3353            new_state.is_final = c[0].is_final
     3354            new_state.word_out = c[0].word_out
     3355            for transition in self.iter_transitions(c[0]):
     3356                new_transition = new.add_transition(
     3357                    from_state=new_state,
     3358                    to_state = state_mapping[transition.to_state],
     3359                    word_in  = transition.word_in,
     3360                    word_out = transition.word_out)
     3361
     3362            # check that all class members have the same information (modulo classes)
     3363            for state in c:
     3364                new_state.is_initial = new_state.is_initial or state.is_initial
     3365                assert new_state.is_final == state.is_final, "Class %s mixes final and non-final states" % (c,)
     3366                assert new_state.word_out == state.word_out, "Class %s mixes different word_out" % (c,)
     3367                assert len(self.transitions(state)) == len(new.transitions(new_state)), \
     3368                    "Class %s has %d outgoing transitions, but class member %s has %d outgoing transitions" %  \
     3369                    (c, len(new.transitions(new_state)), state, len(self.transitions(state)))
     3370                for transition in self.transitions(state):
     3371                    try:
     3372                        new_transition = new.transition((new_state, state_mapping[transition.to_state], transition.word_in, transition.word_out))
     3373                    except LookupError:
     3374                        raise ValueError, "There is a transition %s in the original transducer, but no corresponding transition in the new transducer." % transition
     3375        return new
     3376
     3377    def simplification(self):
     3378        """
     3379        Simplify a transducer by Moore's algorithm, first moving
     3380        common output labels of transitions leaving a state to output
     3381        labels of transitions entering the state
     3382        (cf. :meth:`.prepone_output`).
     3383
     3384        The resulting transducer implements the same function as the
     3385        original transducer.
     3386
     3387        EXAMPLES::
     3388
     3389            sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0),
     3390            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     3391            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     3392            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     3393            sage: fsm.simplification()
     3394            Traceback (most recent call last):
     3395            ...
     3396            NotImplementedError: Simplification is only implemented for Transducers. For Automata, use minimization instead
     3397            sage: fsm = Transducer([("A", "B", 0, 1), ("A", "B", 1, 0),
     3398            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     3399            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     3400            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     3401            sage: fsms = fsm.simplification()
     3402            sage: fsms
     3403            finite state machine with 2 states
     3404            sage: fsms.transitions()
     3405            [Transition from State (State 'B', State 'D')
     3406                          to State (State 'A', State 'C'): 0|0,
     3407             Transition from State (State 'B', State 'D')
     3408                          to State (State 'A', State 'C'): 1|1,
     3409             Transition from State (State 'A', State 'C')
     3410                          to State (State 'B', State 'D'): 0|1,
     3411             Transition from State (State 'A', State 'C')
     3412                          to State (State 'B', State 'D'): 1|0]
     3413            sage: fsms.relabeled().transitions()
     3414            [Transition from State 0 to State 1: 0|0,
     3415             Transition from State 0 to State 1: 1|1,
     3416             Transition from State 1 to State 0: 0|1,
     3417             Transition from State 1 to State 0: 1|0]
     3418        """
     3419        if self.mode!='transducer':
     3420            raise NotImplementedError, "Simplification is only implemented for Transducers. For Automata, use minimization instead"
     3421        fsm=deepcopy(self)
     3422        fsm.prepone_output()
     3423        return fsm.quotient(fsm.equivalence_classes())
     3424
     3425
     3426    # *************************************************************************
     3427    # other
     3428    # *************************************************************************
     3429
     3430
     3431    def graph(self, edge_labels='words_in_out'):
     3432        """
     3433        Returns the graph of the finite state machine with labeled
     3434        vertices and labeled edges.
     3435
     3436        - ``edge_label``: (default: ``'words_in_out'``) can be
     3437             - ``'words_in_out'`` (labels will be strings ``'i|o'``)
     3438             - a function with which takes as input a transition
     3439               and outputs (returns) the label
     3440
     3441        EXAMPLES::
     3442
     3443            sage: A = FSMState('A')
     3444            sage: T = Transducer()
     3445            sage: T.graph()
     3446            Digraph on 0 vertices
     3447            sage: T.add_state(A)
     3448            State 'A'
     3449            sage: T.graph()
     3450            Digraph on 1 vertex
     3451            sage: T.add_transition(('A', 'A', 0, 1))
     3452            Transition from State 'A' to State 'A': 0|1
     3453            sage: T.graph()
     3454            Looped digraph on 1 vertex
     3455        """
     3456        if edge_labels == 'words_in_out':
     3457            label_fct = lambda t:t._in_out_label_()
     3458        elif hasattr(edge_labels, '__call__'):
     3459            label_fct = edge_labels
     3460        else:
     3461            raise TypeError, 'Wrong argument for edge_labels.'
     3462
     3463        graph_data = []
     3464        isolated_vertices = []
     3465        for state in self.iter_states():
     3466            transitions = state.transitions
     3467            if len(transitions) == 0:
     3468                isolated_vertices.append(state.label())
     3469            for t in transitions:
     3470                graph_data.append((t.from_state.label(), t.to_state.label(),
     3471                                   label_fct(t)))
     3472
     3473        G = DiGraph(graph_data)
     3474        G.add_vertices(isolated_vertices)
     3475        return G
     3476
     3477
     3478    digraph = graph
     3479
     3480
     3481    def plot(self):
     3482        """
     3483        Plots a graph of the finite state machine with labeled
     3484        vertices and labeled edges.
     3485
     3486        TESTS::
     3487
     3488            sage: FiniteStateMachine([('A', 'A', 0)]).plot()
     3489        """
     3490        return self.graph(edge_labels='words_in_out').plot()
     3491
     3492
     3493    def predecessors(self, state, valid_input=None):
     3494        """
     3495        Lists all predecessors of a state.
     3496
     3497        If ``valid_input`` is a list, then we only consider
     3498        transitions whose input labels are contained in
     3499        ``valid_input``. ``state`` has to be a :class:`FSMState` (not
     3500        a label of a state). If input labels of length larger than `1`
     3501        are used, then ``valid_input`` has to be a list of lists.
     3502
     3503        EXAMPLES::
     3504
     3505            sage: A = Transducer([('I', 'A', 'a', 'b'), ('I', 'B', 'b', 'c'),
     3506            ....:                 ('I', 'C', 'c', 'a'), ('A', 'F', 'b', 'a'),
     3507            ....:                 ('B', 'F', ['c', 'b'], 'b'), ('C', 'F', 'a', 'c')],
     3508            ....:                initial_states=['I'], final_states=['F'])
     3509            sage: A.predecessors(A.state('A'))
     3510            [State 'A', State 'I']
     3511            sage: A.predecessors(A.state('F'), valid_input=['b', 'a'])
     3512            [State 'F', State 'C', State 'A', State 'I']
     3513            sage: A.predecessors(A.state('F'), valid_input=[['c', 'b'], 'a'])
     3514            [State 'F', State 'C', State 'B']
     3515        """
     3516        if valid_input != None:
     3517            valid_list = list()
     3518            for input in valid_input:
     3519                input_list = input
     3520                if not isinstance(input_list, list):
     3521                    input_list = [input]
     3522                valid_list.append(input_list)
     3523            valid_input = valid_list
     3524
     3525        unhandeled_direct_predecessors = {s:[] for s in self.states() }
     3526        for t in self.transitions():
     3527            if valid_input is None or t.word_in in valid_input:
     3528                unhandeled_direct_predecessors[t.to_state].append(t.from_state)
     3529        done = []
     3530        open = [state]
     3531        while len(open) > 0:
     3532            s = open.pop()
     3533            candidates = unhandeled_direct_predecessors[s]
     3534            if candidates is not None:
     3535                open.extend(candidates)
     3536                unhandeled_direct_predecessors[s] = None
     3537                done.append(s)
     3538        return(done)
     3539
     3540
     3541#*****************************************************************************
     3542
     3543
     3544def is_FSMProcessIterator(PI):
     3545    """
     3546    Tests whether or not ``PI`` inherits from :class:`FSMProcessIterator`.
     3547
     3548    TESTS::
     3549
     3550        sage: from sage.combinat.finite_state_machine import is_FSMProcessIterator
     3551        sage: is_FSMProcessIterator(FSMProcessIterator(FiniteStateMachine()))
     3552        Traceback (most recent call last):
     3553        ...
     3554        ValueError: No state is initial.
     3555    """
     3556    return isinstance(PI, FSMProcessIterator)
     3557
     3558
     3559class FSMProcessIterator:
     3560    """
     3561    This class is for processing an input string on a finite state
     3562    machine.
     3563
     3564    An instance of this class is generated when
     3565    :meth:`FiniteStateMachine.process` or
     3566    :meth:`FiniteStateMachine.iter_process` of the finite state
     3567    machine is invoked. It behaves like an iterator which, in each
     3568    step, takes one letter of the input and runs (one step on) the
     3569    finite state machine with this input. More precisely, in each
     3570    step, the process iterator takes an outgoing transition of the
     3571    current state, whose input label equals the input letter of the
     3572    tape. The output label of the transition, if present, is written
     3573    on the output tape.
     3574
     3575    INPUT:
     3576
     3577    - ``fsm`` -- The finite state machine on which the input should be
     3578      processed.
     3579
     3580    - ``input_tape`` -- The input tape. It can be anything that is
     3581      iterable.
     3582
     3583    - ``initial_state`` -- The initial state in which the machine
     3584      starts. If this is ``None``, the unique inital state of the
     3585      finite state machine is takes. If there are several, an error is
     3586      reported.
     3587
     3588    The process (iteration) stops if there are no more input letters
     3589    on the tape. In this case a StopIteration exception is thrown. As
     3590    result the following attributes are available:
     3591
     3592    - ``accept_input`` -- Is True if the reached state is a final state.
     3593
     3594    - ``current_state`` -- The current/reached state in the process.
     3595
     3596    - ``output_tape`` -- The written output.
     3597
     3598    Current values of those attribtes (except ``accept_input``) are
     3599    (also) available during the iteration.
     3600
     3601
     3602    EXAMPLES:
     3603
     3604    The following transducer reads binary words and outputs a word,
     3605    where blocks of ones are replaced by just a single one. Further
     3606    only words that end with a zero are accepted.
     3607
     3608    ::
     3609
     3610        sage: T = Transducer({'A': [('A', 0, 0), ('B', 1, None)],
     3611        ....:                 'B': [('B', 1, None), ('A', 0, [1, 0])]},
     3612        ....:     initial_states=['A'], final_states=['A'])
     3613        sage: input = [1, 1, 0, 0, 1, 0, 1, 1, 1, 0]
     3614        sage: T.process(input)
     3615        (True, State 'A', [1, 0, 0, 1, 0, 1, 0])
     3616
     3617    The function :meth:`FiniteStateMachine.process` created a new
     3618    ``FSMProcessIterator``. We can do that manually, too, and get full
     3619    access to the iteration process::
     3620
     3621        sage: it = FSMProcessIterator(T, input_tape=input)
     3622        sage: for _ in it:
     3623        ....:     print (it.current_state, it.output_tape)
     3624        (State 'B', [])
     3625        (State 'B', [])
     3626        (State 'A', [1, 0])
     3627        (State 'A', [1, 0, 0])
     3628        (State 'B', [1, 0, 0])
     3629        (State 'A', [1, 0, 0, 1, 0])
     3630        (State 'B', [1, 0, 0, 1, 0])
     3631        (State 'B', [1, 0, 0, 1, 0])
     3632        (State 'B', [1, 0, 0, 1, 0])
     3633        (State 'A', [1, 0, 0, 1, 0, 1, 0])
     3634        sage: it.accept_input
     3635        True
     3636    """
     3637    def __init__(self, fsm, input_tape=None, initial_state=None):
     3638        """
     3639        See :class:`FSMProcessIterator` for more information.
     3640
     3641        EXAMPLES::
     3642
     3643            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3644            ....:     initial_states=['A'], final_states=['A'])
     3645            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3646            sage: for _ in it:
     3647            ....:     pass
     3648            sage: it.output_tape
     3649            [1, 0]
     3650        """
     3651        self.fsm = fsm
     3652        if initial_state is None:
     3653            fsm_initial_states = self.fsm.initial_states()
     3654            try:
     3655                self.current_state = fsm_initial_states[0]
     3656            except IndexError:
     3657                raise ValueError, "No state is initial."
     3658            if len(fsm_initial_states) > 1:
     3659                raise ValueError, "Several initial states."
     3660        else:
     3661            self.current_state = initial_state
     3662        self.output_tape = []
     3663        if input_tape is None:
     3664            self._input_tape_iter_ = iter([])
     3665        else:
     3666            if hasattr(input_tape, '__iter__'):
     3667                self._input_tape_iter_ = iter(input_tape)
     3668            else:
     3669                raise ValueError, "Given input tape is not iterable."
     3670
     3671    def __iter__(self):
     3672        """
     3673        Returns ``self``.
     3674
     3675        TESTS::
     3676
     3677            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3678            ....:     initial_states=['A'], final_states=['A'])
     3679            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3680            sage: id(it) == id(iter(it))
     3681            True
     3682        """
     3683        return self
     3684
     3685    def next(self):
     3686        """
     3687        Makes one step in processing the input tape.
     3688
     3689        It returns the taken transition. A ``StopIteration`` exception is
     3690        thrown when there is nothing more to read.
     3691
     3692        EXAMPLES::
     3693
     3694            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3695            ....:     initial_states=['A'], final_states=['A'])
     3696            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3697            sage: it.next()
     3698            Transition from State 'A' to State 'A': 0|1
     3699            sage: it.next()
     3700            Transition from State 'A' to State 'A': 1|0
     3701            sage: it.next()
     3702            Traceback (most recent call last):
     3703            ...
     3704            StopIteration
     3705        """
     3706        if hasattr(self, 'accept_input'):
     3707            raise StopIteration
     3708        try:
     3709            # process current state
     3710            transition = None
     3711            try:
     3712                transition = self.current_state.hook(
     3713                    self.current_state, self)
     3714            except AttributeError:
     3715                pass
     3716            self.write_word(self.current_state.word_out)
     3717
     3718            # get next
     3719            if not isinstance(transition, FSMTransition):
     3720                next_word = []
     3721                found = False
     3722
     3723                try:
     3724                    while not found:
     3725                        next_word.append(self.read_letter())
     3726                        try:
     3727                            transition = self.get_next_transition(
     3728                                next_word)
     3729                            found = True
     3730                        except ValueError:
     3731                            pass
     3732                except StopIteration:
     3733                    # this means input tape is finished
     3734                    if len(next_word) > 0:
     3735                        self.accept_input = False
     3736                    raise StopIteration
     3737
     3738            # process transition
     3739            try:
     3740                transition.hook(transition, self)
     3741            except AttributeError:
     3742                pass
     3743            self.write_word(transition.word_out)
     3744
     3745            # go to next state
     3746            self.current_state = transition.to_state
     3747
     3748        except StopIteration:
     3749            # this means, either input tape is finished or
     3750            # someone has thrown StopIteration manually (in one
     3751            # of the hooks)
     3752            if not self.current_state.is_final:
     3753                self.accept_input = False
     3754            if not hasattr(self, 'accept_input'):
     3755                self.accept_input = True
     3756            raise StopIteration
     3757
     3758        # return
     3759        return transition
     3760
     3761    def read_letter(self):
     3762        """
     3763        Reads a letter from the input tape.
     3764
     3765        Exception ``StopIteration`` is thrown if tape has reached
     3766        the end.
     3767
     3768        EXAMPLES::
     3769
     3770            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3771            ....:     initial_states=['A'], final_states=['A'])
     3772            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3773            sage: it.read_letter()
     3774            0
     3775        """
     3776        return self._input_tape_iter_.next()
     3777
     3778    def write_letter(self, letter):
     3779        """
     3780        Writes a letter on the output tape.
     3781
     3782        EXAMPLES::
     3783
     3784            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3785            ....:     initial_states=['A'], final_states=['A'])
     3786            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3787            sage: it.write_letter(42)
     3788            sage: it.output_tape
     3789            [42]
     3790        """
     3791        self.output_tape.append(letter)
     3792
     3793    def write_word(self, word):
     3794        """
     3795        Writes a word on the output tape.
     3796
     3797        EXAMPLES::
     3798
     3799            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3800            ....:     initial_states=['A'], final_states=['A'])
     3801            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3802            sage: it.write_word([4, 2])
     3803            sage: it.output_tape
     3804            [4, 2]
     3805        """
     3806        for letter in word:
     3807            self.write_letter(letter)
     3808
     3809    def get_next_transition(self, word_in):
     3810        """
     3811        Returns the next transition according to ``word_in``. It is
     3812        assumed that we are in state ``self.current_state``.
     3813
     3814        EXAMPLES::
     3815
     3816            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     3817            ....:     initial_states=['A'], final_states=['A'])
     3818            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     3819            sage: it.get_next_transition([0])
     3820            Transition from State 'A' to State 'A': 0|1
     3821        """
     3822        for transition in self.current_state.transitions:
     3823            if transition.word_in == word_in:
     3824                return transition
     3825        raise ValueError
     3826
     3827
     3828#*****************************************************************************
     3829
     3830
     3831def setup_latex_preamble():
     3832    """
     3833    This function adds the package ``tikz`` with support for automata
     3834    to the preamble of Latex so that the finite state machines can be
     3835    drawn nicely.
     3836
     3837    TESTS::
     3838
     3839        sage: from sage.combinat.finite_state_machine import setup_latex_preamble
     3840        sage: setup_latex_preamble()
     3841    """
     3842    latex.add_package_to_preamble_if_available('tikz')
     3843    latex.add_to_preamble('\\usetikzlibrary{automata}')
     3844
     3845
     3846#*****************************************************************************