Ticket #15078: trac_15078_fsm_automata_transducers.patch

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

    # HG changeset patch
    # User Daniel Krenn <math+sage@danielkrenn.at>
    # Date 1377175031 -7200
    # Node ID 63a84c5179d8e027c9d547ecafe195e66e8d8a9c
    # 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): P24644, Asymptotic Analysis and Extremal
     277  Discrete Structures
     278
     279"""
     280
     281#*****************************************************************************
     282#  Copyright (C) 2012, 2013 Daniel Krenn <math+sage@danielkrenn.at>
     283#                2012, 2013 Clemens Heuberger <clemens.heuberger@aau.at>
     284#                2012, 2013 Sara Kropf <sara.kropf@aau.at>
     285#
     286#  Distributed under the terms of the GNU General Public License (GPL)
     287#  as published by the Free Software Foundation; either version 2 of
     288#  the License, or (at your option) any later version.
     289#                http://www.gnu.org/licenses/
     290#*****************************************************************************
     291
     292from sage.structure.sage_object import SageObject
     293from sage.graphs.digraph import DiGraph
     294from sage.symbolic.ring import SR
     295from sage.matrix.constructor import matrix
     296from sage.rings.integer_ring import ZZ
     297from sage.calculus.var import var
     298from sage.misc.latex import latex
     299from sage.functions.trig import cos, sin, atan2
     300from sage.symbolic.constants import pi
     301
     302from copy import copy
     303from copy import deepcopy
     304
     305import itertools
     306
     307#*****************************************************************************
     308
     309def conditional_iterator(iterator, condition):
     310    """
     311    Returns a conditional iterator.
     312
     313    Input
     314    - ``iterator``
     315    - ``condition``: function
     316
     317    EXAMPLES::
     318
     319        sage: L = range(10)
     320        sage: from sage.combinat.finite_state_machine import conditional_iterator
     321        sage: it = conditional_iterator(L, lambda x: mod(x, 3) != 0)
     322        sage: [i for i in it]
     323        [1, 2, 4, 5, 7, 8]
     324    """
     325    for i in iterator:
     326        if condition(i):
     327            yield i
     328
     329#*****************************************************************************
     330
     331FSMEmptyWordSymbol = '-'
     332
     333def FSMLetterSymbol(letter):
     334    """
     335    If ``letter`` is ``None`` the symbol for the empty word
     336    ``FSMEmptyWordSymbol`` is returned, otherwise the string associated
     337    to the letter.
     338
     339    EXAMPLES::
     340
     341        sage: from sage.combinat.finite_state_machine import FSMLetterSymbol
     342        sage: FSMLetterSymbol(0)
     343        '0'
     344        sage: FSMLetterSymbol(None)
     345        '-'
     346    """
     347    return FSMEmptyWordSymbol if letter is None else repr(letter)
     348
     349
     350def FSMWordSymbol(word):
     351    """
     352    Returns a string of ``word``. It may returns the symbol of the
     353    empty word ``FSMEmptyWordSymbol``.
     354
     355    EXAMPLES::
     356
     357        sage: from sage.combinat.finite_state_machine import FSMWordSymbol
     358        sage: FSMWordSymbol([0, 1, 1])
     359        '0,1,1'
     360    """
     361    if not isinstance(word, list):
     362        return FSMLetterSymbol(word)
     363    if len(word) == 0:
     364        return FSMEmptyWordSymbol
     365    s = ''
     366    for letter in word:
     367        s += (',' if len(s) > 0 else '') + FSMLetterSymbol(letter)
     368    return s
     369
     370
     371#*****************************************************************************
     372
     373
     374def Automaton(*args, **kwargs):
     375    """
     376    A FiniteStateMachine with ``mode='automaton'``
     377    See class :class:`FiniteStateMachine` for more information.
     378
     379    TESTS::
     380
     381        sage: Automaton()
     382        finite state machine with 0 states
     383    """
     384    return FiniteStateMachine(mode='automaton', *args, **kwargs)
     385
     386
     387def Transducer(*args, **kwargs):
     388    """
     389    A FiniteStateMachine with ``mode='transducer'``
     390    See class :class:`FiniteStateMachine` for more information.
     391
     392    TESTS::
     393
     394        sage: Transducer()
     395        finite state machine with 0 states
     396    """
     397    return FiniteStateMachine(mode='transducer', *args, **kwargs)
     398
     399
     400#*****************************************************************************
     401
     402
     403def is_FSMState(S):
     404    """
     405    Tests whether or not ``S`` inherits from :class:`FSMState`.
     406
     407    TESTS::
     408
     409        sage: from sage.combinat.finite_state_machine import is_FSMState
     410        sage: is_FSMState(FSMState('A'))
     411        True
     412    """
     413    return isinstance(S, FSMState)
     414
     415
     416class FSMState(SageObject):
     417    """
     418    Class for a state of a finite state machine.
     419
     420    EXAMPLES::
     421
     422        sage: A = FSMState('state 1', word_out=0, is_initial=True)
     423        sage: A
     424        State 'state 1'
     425        sage: A.label()
     426        'state 1'
     427        sage: B = FSMState('state 2')
     428        sage: A == B
     429        False
     430
     431    """
     432    def __init__(self, label, word_out=None,
     433                 is_initial=False, is_final=False,
     434                 hook=None):
     435        """
     436        See :class:`FSMState` for more information.
     437
     438        EXAMPLES::
     439
     440            sage: FSMState('final', is_final=True)
     441            State 'final'
     442        """
     443        if label is None or label == "":
     444            raise ValueError, "You have to specify a label for the state."
     445        self._label_ = label
     446
     447        if isinstance(word_out, list):
     448            self.word_out = word_out
     449        elif word_out is not None:
     450            self.word_out = [word_out]
     451        else:
     452            self.word_out = []
     453
     454        self.is_initial = is_initial
     455        self.is_final = is_final
     456        if hook is not None:
     457            if hasattr(hook, '__call__'):
     458                self.hook = hook
     459            else:
     460                raise TypeError, 'Wrong argument for hook.'
     461
     462
     463    def label(self):
     464        """
     465        Returns the label of the state.
     466
     467        EXAMPLES::
     468
     469            sage: A = FSMState('state')
     470            sage: A.label()
     471            'state'
     472        """
     473        return self._label_
     474
     475
     476    def __copy__(self):
     477        """
     478        Returns a (shallow) copy of the state.
     479
     480        EXAMPLES::
     481
     482            sage: A = FSMState('A')
     483            sage: copy(A)
     484            State 'A'
     485        """
     486        new = FSMState(self.label(), self.word_out,
     487                       self.is_initial, self.is_final)
     488        if hasattr(self, 'hook'):
     489            new.hook = self.hook
     490        return new
     491
     492
     493    copy = __copy__
     494
     495
     496    def __deepcopy__(self, memo):
     497        """
     498        Returns a deep copy of the state.
     499
     500        EXAMPLES::
     501
     502            sage: A = FSMState('A')
     503            sage: deepcopy(A)
     504            State 'A'
     505        """
     506        try:
     507            label = self._deepcopy_relabel_
     508        except AttributeError:
     509            label = self.label()
     510        new = FSMState(deepcopy(label, memo), deepcopy(self.word_out, memo),
     511                       self.is_initial, self.is_final)
     512        if hasattr(self, 'hook'):
     513            new.hook = deepcopy(self.hook, memo)
     514        return new
     515
     516
     517    def deepcopy(self, memo=None, label=None):
     518        """
     519        Returns a deep copy of the state.
     520
     521        EXAMPLES::
     522
     523            sage: A = FSMState('A')
     524            sage: deepcopy(A)
     525            State 'A'
     526        """
     527        return deepcopy(self, memo)
     528
     529    def relabeled(self, label, memo=None):
     530        """
     531        Returns a deep copy of the state with a new label.
     532
     533        EXAMPLES::
     534
     535            sage: A = FSMState('A')
     536            sage: A.relabeled('B')
     537            State 'B'
     538
     539        """
     540        self._deepcopy_relabel_ = label
     541        new = deepcopy(self, memo)
     542        del self._deepcopy_relabel_
     543        return new
     544
     545
     546    def __hash__(self):
     547        """
     548        Returns a hash value for the object.
     549
     550        TESTS::
     551
     552            sage: A = FSMState('A')
     553            sage: hash(A) #random
     554            -269909568
     555        """
     556        return hash(self.label())
     557
     558
     559    def _repr_(self):
     560        """
     561        Returns the string "State label".
     562
     563        TESTS:
     564
     565            sage: FSMState('A')._repr_()
     566            "State 'A'"
     567        """
     568        return "State %s" % (repr(self.label()))
     569
     570
     571    def __eq__(left, right):
     572        """
     573        Returns True if two states are the same, i.e., if they have
     574        the same labels.
     575
     576        Note that the hooks and whether the states are initial or
     577        final are not checked.
     578
     579        EXAMPLES::
     580
     581            sage: A = FSMState('A')
     582            sage: B = FSMState('A', is_initial=True)
     583            sage: A == B
     584            True
     585        """
     586        if not is_FSMState(right):
     587            return False
     588        return left.label() == right.label()
     589
     590
     591    def __ne__(left, right):
     592        """
     593        Tests for inequality, complement of __eq__.
     594
     595        EXAMPLES::
     596
     597            sage: A = FSMState('A', is_initial=True)
     598            sage: B = FSMState('A', is_final=True)
     599            sage: A != B
     600            False
     601        """
     602        return (not (left == right))
     603
     604
     605    def __nonzero__(self):
     606        """
     607        Returns True.
     608
     609        TESTS::
     610
     611            sage: FSMState('A').__nonzero__()
     612            True
     613        """
     614        return True  # A state cannot be zero (see __init__)
     615
     616
     617#*****************************************************************************
     618
     619
     620def is_FSMTransition(T):
     621    """
     622    Tests whether or not ``T`` inherits from :class:`FSMTransition`.
     623
     624    TESTS::
     625
     626        sage: from sage.combinat.finite_state_machine import is_FSMTransition
     627        sage: is_FSMTransition(FSMTransition('A', 'B'))
     628        True
     629    """
     630    return isinstance(T, FSMTransition)
     631
     632
     633class FSMTransition(SageObject):
     634    """
     635    Class for a transition of a finite state machine.
     636
     637    EXAMPLES::
     638
     639        sage: A = FSMState('A')
     640        sage: B = FSMState('B')
     641        sage: S = FSMTransition(A, B, 0, 1)
     642        sage: T = FSMTransition('A', 'B', 0, 1)
     643        sage: T == S
     644        True
     645        sage: U = FSMTransition('A', 'B', 0)
     646        sage: U == T
     647        False
     648
     649    """
     650    def __init__(self, from_state, to_state,
     651                 word_in=None, word_out=None,
     652                 hook=None):
     653        """
     654        See :class:`FSMTransition` for more information.
     655
     656        EXAMPLES::
     657
     658            sage: FSMTransition('A', 'B', 0, 1)
     659            Transition from State 'A' to State 'B': 0|1
     660        """
     661        if is_FSMState(from_state):
     662            self.from_state = from_state
     663        else:
     664            self.from_state = FSMState(from_state)
     665        if is_FSMState(to_state):
     666            self.to_state = to_state
     667        else:
     668            self.to_state = FSMState(to_state)
     669
     670        if isinstance(word_in, list):
     671            self.word_in = word_in
     672        elif word_in is not None:
     673            self.word_in = [word_in]
     674        else:
     675            self.word_in = []
     676
     677        if isinstance(word_out, list):
     678            self.word_out = word_out
     679        elif word_out is not None:
     680            self.word_out = [word_out]
     681        else:
     682            self.word_out = []
     683
     684        if hook is not None:
     685            if hasattr(hook, '__call__'):
     686                self.hook = hook
     687            else:
     688                raise TypeError, 'Wrong argument for hook.'
     689
     690
     691    def __copy__(self):
     692        """
     693        Returns a (shallow) copy of the transition.
     694
     695        EXAMPLES::
     696
     697            sage: t = FSMTransition('A', 'B', 0)
     698            sage: copy(t)
     699            Transition from State 'A' to State 'B': 0|-
     700        """
     701        new = FSMTransition(self.from_state, self.to_state,
     702                            self.word_in, self.word_out)
     703        if hasattr(self, 'hook'):
     704            new.hook = self.hook
     705        return new
     706
     707
     708    copy = __copy__
     709
     710    def __deepcopy__(self, memo):
     711        """
     712        Returns a deep copy of the transition.
     713
     714        EXAMPLES::
     715
     716            sage: t = FSMTransition('A', 'B', 0)
     717            sage: deepcopy(t)
     718            Transition from State 'A' to State 'B': 0|-
     719        """
     720        new = FSMTransition(deepcopy(self.from_state, memo),
     721                            deepcopy(self.to_state, memo),
     722                            deepcopy(self.word_in, memo),
     723                            deepcopy(self.word_out, memo))
     724        if hasattr(self, 'hook'):
     725            new.hook = deepcopy(self.hook, memo)
     726        return new
     727
     728
     729    def deepcopy(self, memo=None):
     730        """
     731        Returns a deep copy of the transition.
     732
     733        EXAMPLES::
     734
     735            sage: t = FSMTransition('A', 'B', 0)
     736            sage: deepcopy(t)
     737            Transition from State 'A' to State 'B': 0|-
     738        """
     739        return deepcopy(self, memo)
     740
     741
     742    def __hash__(self):
     743        """
     744        Since transitions are mutable, they should not be hashable, so
     745        we return a type error.
     746
     747        EXAMPLES::
     748
     749            sage: hash(FSMTransition('A', 'B'))
     750            Traceback (most recent call last):
     751            ...
     752            TypeError: Transitions are mutable, and thus not hashable.
     753
     754        """
     755        raise TypeError, "Transitions are mutable, and thus not hashable."
     756
     757
     758    def _repr_(self):
     759        """
     760        Represents a transitions as from state to state and input, output.
     761
     762        EXAMPLES::
     763
     764            sage: FSMTransition('A', 'B', 0, 0)._repr_()
     765            "Transition from State 'A' to State 'B': 0|0"
     766
     767        """
     768        return "Transition from %s to %s: %s" % (repr(self.from_state),
     769                                                 repr(self.to_state),
     770                                                 self._in_out_label_())
     771
     772
     773    def _in_out_label_(self):
     774        """
     775        Returns the input and output of a transition as
     776        "word_in|word_out".
     777
     778        EXAMPLES::
     779
     780            sage: FSMTransition('A', 'B', 0, 1)._in_out_label_()
     781            '0|1'
     782        """
     783        return "%s|%s" % (FSMWordSymbol(self.word_in),
     784                          FSMWordSymbol(self.word_out))
     785
     786
     787    def __eq__(left, right):
     788        """
     789        Returns True if the two transitions are the same, i.e., if the
     790        both go from the same states to the same states and read and
     791        write the same words.
     792
     793        Note that the hooks are not checked.
     794
     795        EXAMPLES::
     796
     797            sage: A = FSMState('A', is_initial=True)
     798            sage: t1 = FSMTransition('A', 'B', 0, 1)
     799            sage: t2 = FSMTransition(A, 'B', 0, 1)
     800            sage: t1 == t2
     801            True
     802        """
     803        if not is_FSMTransition(right):
     804            raise TypeError, 'Only instances of FSMTransition ' \
     805                'can be compared.'
     806        return left.from_state == right.from_state \
     807            and left.to_state == right.to_state \
     808            and left.word_in == right.word_in \
     809            and left.word_out == right.word_out
     810
     811
     812    def __ne__(left, right):
     813        """
     814        Tests for inequality, complement of __eq__.
     815
     816        EXAMPLES::
     817
     818            sage: A = FSMState('A', is_initial=True)
     819            sage: t1 = FSMTransition('A', 'B', 0, 1)
     820            sage: t2 = FSMTransition(A, 'B', 0, 1)
     821            sage: t1!=t2
     822            False
     823        """
     824        return (not (left == right))
     825
     826
     827    def __nonzero__(self):
     828        """
     829        Returns True.
     830
     831        EXAMPLES::
     832
     833            sage: FSMTransition('A', 'B', 0).__nonzero__()
     834            True
     835        """
     836        return True  # A transition cannot be zero (see __init__)
     837
     838
     839#*****************************************************************************
     840
     841
     842def is_FiniteStateMachine(FSM):
     843    """
     844    Tests whether or not ``FSM`` inherits from :class:`FiniteStateMachine`.
     845
     846    TESTS::
     847
     848        sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine
     849        sage: is_FiniteStateMachine(FiniteStateMachine())
     850        True
     851        sage: is_FiniteStateMachine(Automaton())
     852        True
     853        sage: is_FiniteStateMachine(Transducer())
     854        True
     855    """
     856    return isinstance(FSM, FiniteStateMachine)
     857
     858
     859class FiniteStateMachine(SageObject):
     860    """
     861    Class for a finite state machine.
     862
     863    A finite state machine is a finite set of states connected by transistions.
     864
     865
     866    INPUT:
     867
     868    - ``data`` -- can be any of the following:
     869
     870      - ``{A:{B:{word_in=0, word_out=1}, C:{word_in=1, word_out=1}, ...}``
     871      - ``{A:{B:(0, 1), C:(1, 1), ...}``
     872      - ``{A:{B:FSMTransition(A, B, 0, 1), C:FSMTransition(A, C, 1, 1), ...}``
     873      - ``{A:[(B, 0, 1), (C, 1, 1)], ...}``
     874      - ``{A:[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)], ...}``
     875      - ``[{from_state:A, to_state:B, word_in:0, word_out:1}, \
     876            from_state:A, to_state:C, word_in:1, word_out:1}, ...]``
     877      - ``[(A, B, 0, 1), (A, C, 1, 1), ...]``
     878      - ``[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1), ...]``
     879
     880      If ``A``, ``B`` and ``C`` are not instances of :class:`FSMState`,
     881      they are seen as labels of states. Note that the labels of a finite
     882      state machine have to be unique.
     883
     884      Every tuple can be replaced by a list and vice versa. In fact,
     885      everything that is iterable can be used.
     886
     887      Other arguments, which :class:`FSMTransition` accepts, can be added,
     888      too. Do this after the argument ``word_out``.
     889
     890      Further the following things are accepted for ``data``:
     891
     892      - a function (acting as transition function): The input of that
     893        function is the label of a state (the from-state) and a letter
     894        of the alphabet. It should output a tuple consisting of a
     895        label of a state (the to-state) and a word (the word-out). It
     896        may also output a list of such tuples if several transitions
     897        from the from-state and the input letter exist (this means
     898        that the finite state machine is non-deterministic). If the
     899        transition does not exist, the function should raise a
     900        LookUpError or return an empty list.  Some ``initial_states``
     901        and an ``input_alphabet`` have to be specified.
     902
     903      - an other instance of a finite state machine
     904
     905    - ``mode`` -- can be any of the following:
     906
     907      #.  None
     908      #.  'automaton'
     909      #.  'transducer'
     910
     911    - ``initial_states`` and ``final_states`` -- the initial and
     912      finial states if this machine
     913
     914    - ``input_alphabet`` and ``output_alphabet`` -- the input and
     915      output alphabets of this machine
     916
     917    - ``determine_alphabets`` -- If True, then the function
     918      ``determine_alphabets()`` is called after ``data`` was read and
     919      processed, if False, then not. If it is None, then it is decided
     920      during the construction of the finite state machine whether
     921      ``determine_alphabets()`` should be called.
     922
     923    - ``store_states_dict`` -- If True, then additionally the states
     924      are stored in an interal dictionary for speed up.
     925
     926
     927    EXAMPLES:
     928
     929    See documentation for more examples.
     930
     931    We illustrate the different input formats:
     932
     933    #.  A dictionary of lists, where keys are states and list-elements are
     934        states::
     935
     936            sage: a = FSMState('S_a', 'a')
     937            sage: b = FSMState('S_b', 'b')
     938            sage: c = FSMState('S_c', 'c')
     939            sage: d = FSMState('S_d', 'd')
     940            sage: FiniteStateMachine({a:[b, c], b:[b, c, d],
     941            ....:                     c:[a, b], d:[a, c]})
     942            finite state machine with 4 states
     943
     944    #.  A list of transitions::
     945
     946            sage: a = FSMState('S_a', 'a')
     947            sage: b = FSMState('S_b', 'b')
     948            sage: c = FSMState('S_c', 'c')
     949            sage: d = FSMState('S_d', 'd')
     950            sage: t1 = FSMTransition(a, b)
     951            sage: t2 = FSMTransition(b, c)
     952            sage: t3 = FSMTransition(b, d)
     953            sage: t4 = FSMTransition(c, d)
     954            sage: FiniteStateMachine([t1, t2, t3, t4])
     955            finite state machine with 4 states
     956
     957        It is possible to skip ``FSMTransition`` in the example above.
     958
     959    #.  A function:
     960
     961        In this case some inital states and an input alphabet have to
     962        be specified. ::
     963
     964            sage: def f(state_from, read):
     965            ....:     if int(state_from) + read <= 2:
     966            ....:         state_to = 2*int(state_from)+read
     967            ....:         write = 0
     968            ....:     else:
     969            ....:         state_to = 2*int(state_from) + read - 5
     970            ....:         write = 1
     971            ....:     return (str(state_to), write)
     972            sage: F = FiniteStateMachine(f, input_alphabet=[0, 1],
     973            ....:                        initial_states=['0'],
     974            ....:                        final_states=['0'])
     975            sage: F([1, 0, 1])
     976            (True, State '0', [0, 0, 1])
     977
     978        ::
     979
     980            sage: A = FSMState('A')
     981            sage: B = FSMState('B')
     982            sage: C = FSMState('C')
     983            sage: FSM1 = FiniteStateMachine(
     984            ....:  {A:{B:{'word_in':0, 'word_out':1},
     985            ....:   C:{'word_in':1, 'word_out':1}}})
     986            sage: FSM2 = FiniteStateMachine({A:{B:(0, 1), C:(1, 1)}})
     987            sage: FSM3 = FiniteStateMachine(
     988            ....:  {A:{B:FSMTransition(A, B, 0, 1),
     989            ....:      C:FSMTransition(A, C, 1, 1)}})
     990            sage: FSM4 = FiniteStateMachine({A:[(B, 0, 1), (C, 1, 1)]})
     991            sage: FSM5 = FiniteStateMachine(
     992            ....:  {A:[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)]})
     993            sage: FSM6 = FiniteStateMachine(
     994            ....:  [{'from_state':A, 'to_state':B, 'word_in':0, 'word_out':1},
     995            ....:   {'from_state':A, 'to_state':C, 'word_in':1, 'word_out':1}])
     996            sage: FSM7 = FiniteStateMachine([(A, B, 0, 1), (A, C, 1, 1)])
     997            sage: FSM8 = FiniteStateMachine(
     998            ....:  [FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)])
     999
     1000            sage: FSM1 == FSM2 == FSM3 == FSM4 == FSM5 == FSM6 == FSM7 == FSM8
     1001            True
     1002
     1003        It is possible to skip ``FSMTransition`` in the example above.
     1004
     1005    #.  A dictionary of dictionaries, where
     1006
     1007        - the keys of the outer dictionary are state-labels (from-state of
     1008          transition),
     1009        - the keys of the inner dictionaries are state-labels (to-state of
     1010          transition),
     1011        - the values of the inner dictionaries are lists, where the first
     1012          entry is an input label and the second entry is an output label.
     1013
     1014        ::
     1015
     1016            sage: FiniteStateMachine({'a':{'a':[0, 0], 'b':[1, 1]},
     1017            ....:                     'b':{'b':[1, 0]}})
     1018            finite state machine with 2 states
     1019    """
     1020
     1021    #*************************************************************************
     1022    # init
     1023    #*************************************************************************
     1024
     1025
     1026    def __init__(self,
     1027                 data=None,
     1028                 mode=None,
     1029                 initial_states=None, final_states=None,
     1030                 input_alphabet=None, output_alphabet=None,
     1031                 determine_alphabets=None,
     1032                 store_states_dict=True):
     1033        """
     1034        See :class:`FiniteStateMachine` for more information.
     1035
     1036        TEST::
     1037
     1038            sage: FiniteStateMachine()
     1039            finite state machine with 0 states
     1040        """
     1041        self._states_ = []  # List of states in the finite state
     1042                            # machine.  Each state stores a list of
     1043                            # outgoing transitions.
     1044        if store_states_dict:
     1045            self._states_dict_ = {}
     1046
     1047        if initial_states is not None:
     1048            if not hasattr(initial_states, '__iter__'):
     1049                raise TypeError, 'Initial states must be iterable ' \
     1050                    '(e.g. a list of states).'
     1051            for s in initial_states:
     1052                state = self.add_state(s)
     1053                state.is_initial = True
     1054
     1055        if final_states is not None:
     1056            if not hasattr(final_states, '__iter__'):
     1057                raise TypeError, 'Final states must be iterable ' \
     1058                    '(e.g. a list of states).'
     1059            for s in final_states:
     1060                state = self.add_state(s)
     1061                state.is_final = True
     1062
     1063        self.mode = mode
     1064        self.input_alphabet = input_alphabet
     1065        self.output_alphabet = output_alphabet
     1066
     1067        if data is None:
     1068            pass
     1069        elif is_FiniteStateMachine(data):
     1070            raise NotImplementedError
     1071        elif hasattr(data, 'iteritems'):
     1072            # data is a dict (or something similar),
     1073            # format: key = from_state, value = iterator of transitions
     1074            for (sf, iter_transitions) in data.iteritems():
     1075                from_state = self.add_state(sf)
     1076                if hasattr(iter_transitions, 'iteritems'):
     1077                    for (st, transition) in iter_transitions.iteritems():
     1078                        to_state = self.add_state(st)
     1079                        if is_FSMTransition(transition):
     1080                            self.add_transition(transition)
     1081                        elif hasattr(transition, 'iteritems'):
     1082                            self.add_transition(sf, st, **transition)
     1083                        elif hasattr(transition, '__iter__'):
     1084                            self.add_transition(sf, st, *transition)
     1085                        else:
     1086                            self.add_transition(sf, st, transition)
     1087                elif hasattr(iter_transitions, '__iter__'):
     1088                    for transition in iter_transitions:
     1089                        if hasattr(transition, '__iter__'):
     1090                            L = [sf]
     1091                            L.extend(transition)
     1092                        elif is_FSMTransition(transition):
     1093                            L = transition
     1094                        else:
     1095                            L = [sf, transition]
     1096                        self.add_transition(L)
     1097                else:
     1098                    raise TypeError, 'Wrong input data for transition.'
     1099            if determine_alphabets is None and input_alphabet is None \
     1100                    and output_alphabet is None:
     1101                determine_alphabets = True
     1102        elif hasattr(data, '__iter__'):
     1103            # data is a something that is iterable,
     1104            # items are transitions
     1105            for transition in data:
     1106                if is_FSMTransition(transition):
     1107                    self.add_transition(transition)
     1108                elif hasattr(transition, 'iteritems'):
     1109                    self.add_transition(transition)
     1110                elif hasattr(transition, '__iter__'):
     1111                    self.add_transition(transition)
     1112                else:
     1113                    raise TypeError, 'Wrong input data for transition.'
     1114            if determine_alphabets is None and input_alphabet is None \
     1115                    and output_alphabet is None:
     1116                determine_alphabets = True
     1117        elif hasattr(data, '__call__'):
     1118            self.add_from_transition_function(data,
     1119                                              ignore_existing_states=True)
     1120        else:
     1121            raise TypeError, 'Cannot decide what to do with data.'
     1122
     1123        if determine_alphabets:
     1124            self.determine_alphabets()
     1125
     1126
     1127    #*************************************************************************
     1128    # copy and hash
     1129    #*************************************************************************
     1130
     1131
     1132    def __copy__(self):
     1133        """
     1134        Returns a (shallow) copy of the finite state machine.
     1135
     1136        TESTS::
     1137
     1138            sage: copy(FiniteStateMachine())
     1139            Traceback (most recent call last):
     1140            ...
     1141            NotImplementedError
     1142        """
     1143        raise NotImplementedError
     1144
     1145
     1146    copy = __copy__
     1147
     1148
     1149    def __deepcopy__(self, memo):
     1150        """
     1151        Returns a deep copy of the finite state machine.
     1152
     1153        EXAMPLES::
     1154
     1155            sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)])
     1156            sage: deepcopy(F)
     1157            finite state machine with 1 states
     1158        """
     1159        relabel = hasattr(self, '_deepcopy_relabel_')
     1160        new = FiniteStateMachine()
     1161        import itertools
     1162        relabel_iter = itertools.count(0)
     1163        for state in self.iter_states():
     1164            if relabel:
     1165                state._deepcopy_relabel_ = relabel_iter.next()
     1166            s = deepcopy(state, memo)
     1167            if relabel:
     1168                del state._deepcopy_relabel_
     1169            new.add_state(s)
     1170        for transition in self.iter_transitions():
     1171            new.add_transition(deepcopy(transition, memo))
     1172        new.mode = deepcopy(self.mode, memo)
     1173        new.input_alphabet = deepcopy(self.input_alphabet, memo)
     1174        new.output_alphabet = deepcopy(self.output_alphabet, memo)
     1175        return new
     1176
     1177
     1178    def deepcopy(self, memo=None):
     1179        """
     1180        Returns a deep copy of the finite state machine.
     1181
     1182        EXAMPLES::
     1183
     1184            sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)])
     1185            sage: deepcopy(F)
     1186            finite state machine with 1 states
     1187        """
     1188        return deepcopy(self, memo)
     1189
     1190
     1191    def relabeled(self, memo=None):
     1192        """
     1193        Returns a deep copy of the finite state machine, but the
     1194        states are relabeled by integers starting with 0.
     1195
     1196        EXAMPLES::
     1197
     1198            sage: FSM1 = FiniteStateMachine([('A', 'B'), ('B', 'C'), ('C', 'A')])
     1199            sage: FSM1.states()
     1200            [State 'A', State 'B', State 'C']
     1201            sage: FSM2 = FSM1.relabeled()
     1202            sage: FSM2.states()
     1203            [State 0, State 1, State 2]
     1204        """
     1205        self._deepcopy_relabel_ = True
     1206        new = deepcopy(self, memo)
     1207        del self._deepcopy_relabel_
     1208        return new
     1209
     1210
     1211    def __hash__(self):
     1212        """
     1213        Since finite state machines are mutable, they should not be
     1214        hashable, so we return a type error.
     1215
     1216        EXAMPLES::
     1217
     1218            sage: hash(FiniteStateMachine())
     1219            Traceback (most recent call last):
     1220            ...
     1221            TypeError: Finite state machines are mutable, and thus not hashable.
     1222        """
     1223        if getattr(self, "_immutable", False):
     1224            return hash((tuple(self.states()), tuple(self.transitions())))
     1225        raise TypeError, "Finite state machines are mutable, " \
     1226            "and thus not hashable."
     1227
     1228
     1229    #*************************************************************************
     1230    # operators
     1231    #*************************************************************************
     1232
     1233
     1234    def __add__(self, other):
     1235        """
     1236        Returns the disjoint union of the finite state machines self and other.
     1237
     1238        TESTS::
     1239
     1240            sage: FiniteStateMachine() + FiniteStateMachine([('A', 'B')])
     1241            Traceback (most recent call last):
     1242            ...
     1243            NotImplementedError
     1244        """
     1245        if is_FiniteStateMachine(other):
     1246            return self.disjoint_union(other)
     1247
     1248
     1249    def __iadd__(self, other):
     1250        """
     1251        TESTS::
     1252
     1253            sage: F = FiniteStateMachine()
     1254            sage: F += FiniteStateMachine()
     1255            Traceback (most recent call last):
     1256            ...
     1257            NotImplementedError
     1258        """
     1259        raise NotImplementedError
     1260
     1261
     1262    def __mul__(self, other):
     1263        """
     1264        TESTS::
     1265
     1266            sage: FiniteStateMachine() * FiniteStateMachine([('A', 'B')])
     1267            Traceback (most recent call last):
     1268            ...
     1269            NotImplementedError
     1270        """
     1271        if is_FiniteStateMachine(other):
     1272            return self.intersection(other)
     1273
     1274
     1275    def __imul__(self, other):
     1276        """
     1277        TESTS::
     1278
     1279            sage: F = FiniteStateMachine()
     1280            sage: F *= FiniteStateMachine()
     1281            Traceback (most recent call last):
     1282            ...
     1283            NotImplementedError
     1284        """
     1285        raise NotImplementedError
     1286
     1287
     1288    def __call__(self, *args, **kwargs):
     1289        """
     1290        Calls either method ``composition`` or ``process``.
     1291
     1292        EXAMPLES::
     1293
     1294            sage: A = FSMState('A', is_initial=True, is_final=True)
     1295            sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]})
     1296            sage: binary_inverter([0, 1, 0, 0, 1, 1])
     1297            (True, State 'A', [1, 0, 1, 1, 0, 0])
     1298
     1299        ::
     1300
     1301            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'B', 1, 1),
     1302            ....:                 ('B', 'B', 0, 0)],
     1303            ....:                initial_states=['A'], final_states=['B'])
     1304            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     1305            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     1306            ....:                initial_states=[1], final_states=[1])
     1307            sage: H = G(F)
     1308            sage: H.states()
     1309            [State ('A', 1), State ('B', 1), State ('B', 2)]
     1310        """
     1311        if len(args) == 0:
     1312            raise TypeError, "Called with too few arguments."
     1313        if is_FiniteStateMachine(args[0]):
     1314            return self.composition(*args, **kwargs)
     1315        if hasattr(args[0], '__iter__'):
     1316            return self.process(*args, **kwargs)
     1317        raise TypeError, "Do not know what to do with that arguments."
     1318
     1319
     1320    #*************************************************************************
     1321    # tests
     1322    #*************************************************************************
     1323
     1324
     1325    def __nonzero__(self):
     1326        """
     1327        Returns True if the finite state machine consists of at least
     1328        one state.
     1329
     1330        TESTS::
     1331
     1332            sage: FiniteStateMachine().__nonzero__()
     1333            False
     1334        """
     1335        return len(self._states_) > 0
     1336
     1337
     1338    def __eq__(left, right):
     1339        """
     1340        Returns True if the two finite state machines are equal, i.e.,
     1341        if they have the same states and the same transitions.
     1342
     1343        EXAMPLES::
     1344
     1345            sage: F = FiniteStateMachine([('A', 'B', 1)])
     1346            sage: F == FiniteStateMachine()
     1347            False
     1348        """
     1349        if not is_FiniteStateMachine(right):
     1350            raise TypeError, 'Only instances of FiniteStateMachine ' \
     1351                'can be compared.'
     1352        if len(left._states_) != len(right._states_):
     1353            return False
     1354        for state in left.iter_states():
     1355            if state not in right._states_:
     1356                return False
     1357            left_transitions = state.transitions
     1358            right_transitions = right.state(state).transitions
     1359            if len(left_transitions) != len(right_transitions):
     1360                return False
     1361            for t in left_transitions:
     1362                if t not in right_transitions:
     1363                    return False
     1364        return True
     1365
     1366
     1367    def __ne__(left, right):
     1368        """
     1369        Tests for inequality, complement of :meth:`.__eq__`.
     1370
     1371        EXAMPLES::
     1372
     1373            sage: E = FiniteStateMachine([('A', 'B', 0)])
     1374            sage: F = Automaton([('A', 'B', 0)])
     1375            sage: G = Transducer([('A', 'B', 0, 1)])
     1376            sage: E == F
     1377            True
     1378            sage: E == G
     1379            False
     1380        """
     1381        return (not (left == right))
     1382
     1383
     1384    def __contains__(self, item):
     1385        """
     1386        Returns true, if the finite state machine contains the
     1387        state or transition item. Note that only the labels of the
     1388        states and the input and output words are tested.
     1389
     1390        EXAMPLES::
     1391
     1392            sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)])
     1393            sage: FSMState('A', is_initial=True) in F
     1394            True
     1395            sage: 'A' in F
     1396            False
     1397            sage: FSMTransition('A', 'B', 0) in F
     1398            True
     1399        """
     1400        if is_FSMState(item):
     1401            return self.has_state(item)
     1402        if is_FSMTransition(item):
     1403            return self.has_transition(item)
     1404        return False
     1405
     1406
     1407    #*************************************************************************
     1408    # representations / LaTeX
     1409    #*************************************************************************
     1410
     1411
     1412    def _repr_(self):
     1413        """
     1414        Represents the finite state machine as "finite state machine
     1415        with n states" where n is the number of states.
     1416
     1417        EXAMPLES::
     1418
     1419            sage: FiniteStateMachine()._repr_()
     1420            'finite state machine with 0 states'
     1421        """
     1422        return "finite state machine with %s states" % len(self._states_)
     1423
     1424
     1425    def _latex_(self):
     1426        """
     1427        Returns a LaTex code for the graph of the finite state machine.
     1428
     1429        EXAMPLES::
     1430
     1431            sage: F = FiniteStateMachine([('A', 'B', 1)])
     1432            sage: F._latex_()
     1433            '\\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}'
     1434        """
     1435        result = "\\begin{tikzpicture}[auto]\n"
     1436        j = 0;
     1437        for vertex in self.states():
     1438            if not hasattr(vertex, "coordinates"):
     1439                vertex.coordinates = (3*cos(2*pi*j/len(self.states())),
     1440                                      3*sin(2*pi*j/len(self.states())))
     1441            options = ""
     1442            if vertex in self.final_states():
     1443                options += ",accepting"
     1444            if hasattr(vertex, "format_label"):
     1445                label = vertex.format_label()
     1446            elif hasattr(self, "format_state_label"):
     1447                label = self.format_state_label(vertex)
     1448            else:
     1449                label=latex(vertex.label())
     1450            result += "\\node[state%s] (v%d) at (%f,%f) {%s}\n;" % (
     1451                options, j, vertex.coordinates[0],
     1452                vertex.coordinates[1], label)
     1453            vertex._number_ = j
     1454            j += 1
     1455        adjacent = {}
     1456        for source in self.states():
     1457            for target in self.states():
     1458                transitions = filter(lambda transition: \
     1459                                         transition.to_state == target,
     1460                                     source.transitions)
     1461                adjacent[source, target] = transitions
     1462
     1463        for ((source, target), transitions) in adjacent.iteritems():
     1464            if len(transitions) > 0:
     1465                labels = []
     1466                for transition in transitions:
     1467                    if hasattr(transition, "format_label"):
     1468                        labels.append(transition.format_label())
     1469                        continue
     1470                    elif hasattr(self, "format_transition_label"):
     1471                        format_transition_label = self.format_transition_label
     1472                    else:
     1473                        format_transition_label = latex
     1474                    if self.mode == 'automaton':
     1475                        labels.append(format_transition_label(
     1476                                transition.word_in))
     1477                    else:
     1478                        labels.append(format_transition_label(
     1479                            transition.word_in) + "\\mid" + \
     1480                                format_transition_label(transition.word_out))
     1481                label = ", ".join(labels)
     1482                if source != target:
     1483                    if len(adjacent[target, source]) > 0:
     1484                        angle = atan2(
     1485                            target.coordinates[1] - source.coordinates[1],
     1486                            target.coordinates[0]-source.coordinates[0])*180/pi
     1487                        angle_source = ".%.2f" % ((angle+5).n(),)
     1488                        angle_target = ".%.2f" % ((angle+175).n(),)
     1489                    else:
     1490                        angle_source = ""
     1491                        angle_target = ""
     1492                    result += "\\path[->] (v%d%s) edge node {$%s$} (v%d%s);\n" % (
     1493                        source._number_, angle_source, label,
     1494                        target._number_, angle_target)
     1495                else:
     1496                    result += "\\path[->] (v%d) edge[loop above] node {$%s$} ();\n" % (
     1497                        source._number_, label)
     1498
     1499        result += "\\end{tikzpicture}"
     1500        return result
     1501
     1502
     1503    #*************************************************************************
     1504    # other
     1505    #*************************************************************************
     1506
     1507
     1508    def _matrix_(self, R=None):
     1509        """
     1510        Returns the adjacency matrix of the finite state machine.
     1511        See adjacency_matrix for more information.
     1512
     1513        EXAMPLES::
     1514
     1515            sage: B = FiniteStateMachine({0: {0: (0, 0), 'a': (1, 0)},
     1516            ....:                         'a': {2: (0, 0), 3: (1, 0)},
     1517            ....:                         2:{0:(1, 1), 4:(0, 0)},
     1518            ....:                         3:{'a':(0, 1), 2:(1, 1)},
     1519            ....:                         4:{4:(1, 1), 3:(0, 1)}},
     1520            ....:                        initial_states=[0])
     1521            sage: B._matrix_()
     1522            [1 1 0 0 0]
     1523            [0 0 1 1 0]
     1524            [x 0 0 0 1]
     1525            [0 x x 0 0]
     1526            [0 0 0 x x]
     1527        """
     1528        return self.adjacency_matrix()
     1529
     1530
     1531    def adjacency_matrix(self, input=None, entry=(lambda transition:var('x')**transition.word_out[0])):
     1532        """
     1533        Returns the adjacency matrix of the underlying graph where
     1534        only transitions with input label ``input`` are respected.
     1535
     1536        The function ``entry`` takes a transition and the return value
     1537        is written in the matrix as the entry
     1538        ``(transition.from_state, transition.to_state)``. If any
     1539        label of a state is not an integer, the finite state machine
     1540        is relabeled at the beginning.  If there are more than one
     1541        transitions between two states, then the different return
     1542        values of ``entry`` are added up.
     1543
     1544        The default value of entry takes the variable ``x`` to the
     1545        power of the output word of the transition.
     1546
     1547        EXAMPLES::
     1548
     1549            sage: B = FiniteStateMachine({0:{0:(0, 0), 'a':(1, 0)},
     1550            ....:                         'a':{2:(0, 0), 3:(1, 0)},
     1551            ....:                         2:{0:(1, 1), 4:(0, 0)},
     1552            ....:                         3:{'a':(0, 1), 2:(1, 1)},
     1553            ....:                         4:{4:(1, 1), 3:(0, 1)}},
     1554            ....:                        initial_states=[0])
     1555            sage: B.adjacency_matrix()
     1556            [1 1 0 0 0]
     1557            [0 0 1 1 0]
     1558            [x 0 0 0 1]
     1559            [0 x x 0 0]
     1560            [0 0 0 x x]
     1561            sage: B.adjacency_matrix(entry=(lambda transition: 1))
     1562            [1 1 0 0 0]
     1563            [0 0 1 1 0]
     1564            [1 0 0 0 1]
     1565            [0 1 1 0 0]
     1566            [0 0 0 1 1]
     1567            sage: B.adjacency_matrix(1, entry=(lambda transition: \
     1568            ....:     exp(I*transition.word_out[0]*var('t'))))
     1569            [      0       1       0       0       0]
     1570            [      0       0       0       1       0]
     1571            [e^(I*t)       0       0       0       0]
     1572            [      0       0 e^(I*t)       0       0]
     1573            [      0       0       0       0 e^(I*t)]
     1574
     1575        """
     1576        relabeledFSM = self
     1577        l = len(relabeledFSM.states())
     1578        for state in self.states():
     1579            if state.label() not in ZZ or state.label() >= l:
     1580                relabeledFSM = self.relabeled()
     1581                break
     1582        dictionary = {}
     1583        for transition in relabeledFSM.iter_transitions():
     1584            if input is None or transition.word_in == [input]:
     1585                if (transition.from_state.label(), transition.to_state.label()) in dictionary:
     1586                    dictionary[(transition.from_state.label(), transition.to_state.label())] += entry(transition)
     1587                else:
     1588                    dictionary[(transition.from_state.label(), transition.to_state.label())] = entry(transition)
     1589        return matrix(len(relabeledFSM.states()), dictionary)
     1590
     1591
     1592    def determine_alphabets(self, reset=True):
     1593        """
     1594        Determines the input and output alphabet according to the
     1595        transitions in self.
     1596
     1597        After this operation the input alphabet and the output
     1598        alphabet of self are a list of letters.
     1599
     1600        - If reset is True, then the existing input alphabet is
     1601          erased, otherwise new letters are appended to the existing
     1602          alphabet.
     1603
     1604        EXAMPLES::
     1605
     1606            sage: T = Transducer([(1, 1, 1, 0), (1, 2, 2, 1),
     1607            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     1608            ....:                determine_alphabets=False)
     1609            sage: (T.input_alphabet, T.output_alphabet)
     1610            (None, None)
     1611            sage: T.determine_alphabets()
     1612            sage: (T.input_alphabet, T.output_alphabet)
     1613            ([0, 1, 2], [0, 1])
     1614       """
     1615        if reset:
     1616            ain = set()
     1617            aout = set()
     1618        else:
     1619            ain = set(self.input_alphabet)
     1620            aout = set(self.output_alphabet)
     1621
     1622        for t in self.iter_transitions():
     1623            for letter in t.word_in:
     1624                ain.add(letter)
     1625            for letter in t.word_out:
     1626                aout.add(letter)
     1627        self.input_alphabet = list(ain)
     1628        self.output_alphabet = list(aout)
     1629
     1630
     1631    #*************************************************************************
     1632    # get states and transitions
     1633    #*************************************************************************
     1634
     1635
     1636    def states(self):
     1637        """
     1638        Returns the states of the finite state machine as list.
     1639
     1640        EXAMPLES::
     1641
     1642            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1643            sage: FSM.states()
     1644            [State '1', State '2']
     1645
     1646        """
     1647        return copy(self._states_)
     1648
     1649
     1650    def iter_states(self):
     1651        """
     1652        Returns an iterator of the states.
     1653
     1654        EXAMPLES::
     1655
     1656            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1657            sage: [s.label() for s in FSM.iter_states()]
     1658            ['1', '2']
     1659        """
     1660        return iter(self._states_)
     1661
     1662
     1663    def transitions(self, from_state=None):
     1664        """
     1665        Returns a list of all transitions. If ``from_state`` is given,
     1666        then a list of transitions starting there is given.
     1667
     1668        EXAMPLES::
     1669
     1670            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1671            sage: FSM.transitions()
     1672            [Transition from State '1' to State '2': 1|-,
     1673             Transition from State '2' to State '2': 0|-]
     1674        """
     1675        return list(self.iter_transitions(from_state))
     1676
     1677
     1678    def iter_transitions(self, from_state=None):
     1679        """
     1680        Returns an iterator of all transitions. If ``from_state`` is
     1681        given, then an iterator of transitions starting there is given.
     1682
     1683        EXAMPLES::
     1684
     1685            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1686            sage: [(t.from_state.label(), t.to_state.label())
     1687            ....:     for t in FSM.iter_transitions('1')]
     1688            [('1', '2')]
     1689            sage: [(t.from_state.label(), t.to_state.label())
     1690            ....:     for t in FSM.iter_transitions('2')]
     1691            [('2', '2')]
     1692            sage: [(t.from_state.label(), t.to_state.label())
     1693            ....:     for t in FSM.iter_transitions()]
     1694            [('1', '2'), ('2', '2')]
     1695        """
     1696        if from_state is None:
     1697            return self._iter_transitions_all_()
     1698        else:
     1699            return iter(self.state(from_state).transitions)
     1700
     1701
     1702    def _iter_transitions_all_(self):
     1703        """
     1704        Returns an iterator over all transitions.
     1705
     1706        EXAMPLES::
     1707
     1708            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     1709            sage: [(t.from_state.label(), t.to_state.label())
     1710            ....:     for t in FSM._iter_transitions_all_()]
     1711            [('1', '2'), ('2', '2')]
     1712        """
     1713        for state in self.iter_states():
     1714            for t in state.transitions:
     1715                yield t
     1716
     1717
     1718    def initial_states(self):
     1719        """
     1720        Returns a list of all initial states.
     1721
     1722        EXAMPLES::
     1723
     1724            sage: A = FSMState('A', is_initial=True)
     1725            sage: B = FSMState('B')
     1726            sage: F = FiniteStateMachine([(A, B, 1, 0)])
     1727            sage: F.initial_states()
     1728            [State 'A']
     1729        """
     1730        return list(self.iter_initial_states())
     1731
     1732
     1733    def iter_initial_states(self):
     1734        """
     1735        Returns an iterator of the initial states.
     1736
     1737        EXAMPLES::
     1738
     1739            sage: A = FSMState('A', is_initial=True)
     1740            sage: B = FSMState('B')
     1741            sage: F = FiniteStateMachine([(A, B, 1, 0)])
     1742            sage: [s.label() for s in F.iter_initial_states()]
     1743            ['A']
     1744        """
     1745        return conditional_iterator(self.iter_states(),
     1746                                    lambda s:s.is_initial)
     1747
     1748
     1749    def final_states(self):
     1750        """
     1751        Returns a list of all final states.
     1752
     1753        EXAMPLES::
     1754
     1755            sage: A = FSMState('A', is_final=True)
     1756            sage: B = FSMState('B', is_initial=True)
     1757            sage: C = FSMState('C', is_final=True)
     1758            sage: F = FiniteStateMachine([(A, B), (A, C)])
     1759            sage: F.final_states()
     1760            [State 'A', State 'C']
     1761        """
     1762        return list(self.iter_final_states())
     1763
     1764
     1765    def iter_final_states(self):
     1766        """
     1767        Returns an iterator of the final states.
     1768
     1769        EXAMPLES::
     1770
     1771            sage: A = FSMState('A', is_final=True)
     1772            sage: B = FSMState('B', is_initial=True)
     1773            sage: C = FSMState('C', is_final=True)
     1774            sage: F = FiniteStateMachine([(A, B), (A, C)])
     1775            sage: [s.label() for s in F.iter_final_states()]
     1776            ['A', 'C']
     1777        """
     1778        return conditional_iterator(self.iter_states(),
     1779                                    lambda s:s.is_final)
     1780
     1781
     1782    def state(self, state):
     1783        """
     1784        Returns the state of the finite state machine corresponding to
     1785        ``state``. If ``state`` is not an instance of
     1786        :class:`FSMState`, then it is assumed that it is the label of
     1787        a state.
     1788
     1789        If no state is found, then a ``LookupError`` is thrown.
     1790
     1791        EXAMPLES::
     1792
     1793            sage: A = FSMState('A')
     1794            sage: FSM = FiniteStateMachine([(A, 'B'), ('C', A)])
     1795            sage: FSM.state('A') == A
     1796            True
     1797            sage: FSM.state('xyz')
     1798            Traceback (most recent call last):
     1799            ...
     1800            LookupError: No state with label xyz found.
     1801        """
     1802        def what(s, switch):
     1803            if switch:
     1804                return s.label()
     1805            else:
     1806                return s
     1807        switch = is_FSMState(state)
     1808
     1809        try:
     1810            return self._states_dict_[what(state, switch)]
     1811        except AttributeError:
     1812            for s in self.iter_states():
     1813                if what(s, not switch) == state:
     1814                    return s
     1815        except KeyError:
     1816            pass
     1817        raise LookupError, \
     1818            "No state with label %s found." % (what(state, switch),)
     1819
     1820
     1821    def transition(self, transition):
     1822        """
     1823        Returns the transition of the finite state machine
     1824        corresponding to ``transition``. If ``transition`` is not an
     1825        instance of :class:`FSMTransition`, then it is assumed that it
     1826        is a tuple ``(from_state, to_state, word_in, word_out)``.
     1827
     1828        If no transition is found, then a ``LookupError`` is thrown.
     1829
     1830        EXAMPLES::
     1831
     1832            sage: t = FSMTransition('A', 'B', 0)
     1833            sage: F = FiniteStateMachine([t])
     1834            sage: F.transition(('A', 'B', 0))
     1835            Transition from State 'A' to State 'B': 0|-
     1836            sage: id(t) == id(F.transition(('A', 'B', 0)))
     1837            True
     1838       """
     1839        if not is_FSMTransition(transition):
     1840            transition = FSMTransition(*transition)
     1841        for s in self.iter_transitions(transition.from_state):
     1842            if s == transition:
     1843                return s
     1844        raise LookupError, "No transition found."
     1845
     1846
     1847    #*************************************************************************
     1848    # properties (state and transitions)
     1849    #*************************************************************************
     1850
     1851
     1852    def has_state(self, state):
     1853        """
     1854        Returns whether ``state`` is one of the states of the finite
     1855        state machine. ``state`` can be a :class:`FSMState` or a label
     1856        of a state.
     1857
     1858        EXAMPLES::
     1859
     1860            sage: FiniteStateMachine().has_state('A')
     1861            False
     1862        """
     1863        try:
     1864            self.state(state)
     1865            return True
     1866        except LookupError:
     1867            return False
     1868
     1869
     1870    def has_transition(self, transition):
     1871        """
     1872        Returns whether ``transition`` is one of the transitions of
     1873        the finite state machine. ``transition`` has to be a
     1874        :class:`FSMTransition`.
     1875
     1876        EXAMPLES::
     1877
     1878            sage: t = FSMTransition('A', 'A', 0, 1)
     1879            sage: FiniteStateMachine().has_transition(t)
     1880            False
     1881            sage: FiniteStateMachine().has_transition(('A', 'A', 0, 1))
     1882            Traceback (most recent call last):
     1883            ...
     1884            TypeError: Transition is not an instance of FSMTransition.
     1885        """
     1886        if is_FSMTransition(transition):
     1887            return transition in self.iter_transitions()
     1888        raise TypeError, "Transition is not an instance of FSMTransition."
     1889
     1890
     1891    def has_initial_state(self, state):
     1892        """
     1893        Returns whether ``state`` is one of the initial states of the
     1894        finite state machine. ``state`` can be a :class:`FSMState` or
     1895        a label.
     1896
     1897        EXAMPLES::
     1898
     1899            sage: F = FiniteStateMachine([('A', 'A')], initial_states=['A'])
     1900            sage: F.has_initial_state('A')
     1901            True
     1902        """
     1903        try:
     1904            return self.state(state).is_initial
     1905        except LookupError:
     1906            return False
     1907
     1908
     1909    def has_initial_states(self):
     1910        """
     1911        Returns whether the finite state machine has an initial state.
     1912
     1913        EXAMPLES::
     1914
     1915            sage: FiniteStateMachine().has_initial_states()
     1916            False
     1917        """
     1918        return len(self.initial_states()) > 0
     1919
     1920
     1921    def has_final_state(self, state):
     1922        """
     1923        Returns whether ``state`` is one of the final states of the
     1924        finite state machine.
     1925
     1926        EXAMPLES::
     1927
     1928            sage: FiniteStateMachine(final_states=['A']).has_final_state('A')
     1929            True
     1930        """
     1931        try:
     1932            return self.state(state).is_final
     1933        except LookupError:
     1934            return False
     1935
     1936
     1937    def has_final_states(self):
     1938        """
     1939        Returns whether the finite state machine has a final state.
     1940
     1941        EXAMPLES::
     1942
     1943            sage: FiniteStateMachine().has_final_states()
     1944            False
     1945        """
     1946        return len(self.final_states()) > 0
     1947
     1948
     1949    #*************************************************************************
     1950    # properties
     1951    #*************************************************************************
     1952
     1953
     1954    def is_deterministic(self):
     1955        """
     1956        Returns whether the finite finite state machine is deterministic.
     1957
     1958        A finite state machine is considered to be deterministic if
     1959        each transition has input label of length one and for each
     1960        pair `(q,a)` where `q` is a state and `a` is an element of the
     1961        input alphabet, there is at most one transition from `q` with
     1962        input label `a`.
     1963
     1964        TESTS::
     1965
     1966            sage: fsm = FiniteStateMachine()
     1967            sage: fsm.add_transition( ('A','B',0,[]))
     1968            Transition from State 'A' to State 'B': 0|-
     1969            sage: fsm.is_deterministic()
     1970            True
     1971            sage: fsm.add_transition( ('A','C',0,[]))
     1972            Transition from State 'A' to State 'C': 0|-
     1973            sage: fsm.is_deterministic()
     1974            False
     1975            sage: fsm.add_transition( ('A','B',[0,1],[]))
     1976            Transition from State 'A' to State 'B': 0,1|-
     1977            sage: fsm.is_deterministic()
     1978            False
     1979        """
     1980        for state in self.states():
     1981            for transition in state.transitions:
     1982                if len(transition.word_in)!=1:
     1983                    return False
     1984
     1985            transition_classes_by_word_in=itertools.groupby(
     1986                sorted(state.transitions, key=lambda t: t.word_in),
     1987                key=lambda t:t.word_in)
     1988
     1989            for key,transition_class in transition_classes_by_word_in:
     1990                if len(list(transition_class))>1:
     1991                    return False
     1992        return True
     1993
     1994
     1995    def is_connected(self):
     1996        """
     1997        TESTS::
     1998
     1999            sage: FiniteStateMachine().is_connected()
     2000            Traceback (most recent call last):
     2001            ...
     2002            NotImplementedError
     2003        """
     2004        raise NotImplementedError
     2005
     2006
     2007    #*************************************************************************
     2008    # let the finite state machine work
     2009    #*************************************************************************
     2010
     2011
     2012    def process(self, *args, **kwargs):
     2013        """
     2014        Returns whether the finite state machine accepts the input, the state
     2015        where the computation stops and which output is generated. The input
     2016        can be a list with entries from the input alphabet.
     2017
     2018        EXAMPLES::
     2019
     2020            sage: A = FSMState('A', is_initial = True, is_final = True)
     2021            sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]})
     2022            sage: binary_inverter.process([0, 1, 0, 0, 1, 1])
     2023            (True, State 'A', [1, 0, 1, 1, 0, 0])
     2024
     2025        Alternatively, we can invoke this function by::
     2026
     2027            sage: binary_inverter([0, 1, 0, 0, 1, 1])
     2028            (True, State 'A', [1, 0, 1, 1, 0, 0])
     2029
     2030        ::
     2031
     2032            sage: NAF_ = FSMState('_', is_initial = True, is_final = True)
     2033            sage: NAF1 = FSMState('1', is_final = True)
     2034            sage: NAF = Automaton(
     2035            ....:     {NAF_: [(NAF_, 0), (NAF1, 1)], NAF1: [(NAF_, 0)]})
     2036            sage: [NAF.process(w)[0] for w in [[0], [0, 1], [1, 1], [0, 1, 0, 1],
     2037            ....: [0, 1, 1, 1, 0], [1, 0, 0, 1, 1]]]
     2038            [True, True, False, True, False, False]
     2039
     2040        """
     2041        it = self.iter_process(*args, **kwargs)
     2042        for _ in it:
     2043            pass
     2044        return (it.accept_input, it.current_state, it.output_tape)
     2045
     2046
     2047    def iter_process(self, input_tape=None, initial_state=None):
     2048        """
     2049        See `process` for more informations.
     2050
     2051        EXAMPLES::
     2052
     2053            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     2054            ....:     initial_states=['A'], final_states=['A'])
     2055            sage: it = inverter.iter_process(input_tape=[0, 1, 1])
     2056            sage: for _ in it:
     2057            ....:     pass
     2058            sage: it.output_tape
     2059            [1, 0, 0]
     2060        """
     2061        return FSMProcessIterator(self, input_tape, initial_state)
     2062
     2063
     2064    #*************************************************************************
     2065    # change finite state machine (add/remove state/transitions)
     2066    #*************************************************************************
     2067
     2068
     2069    def add_state(self, state):
     2070        """
     2071        Adds a state to the finite state machine and returns the new
     2072        state. If the state already exists, that existing state is
     2073        returned.
     2074
     2075        ``state`` is either an instance of FSMState or, otherwise, a
     2076        label of a state.
     2077
     2078        EXAMPLES::
     2079
     2080            sage: F = FiniteStateMachine()
     2081            sage: A = FSMState('A', is_initial=True)
     2082            sage: F.add_state(A)
     2083            State 'A'
     2084        """
     2085        try:
     2086            return self.state(state)
     2087        except LookupError:
     2088            pass
     2089        # at this point we know that we have a new state
     2090        if is_FSMState(state):
     2091            s = state
     2092        else:
     2093            s = FSMState(state)
     2094        s.transitions = list()
     2095        self._states_.append(s)
     2096        try:
     2097            self._states_dict_[s.label()] = s
     2098        except AttributeError:
     2099            pass
     2100        return s
     2101
     2102
     2103    def add_states(self, states):
     2104        """
     2105        Adds several states. See add_state for more information.
     2106
     2107        EXAMPLES::
     2108
     2109            sage: F = FiniteStateMachine()
     2110            sage: F.add_states(['A', 'B'])
     2111            sage: F.states()
     2112            [State 'A', State 'B']
     2113        """
     2114        for state in states:
     2115            self.add_state(state)
     2116
     2117
     2118    def add_transition(self, *args, **kwargs):
     2119        """
     2120        Adds a transition to the finite state machine and returns the
     2121        new transition. If the transition already exists, that
     2122        existing transition is returned.
     2123
     2124        INPUT:
     2125
     2126        The following forms are all accepted:
     2127
     2128        ::
     2129
     2130            sage: A = FSMState('A')
     2131            sage: B = FSMState('B')
     2132
     2133            sage: FSM = FiniteStateMachine()
     2134            sage: FSM.add_transition(FSMTransition(A, B, 0, 1))
     2135            Transition from State 'A' to State 'B': 0|1
     2136
     2137            sage: FSM = FiniteStateMachine()
     2138            sage: FSM.add_transition(A, B, 0, 1)
     2139            Transition from State 'A' to State 'B': 0|1
     2140
     2141            sage: FSM = FiniteStateMachine()
     2142            sage: FSM.add_transition(A, B, word_in=0, word_out=1)
     2143            Transition from State 'A' to State 'B': 0|1
     2144
     2145            sage: FSM = FiniteStateMachine()
     2146            sage: FSM.add_transition('A', 'B', {'word_in': 0, 'word_out': 1})
     2147            Transition from State 'A' to State 'B': {'word_in': 0, 'word_out': 1}|-
     2148
     2149            sage: FSM = FiniteStateMachine()
     2150            sage: FSM.add_transition(from_state=A, to_state=B,
     2151            ....:                    word_in=0, word_out=1)
     2152            Transition from State 'A' to State 'B': 0|1
     2153
     2154            sage: FSM = FiniteStateMachine()
     2155            sage: FSM.add_transition({'from_state': A, 'to_state': B,
     2156            ....:                    'word_in': 0, 'word_out': 1})
     2157            Transition from State 'A' to State 'B': 0|1
     2158
     2159            sage: FSM = FiniteStateMachine()
     2160            sage: FSM.add_transition((A, B, 0, 1))
     2161            Transition from State 'A' to State 'B': 0|1
     2162
     2163            sage: FSM = FiniteStateMachine()
     2164            sage: FSM.add_transition([A, B, 0, 1])
     2165            Transition from State 'A' to State 'B': 0|1
     2166
     2167        If the states ``A`` and ``B`` are not instances of FSMState, then
     2168        it is assumed that they are labels of states.
     2169        """
     2170        if len(args) + len(kwargs) == 0:
     2171            return
     2172        if len(args) + len(kwargs) == 1:
     2173            if len(args) == 1:
     2174                d = args[0]
     2175                if is_FSMTransition(d):
     2176                    return self._add_fsm_transition_(d)
     2177            else:
     2178                d = kwargs.itervalues().next()
     2179            if hasattr(d, 'iteritems'):
     2180                args = []
     2181                kwargs = d
     2182            elif hasattr(d, '__iter__'):
     2183                args = d
     2184                kwargs = {}
     2185            else:
     2186                raise TypeError, "Cannot decide what to do with input."
     2187
     2188        data = dict(zip(
     2189                ('from_state', 'to_state', 'word_in', 'word_out', 'hook'),
     2190                args))
     2191        data.update(kwargs)
     2192
     2193        data['from_state'] = self.add_state(data['from_state'])
     2194        data['to_state'] = self.add_state(data['to_state'])
     2195
     2196        return self._add_fsm_transition_(FSMTransition(**data))
     2197
     2198
     2199    def _add_fsm_transition_(self, t):
     2200        """
     2201        Adds a transition ``t``, which is an instance of ``FSMTransition``.
     2202
     2203        TESTS::
     2204
     2205            sage: F = FiniteStateMachine()
     2206            sage: F._add_fsm_transition_(FSMTransition('A', 'B'))
     2207            Transition from State 'A' to State 'B': -|-
     2208        """
     2209        try:
     2210            return self.transition(t)
     2211        except LookupError:
     2212            pass
     2213        from_state = self.add_state(t.from_state)
     2214        to_state = self.add_state(t.to_state)
     2215        from_state.transitions.append(t)
     2216        return t
     2217
     2218
     2219    def add_from_transition_function(self, function, initial_states=None,
     2220                                     ignore_existing_states=False):
     2221        """
     2222        Constructs a finite state machine from a transition function.
     2223
     2224        - ``function`` may return a tuple (new_state, output_word) or a
     2225          list of such tuples.
     2226        - If no initial states are given, the already existing initial
     2227          states of self are taken.
     2228        - If ``ignore_existing_states`` is True, then already existing states
     2229          in self (e.g. already given final states) won't be processed.
     2230
     2231        EXAMPLES::
     2232
     2233            sage: F = FiniteStateMachine(initial_states=['A'],
     2234            ....:                        input_alphabet=[0, 1])
     2235            sage: def f(state, input):
     2236            ....:     return [('A', input), ('B', 1-input)]
     2237            sage: F.add_from_transition_function(f)
     2238            sage: F.transitions()
     2239            [Transition from State 'A' to State 'A': 0|0,
     2240            Transition from State 'A' to State 'B': 0|1,
     2241            Transition from State 'A' to State 'A': 1|1,
     2242            Transition from State 'A' to State 'B': 1|0,
     2243            Transition from State 'B' to State 'A': 0|0,
     2244            Transition from State 'B' to State 'B': 0|1,
     2245            Transition from State 'B' to State 'A': 1|1,
     2246            Transition from State 'B' to State 'B': 1|0]
     2247
     2248        TEST::
     2249
     2250            sage: F = FiniteStateMachine(initial_states=['A'])
     2251            sage: def f(state, input):
     2252            ....:     return [('A', input), ('B', 1-input)]
     2253            sage: F.add_from_transition_function(f)
     2254            Traceback (most recent call last):
     2255            ...
     2256            ValueError: No input alphabet is given.
     2257            Try calling determine_alphabets().
     2258        """
     2259        if self.input_alphabet is None:
     2260            raise ValueError, ("No input alphabet is given. "
     2261                               "Try calling determine_alphabets().")
     2262
     2263        if initial_states is None:
     2264            not_done = self.initial_states()
     2265        else:
     2266            not_done = copy(initial_states)
     2267        if len(not_done) == 0:
     2268            raise ValueError, "No state is initial."
     2269        if ignore_existing_states:
     2270            ignore_done = self.states()
     2271            for s in not_done:
     2272                try:
     2273                    ignore_done.remove(s)
     2274                except ValueError:
     2275                    pass
     2276        else:
     2277            ignore_done = []
     2278        while len(not_done) > 0:
     2279            s = not_done.pop(0)
     2280            for letter in self.input_alphabet:
     2281                try:
     2282                    return_value = function(s.label(), letter)
     2283                except LookupError:
     2284                    continue
     2285                if not hasattr(return_value, "pop"):
     2286                    return_value = [return_value]
     2287                try:
     2288                    for (st_label, word) in return_value:
     2289                        if not self.has_state(st_label):
     2290                            not_done.append(self.add_state(st_label))
     2291                        elif len(ignore_done) > 0:
     2292                            u = self.state(st_label)
     2293                            if u in ignore_done:
     2294                                not_done.append(u)
     2295                                ignore_done.remove(u)
     2296                        self.add_transition(s, st_label,
     2297                                            word_in=letter, word_out=word)
     2298                except TypeError:
     2299                    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))
     2300
     2301
     2302    def add_transitions_from_function(self, function, labels_as_input=True):
     2303        """
     2304        Adds a transition if ``function(state, state)`` says that there is one.
     2305
     2306        EXAMPLES::
     2307
     2308            sage: F = FiniteStateMachine()
     2309            sage: F.add_states(['A', 'B', 'C'])
     2310            sage: def f(state1, state2):
     2311            ....:     if state1 == 'C':
     2312            ....:         return None
     2313            ....:     return [0, 1]
     2314            sage: F.add_transitions_from_function(f)
     2315            sage: len(F.transitions())
     2316            6
     2317        """
     2318        for s_from in self.iter_states():
     2319            for s_to in self.iter_states():
     2320                if labels_as_input:
     2321                    t = function(s_from.label(), s_to.label())
     2322                else:
     2323                    t = function(s_from, s_to)
     2324                if hasattr(t, '__getitem__'):
     2325                    label_in = t[0]
     2326                    try:
     2327                        label_out = t[1]
     2328                    except LookupError:
     2329                        label_out = None
     2330                    self.add_transition(s_from, s_to, label_in, label_out)
     2331
     2332
     2333    def delete_transition(self, t):
     2334        """
     2335        Deletes a transition by removing it from the list of transitions of
     2336        the state, where the transition starts.
     2337
     2338        EXAMPLES::
     2339
     2340            sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)])
     2341            sage: F.delete_transition(('A', 'B', 0))
     2342            sage: F.transitions()
     2343            [Transition from State 'B' to State 'A': 1|-]
     2344        """
     2345        transition = self.transition(t)
     2346        transition.from_state.transitions.remove(transition)
     2347
     2348
     2349    def delete_state(self, s):
     2350        """
     2351        Deletes a state and all transitions coming or going to this state.
     2352        s has to be a label of a state or :class:`FSMState`.
     2353
     2354        EXAMPLES::
     2355
     2356            sage: t1 = FSMTransition('A', 'B', 0)
     2357            sage: t2 = FSMTransition('B', 'B', 1)
     2358            sage: F = FiniteStateMachine([t1, t2])
     2359            sage: F.delete_state('A')
     2360            sage: F. transitions()
     2361            [Transition from State 'B' to State 'B': 1|-]
     2362        """
     2363        state = self.state(s)
     2364        for transition in self.transitions():
     2365            if transition.to_state == state:
     2366                self.delete_transition(transition)
     2367        self._states_.remove(state)
     2368
     2369
     2370    def remove_epsilon_transitions(self):
     2371        """
     2372        TESTS::
     2373
     2374            sage: FiniteStateMachine().remove_epsilon_transitions()
     2375            Traceback (most recent call last):
     2376            ...
     2377            NotImplementedError
     2378        """
     2379        raise NotImplementedError
     2380
     2381
     2382    def accessible_components(self):
     2383        """
     2384        Returns a new finite state machine with the accessible states
     2385        of self and all transitions between those states.
     2386
     2387        A state is accessible if there is a directed path from an
     2388        initial state to the state. If self has no initial states then
     2389        a copy of the finite state machine self is returned.
     2390
     2391        EXAMPLES::
     2392
     2393            sage: F = Automaton([(0, 0, 1), (0, 0, 1), (1, 1, 0), (1, 0, 1)],
     2394            ....:               initial_states=[0])
     2395            sage: F.accessible_components()
     2396            finite state machine with 1 states
     2397        """
     2398        if len(self.initial_states()) == 0:
     2399            return deepcopy(self)
     2400
     2401        memo = {}
     2402        def accessible(sf, read):
     2403            trans = filter(lambda x: x.word_in[0] == read,
     2404                           self.transitions(sf))
     2405            return map(lambda x: (deepcopy(x.to_state, memo), x.word_out),
     2406                       trans)
     2407
     2408        return FiniteStateMachine(
     2409            accessible,
     2410            initial_states=map(lambda x: deepcopy(x, memo),
     2411                               self.initial_states()),
     2412            final_states=map(lambda x: deepcopy(x, memo),
     2413                             self.final_states()),
     2414            input_alphabet=deepcopy(self.input_alphabet, memo))
     2415
     2416
     2417    # *************************************************************************
     2418    # creating new finite state machines
     2419    # *************************************************************************
     2420
     2421
     2422    def disjoint_union(self, other):
     2423        """
     2424        TESTS::
     2425
     2426            sage: F = FiniteStateMachine([('A', 'A')])
     2427            sage: FiniteStateMachine().disjoint_union(F)
     2428            Traceback (most recent call last):
     2429            ...
     2430            NotImplementedError
     2431        """
     2432        raise NotImplementedError
     2433
     2434
     2435    def concatenation(self, other):
     2436        """
     2437        TESTS::
     2438
     2439            sage: F = FiniteStateMachine([('A', 'A')])
     2440            sage: FiniteStateMachine().concatenation(F)
     2441            Traceback (most recent call last):
     2442            ...
     2443            NotImplementedError
     2444        """
     2445        raise NotImplementedError
     2446
     2447
     2448    def Kleene_closure(self):
     2449        """
     2450        TESTS::
     2451
     2452            sage: FiniteStateMachine().Kleene_closure()
     2453            Traceback (most recent call last):
     2454            ...
     2455            NotImplementedError
     2456        """
     2457        raise NotImplementedError
     2458
     2459
     2460    def intersection(self, other):
     2461        """
     2462        TESTS::
     2463
     2464            sage: F = FiniteStateMachine([('A', 'A')])
     2465            sage: FiniteStateMachine().intersection(F)
     2466            Traceback (most recent call last):
     2467            ...
     2468            NotImplementedError
     2469        """
     2470        raise NotImplementedError
     2471
     2472
     2473    def product_FiniteStateMachine(self, other, function,
     2474                                   new_input_alphabet=None,
     2475                                   only_accessible_components=True):
     2476        """
     2477        Returns a new finite state machine whose states are
     2478        pairs of states of the original finite state machines.
     2479        The labels of the transitions are defined by ``function``.
     2480
     2481        ``function`` has to accept two transitions from `A` to `B` and
     2482        `C` to `D` and returns a pair ``(word_in, word_out)`` which is
     2483        the label of the transition `(A, C)` to `(B, D)`. If there is
     2484        no transition from `(A, C)` to `(B, D)`, then ``function``
     2485        raises a ``LookupError``.
     2486
     2487        EXAMPLES::
     2488
     2489            sage: F = Automaton([('A', 'B', 1), ('A', 'A', 0), ('B', 'A', 2)],
     2490            ....:               initial_states=['A'], final_states=['B'],
     2491            ....:               determine_alphabets=True)
     2492            sage: G = Automaton([(1, 1, 1)], initial_states=[1], final_states=[1])
     2493            sage: def addition(transition1, transition2):
     2494            ....:     return (transition1.word_in[0] + transition2.word_in[0],
     2495            ....:             None)
     2496            sage: H = F.product_FiniteStateMachine(G, addition, [0, 1, 2, 3])
     2497            sage: H.transitions()
     2498            [Transition from State ('A', 1) to State ('A', 1): 1|-,
     2499             Transition from State ('A', 1) to State ('B', 1): 2|-,
     2500             Transition from State ('B', 1) to State ('A', 1): 3|-]
     2501        """
     2502        if new_input_alphabet is None:
     2503            new_input_alphabet = self.input_alphabet
     2504
     2505        result = FiniteStateMachine(input_alphabet=new_input_alphabet)
     2506
     2507        for transition1 in self.transitions():
     2508            for transition2 in other.transitions():
     2509                try:
     2510                    word = function(transition1, transition2)
     2511                except LookupError:
     2512                    continue
     2513                result.add_transition((transition1.from_state.label(),
     2514                                       transition2.from_state.label()),
     2515                                      (transition1.to_state.label(),
     2516                                       transition2.to_state.label()),
     2517                                      word[0], word[1])
     2518
     2519        for state1 in self.initial_states():
     2520            for state2 in other.initial_states():
     2521                result.state((state1.label(), state2.label())).is_initial = True
     2522
     2523        for state1 in self.final_states():
     2524            for state2 in other.final_states():
     2525                result.state((state1.label(), state2.label())).is_final = True
     2526
     2527        if only_accessible_components:
     2528            return result.accessible_components()
     2529        else:
     2530            return result
     2531
     2532
     2533    def cartesian_product(self, other, only_accessible_components=True):
     2534        """
     2535        Returns a new finite state machine, which is the cartesian
     2536        product of self and other.
     2537
     2538        The set of states of the new automaton is the cartesian
     2539        product of the set of states of both given automata. There is
     2540        a transition `((A, B), (C, D), a)` in the new automaton if
     2541        there are transitions `(A, C, a)` and `(B, C, a)` in the old
     2542        automata.
     2543
     2544        EXAMPLES::
     2545
     2546            sage: aut1 = Automaton([('1', '2', 1), ('2', '2', 1), ('2', '2', 0)],
     2547            ....:                initial_states=['1'], final_states=['2'],
     2548            ....:                determine_alphabets=True)
     2549            sage: aut2 = Automaton([('A', 'A', 1), ('A', 'B', 1),
     2550            ....:                 ('B', 'B', 0), ('B', 'A', 0)],
     2551            ....:                initial_states=['A'], final_states=['B'],
     2552            ....:                determine_alphabets=True)
     2553            sage: res = aut1.cartesian_product(aut2)
     2554            sage: res.transitions()
     2555            [Transition from State ('1', 'A') to State ('2', 'A'): 1|-,
     2556             Transition from State ('1', 'A') to State ('2', 'B'): 1|-,
     2557             Transition from State ('2', 'B') to State ('2', 'B'): 0|-,
     2558             Transition from State ('2', 'B') to State ('2', 'A'): 0|-,
     2559             Transition from State ('2', 'A') to State ('2', 'A'): 1|-,
     2560             Transition from State ('2', 'A') to State ('2', 'B'): 1|-]
     2561        """
     2562        def function(transition1, transition2):
     2563            if transition1.word_in == transition2.word_in \
     2564                    and transition1.word_out == transition2.word_out:
     2565                return (transition1.word_in, transition1.word_out)
     2566            else:
     2567                raise LookupError
     2568
     2569        return self.product_FiniteStateMachine(
     2570            other, function,
     2571            only_accessible_components = only_accessible_components)
     2572
     2573
     2574    def composition(self, other, algorithm=None,
     2575                    only_accessible_components=True):
     2576        """
     2577        Returns a new transducer which is the composition of self and
     2578        other.
     2579
     2580        INPUT:
     2581
     2582        - ``algorithm`` -- can be one of the following
     2583
     2584          - ``direct`` -- The composition is calculated directly.
     2585
     2586            There can be arbitrarily many initial and final states,
     2587            but the input and output labels must have length 1.
     2588
     2589            WARNING: The output of other is fed into self.
     2590
     2591          - ``explorative`` -- An explorative algorithm is used.
     2592
     2593            At least the following restrictions apply, but are not
     2594            checked:
     2595            - both self and other have exactly one initial state
     2596            - all input labels of transitions have length exactly 1
     2597
     2598            The input alphabet of self has to be specified.
     2599
     2600            This is a very limited implementation of composition.
     2601            WARNING: The output of ``other`` is fed into ``self``.
     2602
     2603          If algorithm is ``None``, then the algorithm is chosen
     2604          automatically (at the moment always ``direct``).
     2605
     2606
     2607        EXAMPLES::
     2608
     2609            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     2610            ....:                initial_states=['A', 'B'], final_states=['B'],
     2611            ....:                determine_alphabets=True)
     2612            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     2613            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2614            ....:                initial_states=[1], final_states=[2],
     2615            ....:                determine_alphabets=True)
     2616            sage: Hd = F.composition(G, algorithm='direct')
     2617            sage: Hd.initial_states()
     2618            [State (1, 'B'), State (1, 'A')]
     2619            sage: Hd.transitions()
     2620            [Transition from State (1, 'B') to State (1, 'A'): 1|1,
     2621             Transition from State (1, 'A') to State (2, 'B'): 0|0,
     2622             Transition from State (2, 'B') to State (2, 'A'): 0|1,
     2623             Transition from State (2, 'A') to State (2, 'B'): 1|0]
     2624
     2625        ::
     2626
     2627            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     2628            ....:                 ('B', 'B', 0, 0)],
     2629            ....:                initial_states=['A'], final_states=['B'])
     2630            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     2631            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     2632            ....:                initial_states=[1], final_states=[1])
     2633            sage: He = G.composition(F, algorithm='explorative')
     2634            sage: He.transitions()
     2635            [Transition from State ('A', 1) to State (None, None): 0|-,
     2636             Transition from State ('A', 1) to State ('B', 2): 1|0,1,
     2637             Transition from State (None, None) to State (None, None): 0|-,
     2638             Transition from State (None, None) to State (None, None): 1|-,
     2639             Transition from State ('B', 2) to State ('B', 2): 0|1,
     2640             Transition from State ('B', 2) to State ('B', 1): 1|1,
     2641             Transition from State ('B', 1) to State ('B', 1): 0|0,
     2642             Transition from State ('B', 1) to State ('B', 2): 1|0]
     2643
     2644        TESTS:
     2645
     2646        Due to the limitations of the two algorithms the following
     2647        (examples from above, but different algorithm used) does not
     2648        give a full answer or does not work
     2649
     2650        ::
     2651
     2652            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     2653            ....:                initial_states=['A', 'B'], final_states=['B'],
     2654            ....:                determine_alphabets=True)
     2655            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     2656            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2657            ....:                initial_states=[1], final_states=[2],
     2658            ....:                determine_alphabets=True)
     2659            sage: He = F.composition(G, algorithm='explorative')
     2660            sage: He.initial_states()
     2661            [State (1, 'A')]
     2662            sage: He.transitions()
     2663            [Transition from State (1, 'A') to State (2, 'B'): 0|0,
     2664             Transition from State (1, 'A') to State (None, None): 1|-,
     2665             Transition from State (2, 'B') to State (2, 'A'): 0|1,
     2666             Transition from State (2, 'B') to State (None, None): 1|-,
     2667             Transition from State (None, None) to State (None, None): 0|-,
     2668             Transition from State (None, None) to State (None, None): 1|-,
     2669             Transition from State (2, 'A') to State (None, None): 0|-,
     2670             Transition from State (2, 'A') to State (2, 'B'): 1|0]
     2671
     2672        ::
     2673
     2674            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     2675            ....:                 ('B', 'B', 0, 0)],
     2676            ....:                initial_states=['A'], final_states=['B'])
     2677            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     2678            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     2679            ....:                initial_states=[1], final_states=[1])
     2680            sage: Hd = G.composition(F, algorithm='direct')
     2681            Traceback (most recent call last):
     2682            ...
     2683            LookupError: No state with label ('A', 1) found.
     2684        """
     2685        if algorithm==None:
     2686            algorithm = 'direct'
     2687        if algorithm=='direct':
     2688            return self._composition_direct_(other, only_accessible_components)
     2689        elif algorithm=='explorative':
     2690            return self._composition_explorative_(other)
     2691        else:
     2692            raise ValueError, "Unknown algorithm %s." % (algorithm,)
     2693
     2694
     2695    def _composition_direct_(self, other, only_accessible_components=True):
     2696        """
     2697        See :meth:`.composition` for details.
     2698
     2699        TESTS::
     2700
     2701            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     2702            ....:                initial_states=['A', 'B'], final_states=['B'],
     2703            ....:                determine_alphabets=True)
     2704            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     2705            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2706            ....:                initial_states=[1], final_states=[2],
     2707            ....:                determine_alphabets=True)
     2708            sage: Hd = F._composition_direct_(G)
     2709            sage: Hd.initial_states()
     2710            [State (1, 'B'), State (1, 'A')]
     2711            sage: Hd.transitions()
     2712            [Transition from State (1, 'B') to State (1, 'A'): 1|1,
     2713             Transition from State (1, 'A') to State (2, 'B'): 0|0,
     2714             Transition from State (2, 'B') to State (2, 'A'): 0|1,
     2715             Transition from State (2, 'A') to State (2, 'B'): 1|0]
     2716
     2717        """
     2718        def function(transition1, transition2):
     2719            if transition1.word_out == transition2.word_in:
     2720                return (transition1.word_in, transition2.word_out)
     2721            else:
     2722                raise LookupError
     2723
     2724        return other.product_FiniteStateMachine(
     2725            self, function,
     2726            only_accessible_components = only_accessible_components)
     2727
     2728
     2729    def _composition_explorative_(self, other):
     2730        """
     2731        See :meth:`.composition` for details.
     2732
     2733        TESTS::
     2734
     2735            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     2736            ....:                 ('B', 'B', 0, 0)],
     2737            ....:                initial_states=['A'], final_states=['B'])
     2738            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     2739            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     2740            ....:                initial_states=[1], final_states=[1])
     2741            sage: He = G._composition_explorative_(F)
     2742            sage: He.transitions()
     2743            [Transition from State ('A', 1) to State (None, None): 0|-,
     2744             Transition from State ('A', 1) to State ('B', 2): 1|0,1,
     2745             Transition from State (None, None) to State (None, None): 0|-,
     2746             Transition from State (None, None) to State (None, None): 1|-,
     2747             Transition from State ('B', 2) to State ('B', 2): 0|1,
     2748             Transition from State ('B', 2) to State ('B', 1): 1|1,
     2749             Transition from State ('B', 1) to State ('B', 1): 0|0,
     2750             Transition from State ('B', 1) to State ('B', 2): 1|0]
     2751
     2752        TODO:
     2753
     2754        The explorative algorithm should be re-implemented using the
     2755        process iterators of both finite state machines.
     2756        """
     2757        def composition_transition(state, input):
     2758            (state1, state2) = state
     2759            if state1 is None and state2 is None:
     2760                return ((None, None), [])
     2761            transition1 = None
     2762            for transition in other.iter_transitions(state1):
     2763                if transition.word_in == [input]:
     2764                    transition1 = transition
     2765                    break
     2766            if transition1 is None:
     2767                return((None, None), [])
     2768            new_state1 = transition1.to_state.label()
     2769            new_state2 = state2
     2770            output = []
     2771            for o in transition1.word_out:
     2772                transition2 = None
     2773                for transition in self.iter_transitions(new_state2):
     2774                    if transition.word_in == [o]:
     2775                        transition2 = transition
     2776                        break
     2777                if transition2 is None:
     2778                    return((None, None), [])
     2779                new_state2 = transition2.to_state.label()
     2780                output += transition2.word_out
     2781            return ((new_state1, new_state2), output)
     2782
     2783        F = FiniteStateMachine(input_alphabet=other.input_alphabet,
     2784                               data=composition_transition,
     2785                               initial_states=[(other.initial_states()[0].label(), self.initial_states()[0].label())])
     2786
     2787        for state1 in other.final_states():
     2788            for state2 in self.final_states():
     2789                F.state((state1.label(), state2.label())).is_final = True
     2790        return F
     2791
     2792
     2793    def input_projection(self):
     2794        """
     2795        Returns a automaton where the output of each transition of
     2796        self is deleted.
     2797
     2798        EXAMPLES::
     2799
     2800            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     2801            ....:                         ('B', 'B', 1, 0)])
     2802            sage: G = F.input_projection()
     2803            sage: G.transitions()
     2804            [Transition from State 'A' to State 'B': 0|-,
     2805             Transition from State 'A' to State 'A': 1|-,
     2806             Transition from State 'B' to State 'B': 1|-]
     2807        """
     2808        return self.projection(what='input')
     2809
     2810
     2811    def output_projection(self):
     2812        """
     2813        Returns a automaton where the input of each transition of self
     2814        is deleted and the new input is the original output.
     2815
     2816        EXAMPLES::
     2817
     2818            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     2819            ....:                         ('B', 'B', 1, 0)])
     2820            sage: G = F.output_projection()
     2821            sage: G.transitions()
     2822            [Transition from State 'A' to State 'B': 1|-,
     2823             Transition from State 'A' to State 'A': 1|-,
     2824             Transition from State 'B' to State 'B': 0|-]
     2825        """
     2826        return self.projection(what='output')
     2827
     2828
     2829    def projection(self, what='input'):
     2830        """
     2831        Returns an Automaton which transition labels are the projection
     2832        of the transition labels of the input.
     2833
     2834        EXAMPLES::
     2835
     2836            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     2837            ....:                         ('B', 'B', 1, 0)])
     2838            sage: G = F.projection(what='output')
     2839            sage: G.transitions()
     2840            [Transition from State 'A' to State 'B': 1|-,
     2841             Transition from State 'A' to State 'A': 1|-,
     2842             Transition from State 'B' to State 'B': 0|-]
     2843        """
     2844        new = Automaton()
     2845
     2846        state_mapping = {}
     2847        for state in self.iter_states():
     2848            state_mapping[state] = new.add_state(deepcopy(state))
     2849        for transition in self.iter_transitions():
     2850            if what == 'input':
     2851                new_word_in = transition.word_in
     2852            elif what == 'output':
     2853                new_word_in = transition.word_out
     2854            else:
     2855                raise NotImplementedError
     2856            new.add_transition((state_mapping[transition.from_state],
     2857                                state_mapping[transition.to_state],
     2858                                new_word_in, None))
     2859        if what == 'input':
     2860            new.input_alphabet = self.input_alphabet
     2861        elif what == 'output':
     2862            new.input_alphabet = self.output_alphabet
     2863        else:
     2864            raise NotImplementedError
     2865        return new
     2866
     2867
     2868    def determinisation(self):
     2869        """
     2870        Returns a deterministic automaton which accepts the same input
     2871        words as the original one.
     2872
     2873        The input alphabet must be specified. It is restricted to nice
     2874        cases: only ``mode=automaton`` is considered, input words have to
     2875        have length at most `1`.
     2876
     2877        EXAMPLES::
     2878
     2879            sage: aut = Automaton([('A', 'A', 0), ('A', 'B', 1), ('B', 'B', 1)],
     2880            ....:                 initial_states=['A'], final_states=['B'])
     2881            sage: aut.determinisation().transitions()
     2882            [Transition from State frozenset(['A'])
     2883                          to State frozenset(['A']): 0|-,
     2884            Transition from State frozenset(['A'])
     2885                          to State frozenset(['B']): 1|-,
     2886             Transition from State frozenset(['B'])
     2887                          to State frozenset([]): 0|-,
     2888             Transition from State frozenset(['B'])
     2889                          to State frozenset(['B']): 1|-,
     2890             Transition from State frozenset([])
     2891                          to State frozenset([]): 0|-,
     2892             Transition from State frozenset([])
     2893                          to State frozenset([]): 1|-]
     2894
     2895        ::
     2896
     2897            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     2898            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     2899            ....:               initial_states=['A'], final_states=['C'])
     2900            sage: A.determinisation().states()
     2901            [State frozenset(['A']), State frozenset(['A', 'B']),
     2902            State frozenset(['A', 'C']), State frozenset(['A', 'C', 'B'])]
     2903        """
     2904        assert self.mode == 'automaton'
     2905        for transition in self.transitions():
     2906            assert len(transition.word_in) <= 1, "%s has input label of length > 1, which we cannot handle" % (transition,)
     2907
     2908        epsilon_successors = {}
     2909        direct_epsilon_successors = {}
     2910        for state in self.states():
     2911            direct_epsilon_successors[state.label()] = set(map(lambda t:t.to_state.label(), filter(lambda transition: len(transition.word_in) == 0, self.transitions(state))))
     2912            epsilon_successors[state.label()] = set([state.label()])
     2913
     2914        old_count_epsilon_successors = 0
     2915        count_epsilon_successors = len(epsilon_successors)
     2916
     2917        while old_count_epsilon_successors < count_epsilon_successors:
     2918            old_count_epsilon_successors = count_epsilon_successors
     2919            count_epsilon_successors = 0
     2920            for state in self.states():
     2921                for direct_successor in direct_epsilon_successors[state.label()]:
     2922                    epsilon_successors[state.label()] = epsilon_successors[state.label()].union(epsilon_successors[direct_successor])
     2923                count_epsilon_successors += len(epsilon_successors[state.label()])
     2924
     2925
     2926        def set_transition(states, letter):
     2927            result = set()
     2928            for state in states:
     2929                for transition in self.transitions(state):
     2930                    if transition.word_in == [letter]:
     2931                        result.add(transition.to_state.label())
     2932            result = result.union(*map(lambda s:epsilon_successors[s], result))
     2933            return (frozenset(result), [])
     2934
     2935        result = Automaton(input_alphabet=self.input_alphabet,
     2936                           initial_states=[ frozenset([state.label() for state in self.initial_states()])],
     2937                          data=set_transition )
     2938
     2939        for state in result.states():
     2940            if state.label().intersection(map(lambda s: s.label(), self.final_states())):
     2941                state.is_final = True
     2942
     2943        return result
     2944
     2945
     2946    def minimization(self, algorithm=None):
     2947        """
     2948        Returns the minimization of the input automaton as a new automaton.
     2949
     2950        The resulting automaton is deterministic and has a minimal
     2951        number of states. Either Moore's algorithm is used (default or
     2952        ``algorithm='Moore'``), or Brzozowski's algorithm when
     2953        ``algorithm='Brzozowski'``.  Only ``mode=automaton`` is
     2954        accepted. Use :meth:`.simplification` for transducers.
     2955
     2956        EXAMPLES::
     2957
     2958            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     2959            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     2960            ....:               initial_states=['A'], final_states=['C'])
     2961            sage: B = A.minimization(algorithm='Brzozowski')
     2962            sage: B.transitions(B.states()[1])
     2963            [Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2964            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2965            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2966            frozenset(['A', 'C']), frozenset(['C'])]): 0|-,
     2967            Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2968            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2969            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2970            frozenset(['A', 'C'])]): 1|-]
     2971            sage: len(B.states())
     2972            3
     2973            sage: C = A.minimization(algorithm='Brzozowski')
     2974            sage: C.transitions(C.states()[1])
     2975            [Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2976            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2977            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2978            frozenset(['A', 'C']), frozenset(['C'])]): 0|-,
     2979            Transition from State frozenset([frozenset(['A', 'C', 'B']),
     2980            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to State
     2981            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     2982            frozenset(['A', 'C'])]): 1|-]
     2983            sage: len(C.states())
     2984            3
     2985
     2986        ::
     2987
     2988            sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'),
     2989            ....:                  ('3', '2', 'a'), ('2', '1', 'b'),
     2990            ....:                  ('3', '4', 'a'), ('4', '3', 'b')],
     2991            ....:                  initial_states=['1'], final_states=['1'])
     2992            sage: min = aut.minimization(algorithm='Brzozowski')
     2993            sage: [len(min.states()), len(aut.states())]
     2994            [3, 4]
     2995            sage: min = aut.minimization(algorithm='Moore')
     2996            Traceback (most recent call last):
     2997            ...
     2998            NotImplementedError: Minimization via Moore's Algorithm is only
     2999            implemented for deterministic finite state machines
     3000        """
     3001        if self.mode is None:
     3002            raise NotImplementedError, "The mode attribute must be set."
     3003        if self.mode == 'transducer':
     3004            raise NotImplementedError, "Minimization for Transducer is not implemented. Try the simplification method."
     3005        if not self.mode == 'automaton':
     3006            raise NotImplementedError
     3007
     3008        if algorithm is None or algorithm == "Moore":
     3009            return self._minimization_Moore_()
     3010        elif algorithm == "Brzozowski":
     3011            return self._minimization_Brzozowski_()
     3012        else:
     3013            raise NotImplementedError, "Algorithm '%s' is not implemented. Choose 'Moore' or 'Brzozowski'" % algorithm
     3014
     3015
     3016    def _minimization_Brzozowski_(self):
     3017        """
     3018        Returns a minimized automaton by using Brzozowski's algorithm.
     3019
     3020        See also :meth:`.minimization`.
     3021
     3022        TESTS::
     3023
     3024            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     3025            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     3026            ....:               initial_states=['A'], final_states=['C'])
     3027            sage: B = A._minimization_Brzozowski_()
     3028            sage: len(B.states())
     3029            3
     3030        """
     3031        return self.transposition().determinisation().transposition().determinisation()
     3032
     3033
     3034    def _minimization_Moore_(self):
     3035        """
     3036        Returns a minimized automaton by using Brzozowski's algorithm.
     3037
     3038        See also :meth:`.minimization`.
     3039
     3040        TESTS::
     3041
     3042            sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'),
     3043            ....:                  ('3', '2', 'a'), ('2', '1', 'b'),
     3044            ....:                  ('3', '4', 'a'), ('4', '3', 'b')],
     3045            ....:                  initial_states=['1'], final_states=['1'])
     3046            sage: min = aut._minimization_Moore_()
     3047            Traceback (most recent call last):
     3048            ...
     3049            NotImplementedError: Minimization via Moore's Algorithm is only
     3050            implemented for deterministic finite state machines
     3051        """
     3052        return self.quotient(self.equivalence_classes())
     3053
     3054
     3055    def transposition(self):
     3056        """
     3057        Returns a new finite state machine, where all transitions of the
     3058        input finite state machine are reversed.
     3059
     3060        EXAMPLES::
     3061
     3062            sage: aut=Automaton([('A', 'A', 0), ('A', 'A', 1), ('A', 'B', 0)],
     3063            ....:               initial_states=['A'], final_states=['B'])
     3064            sage: aut.transposition().transitions('B')
     3065            [Transition from State 'B' to State 'A': 0|-]
     3066
     3067        ::
     3068
     3069            sage: aut=Automaton([('1', '1', 1), ('1', '2', 0), ('2', '2', 0)],
     3070            ....:               initial_states=['1'], final_states=['1', '2'])
     3071            sage: aut.transposition().initial_states()
     3072            [State '1', State '2']
     3073        """
     3074        transposition=FiniteStateMachine(input_alphabet=self.input_alphabet,
     3075                                         mode=self.mode)
     3076        for state in self.states():
     3077            transposition.add_state(deepcopy(state))
     3078
     3079        for transition in self.transitions():
     3080            transposition.add_transition(
     3081                transition.to_state.label(), transition.from_state.label(),
     3082                transition.word_in, transition.word_out)
     3083
     3084        for initial in self.initial_states():
     3085            state=transposition.state(initial.label())
     3086            if not initial.is_final:
     3087                state.is_final=True
     3088                state.is_initial=False
     3089
     3090        for final in self.final_states():
     3091            state=transposition.state(final.label())
     3092            if not final.is_initial:
     3093                state.is_final=False
     3094                state.is_initial=True
     3095
     3096        return transposition
     3097
     3098
     3099    def split_transitions(self):
     3100        """
     3101        Returns a new transducer, where all transitions in self with input
     3102        labels consisting of more than one letter
     3103        are replaced by a path of the corresponding length.
     3104
     3105        EXAMPLES::
     3106
     3107            sage: A=Transducer([('A', 'B', [1, 2, 3], 0)],
     3108            ....:              initial_states=['A'], final_states=['B'])
     3109            sage: A.split_transitions().states()
     3110            [State (State 'A', ()), State (State 'B', ()),
     3111             State (State 'A', (1,)), State (State 'A', (1, 2))]
     3112        """
     3113        new=FiniteStateMachine(mode=self.mode,
     3114                               input_alphabet=self.input_alphabet,
     3115                               output_alphabet=self.output_alphabet)
     3116        for state in self.states():
     3117            new.add_state(FSMState((state, ()), is_initial=state.is_initial,
     3118                                   is_final=state.is_final))
     3119        for transition in self.transitions():
     3120            for j in range(len(transition.word_in)-1):
     3121                new.add_transition( (
     3122                        (transition.from_state, tuple(transition.word_in[:j])),
     3123                        (transition.from_state, tuple(transition.word_in[:j+1])),
     3124                        transition.word_in[j],
     3125                        []))
     3126            new.add_transition((
     3127                    (transition.from_state, tuple(transition.word_in[:-1])),
     3128                    (transition.to_state, ()),
     3129                    transition.word_in[-1:],
     3130                    transition.word_out))
     3131        return new
     3132
     3133
     3134    # *************************************************************************
     3135    # simplifications
     3136    # *************************************************************************
     3137
     3138
     3139    def prepone_output(self):
     3140        """
     3141        Apply the following to each state `s` (except initial and
     3142        final states) of the finite state machine as often as
     3143        possible:
     3144
     3145        If the letter a is prefix of the output label of all
     3146        transitions from `s`, then remove it from all these labels and
     3147        append it to all output labels of all transitions leading to
     3148        `s`.
     3149
     3150        We assume that the states have no output labels.
     3151
     3152        EXAMPLES::
     3153
     3154            sage: A = Transducer([('A', 'B', 1, 1), ('B', 'B', 0, 0), ('B', 'C', 1, 0)],
     3155            ....:                initial_states=['A'], final_states=['C'])
     3156            sage: A.prepone_output()
     3157            sage: A.transitions()
     3158            [Transition from State 'A' to State 'B': 1|1,0,
     3159             Transition from State 'B' to State 'B': 0|0,
     3160             Transition from State 'B' to State 'C': 1|-]
     3161
     3162        ::
     3163
     3164            sage: B = Transducer([('A', 'B', 0, 1), ('B', 'C', 1, [1, 1]), ('B', 'C', 0, 1)],
     3165            ....:                initial_states=['A'], final_states=['C'])
     3166            sage: B.prepone_output()
     3167            sage: B.transitions()
     3168            [Transition from State 'A' to State 'B': 0|1,1,
     3169             Transition from State 'B' to State 'C': 1|1,
     3170             Transition from State 'B' to State 'C': 0|-]
     3171        """
     3172        def find_common_output(state):
     3173            if len(filter(lambda transition: len(transition.word_out) == 0, self.transitions(state))) > 0:
     3174                return ()
     3175            first_letters = set(map(lambda transition: transition.word_out[0], self.transitions(state)))
     3176            if len(first_letters) == 1:
     3177                return (first_letters.pop(),)
     3178            return ()
     3179
     3180        changed = 1
     3181        iteration = 0
     3182        while changed > 0:
     3183            changed = 0
     3184            iteration += 1
     3185            for state in self.states():
     3186                if state.is_initial or state.is_final:
     3187                    continue
     3188                assert len(state.word_out) == 0, \
     3189                    "prepone_output assumes that all states have empty output word, but state %s has output word %s" % \
     3190                    (state, state.word_out)
     3191                common_output = find_common_output(state)
     3192                if len(common_output) > 0:
     3193                    changed += 1
     3194                    for transition in self.transitions(state):
     3195                        assert transition.word_out[0] == common_output[0]
     3196                        transition.word_out = transition.word_out[1:]
     3197                    for transition in self.transitions():
     3198                        if transition.to_state == state:
     3199                            transition.word_out.append(common_output[0])
     3200
     3201
     3202    def equivalence_classes(self):
     3203        """
     3204        Two states `a` and `b` are equivalent, if and only if for each
     3205        input label word_in the following holds:
     3206
     3207        For paths `p_a` from `a` to `a'` with input label ``word_in``
     3208        and output label ``word_out_a`` and `p_b` from `b` to `b'`
     3209        with input label ``word_in`` and output label ``word_out_b``,
     3210        we have ``word_out_a=word_out_b``, `a'` and `b'` have the same
     3211        output label and are both final or both non-final.
     3212
     3213        The function :meth:`.equivalence_classes` returns a list of
     3214        the equivalence classes to this equivalence relation.
     3215
     3216        This is one step of Moore's minimization algorithm.
     3217
     3218        .. SEEALSO::
     3219
     3220            :meth:`.minimization`
     3221
     3222        EXAMPLES::
     3223
     3224            sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0),
     3225            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     3226            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     3227            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     3228            sage: fsm.equivalence_classes()
     3229            [[State 'B', State 'D'], [State 'A', State 'C']]
     3230        """
     3231
     3232        # Two states a and b are said to be 0-equivalent, if their output
     3233        # labels agree and if they are both final or non-final.
     3234        #
     3235        # For some j >= 1, two states a and b are said to be j-equivalent, if
     3236        # they are j-1 equivalent and if for each element letter letter_in of
     3237        # the input alphabet and transitions t_a from a with input label
     3238        # letter_in, output label word_out_a to a' and t_b from b with input
     3239        # label letter_in, output label word_out_b to b', we have
     3240        # word_out_a=word_out_b and a' and b' are j-1 equivalent.
     3241
     3242        # If for some j the relations j-1 equivalent and j-equivalent
     3243        # coincide, then they are equal to the equivalence relation described
     3244        # in the docstring.
     3245
     3246        # classes_current holds the equivalence classes of j-equivalence,
     3247        # classes_previous holds the equivalence classes of j-1 equivalence.
     3248
     3249        if not self.is_deterministic():
     3250            raise NotImplementedError, "Minimization via Moore's Algorithm is only implemented for deterministic finite state machines"
     3251
     3252        # initialize with 0-equivalence
     3253        classes_previous=[]
     3254        key_0=lambda state: (state.is_final, state.word_out)
     3255        states_sorted=sorted( self.states(), key=key_0)
     3256        states_grouped= itertools.groupby( states_sorted, key=key_0)
     3257        classes_current=[ list(equivalence_class) for
     3258                          (key,equivalence_class) in states_grouped ]
     3259
     3260        while len(classes_current)!=len(classes_previous):
     3261            class_of={}
     3262            classes_previous=classes_current
     3263            classes_current=[]
     3264
     3265            for k in range(len(classes_previous)):
     3266                for state in classes_previous[k]:
     3267                    class_of[state]=k
     3268
     3269            key_current=lambda state: sorted(
     3270                [ (transition.word_in,
     3271                   transition.word_out,
     3272                   class_of[transition.to_state])
     3273                  for transition in state.transitions ]
     3274                )
     3275
     3276            for class_previous in classes_previous:
     3277                states_sorted = sorted( class_previous, key=key_current)
     3278                states_grouped = itertools.groupby( states_sorted, key=key_current)
     3279                classes_current.extend([ list(equivalence_class) for
     3280                                       (key,equivalence_class) in states_grouped ])
     3281
     3282        return classes_current
     3283
     3284
     3285    def quotient(self, classes):
     3286        """
     3287        Constructs the quotient with respect to the equivalence
     3288        classes.
     3289
     3290        ``classes`` is a list of equivalence classes of states.
     3291
     3292        Assume that `c` is a class and `s`, `s'` are states in `c`. If
     3293        there is a transition from `s` to some `t` with input label
     3294        ``word_in`` and output label ``word_out``, then there has to
     3295        be a transition from `s'` to some `t'` with input label
     3296        ``word_in`` and output label ``word_out`` such that `s'` and
     3297        `t'` are states of the same class `c'`. Then there is a
     3298        transition from `c` to `c'` in the quotient with input label
     3299        ``word_in`` and output label ``word_out``.
     3300
     3301        Non-initial states may be merged with initial states, the
     3302        resulting state is an initial state.
     3303
     3304        All states in a class must have the same ``is_final`` and
     3305        ``word_out`` values.
     3306
     3307        EXAMPLES::
     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` oder
     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#*****************************************************************************