Ticket #15078: trac_15078_fsm_automata_transducers.8.patch

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

    # HG changeset patch
    # User Daniel Krenn <math+sage@danielkrenn.at>
    # Date 1384187087 -3600
    # Node ID 08525a9e1043652d14541f762072c292096fd241
    # Parent  7285f9d8a2aa2116547de1028090bb921e1d128e
    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  
    8080   designs
    8181   species
    8282   developer
     83   sage/combinat/finite_state_machine
    8384   words
    8485
    8586   sage/combinat/dict_addition
  • sage/combinat/all.py

    diff --git a/sage/combinat/all.py b/sage/combinat/all.py
    a b  
    159159# Gelfand-Tsetlin patterns
    160160from gelfand_tsetlin_patterns import GelfandTsetlinPattern, GelfandTsetlinPatterns
    161161
     162# Finite State Machines (Automaton, Transducer)
     163sage.misc.lazy_import.lazy_import('sage.combinat.finite_state_machine',
     164                                  ['Automaton', 'Transducer',
     165                                   'FiniteStateMachine'])
    162166# Binary Recurrence Sequences
    163167from binary_recurrence_sequences import BinaryRecurrenceSequence
  • 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: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     28    sage: day = FSMState('day')
     29    sage: night = FSMState('night')
     30    sage: sunrise = FSMTransition(night, day)
     31    sage: sunset = FSMTransition(day, night)
     32
     33And now let's add those states and transitions to our finite state machine::
     34
     35    sage: fsm.add_transition(sunrise)
     36    Transition from 'night' to 'day': -|-
     37    sage: fsm.add_transition(sunset)
     38    Transition from 'day' to 'night': -|-
     39
     40Note that the states are added automatically, since they are present
     41in the transitions. We could add the states manually by
     42
     43::
     44
     45    sage: fsm.add_state(day)
     46    'day'
     47    sage: fsm.add_state(night)
     48    'night'
     49
     50Anyhow, we got the following finite state machine::
     51
     52    sage: fsm
     53    Finite state machine with 2 states
     54
     55We can also visualize it as a graph by
     56
     57::
     58
     59    sage: fsm.graph()
     60    Digraph on 2 vertices
     61
     62Alternatively, we could have created the finite state machine above
     63simply by
     64
     65::
     66
     67    sage: FiniteStateMachine([('night', 'day'), ('day', 'night')])
     68    Finite state machine with 2 states
     69
     70or by
     71
     72::
     73
     74    sage: fsm = FiniteStateMachine()
     75    sage: day = fsm.add_state('day')
     76    sage: night = fsm.add_state('night')
     77    sage: sunrise = fsm.add_transition(night, day)
     78    sage: sunset = fsm.add_transition(day, night)
     79    sage: fsm
     80    Finite state machine with 2 states
     81
     82A simple Automaton (recognizing NAFs)
     83---------------------------------------
     84
     85We want to build an automaton which recognizes non-adjacent forms
     86(NAFs), i.e., sequences which have no adjacent non-zeros.
     87We use `0`, `1`, and `-1` as digits::
     88
     89    sage: NAF = Automaton(
     90    ....:     {'A': [('A', 0), ('B', 1), ('B', -1)], 'B': [('A', 0)]})
     91    sage: NAF.state('A').is_initial = True
     92    sage: NAF.state('A').is_final = True
     93    sage: NAF.state('B').is_final = True
     94    sage: NAF
     95    Automaton with 2 states
     96
     97Of course, we could have specified the initial and final states
     98directly in the definition of ``NAF`` by ``initial_states=['A']`` and
     99``final_states=['A', 'B']``.
     100
     101So let's test the automaton with some input::
     102
     103    sage: NAF([0])[0]
     104    True
     105    sage: NAF([0, 1])[0]
     106    True
     107    sage: NAF([1, -1])[0]
     108    False
     109    sage: NAF([0, -1, 0, 1])[0]
     110    True
     111    sage: NAF([0, -1, -1, -1, 0])[0]
     112    False
     113    sage: NAF([-1, 0, 0, 1, 1])[0]
     114    False
     115
     116Alternatively, we could call that by
     117
     118::
     119
     120    sage: NAF.process([-1, 0, 0, 1, 1])[0]
     121    False
     122
     123A simple transducer (binary inverter)
     124-------------------------------------
     125
     126Let's build a simple transducer, which rewrites a binary word by
     127iverting each bit::
     128
     129    sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     130    ....:     initial_states=['A'], final_states=['A'])
     131
     132We can look at the states and transitions::
     133
     134    sage: inverter.states()
     135    ['A']
     136    sage: for t in inverter.transitions():
     137    ....:     print t
     138    Transition from 'A' to 'A': 0|1
     139    Transition from 'A' to 'A': 1|0
     140
     141Now we apply a word to it and see what the transducer does::
     142
     143    sage: inverter([0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1])
     144    (True, 'A', [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0])
     145
     146``True`` means, that we landed in a final state, that state is labeled
     147``'A'``, and we also got an output.
     148
     149
     150A transducer which performs division by `3` in binary
     151-----------------------------------------------------
     152
     153Now we build a transducer, which divides a binary number by 3.
     154The labels of the states are the remainder of the division.
     155The transition function is
     156
     157::
     158
     159    sage: def f(state_from, read):
     160    ....:     if state_from + read <= 1:
     161    ....:         state_to = 2*state_from + read
     162    ....:         write = 0
     163    ....:     else:
     164    ....:         state_to = 2*state_from + read - 3
     165    ....:         write = 1
     166    ....:     return (state_to, write)
     167
     168We get the transducer with
     169
     170::
     171
     172    sage: D = Transducer(f, initial_states=[0], final_states=[0],
     173    ....:                input_alphabet=[0, 1])
     174
     175Now we want to divide 13 by 3::
     176
     177    sage: D([1, 1, 0, 1])
     178    (False, 1, [0, 1, 0, 0])
     179
     180So we have 13 : 3 = 4 and the reminder is 1. ``False`` means 13 is not
     181divisible by 3.
     182
     183
     184Using the hook-functions
     185------------------------
     186
     187Let's use the previous example "divison by `3`" to demonstrate the
     188optional state and transition parameters ``hook``.
     189
     190First, we define, what those functions should do. In our case, this is
     191just saying in which state we are and which transition we take
     192
     193::
     194
     195    sage: def state_hook(state, process):
     196    ....:     print "We are now in State %s." % (state.label(),)
     197    sage: from sage.combinat.finite_state_machine import FSMWordSymbol
     198    sage: def transition_hook(transition, process):
     199    ....:     print ("Currently we go from %s to %s, "
     200    ....:            "reading %s and writing %s." % (
     201    ....:                transition.from_state, transition.to_state,
     202    ....:                FSMWordSymbol(transition.word_in),
     203    ....:                FSMWordSymbol(transition.word_out)))
     204
     205Now, let's add these hook-functions to the existing transducer::
     206
     207    sage: for s in D.iter_states():
     208    ....:     s.hook = state_hook
     209    sage: for t in D.iter_transitions():
     210    ....:     t.hook = transition_hook
     211
     212Rerunning the process again now gives the following output::
     213
     214    sage: D.process([1, 1, 0, 1])
     215    We are now in State 0.
     216    Currently we go from 0 to 1, reading 1 and writing 0.
     217    We are now in State 1.
     218    Currently we go from 1 to 0, reading 1 and writing 1.
     219    We are now in State 0.
     220    Currently we go from 0 to 0, reading 0 and writing 0.
     221    We are now in State 0.
     222    Currently we go from 0 to 1, reading 1 and writing 0.
     223    We are now in State 1.
     224    (False, 1, [0, 1, 0, 0])
     225
     226The example above just explains the basic idea of using
     227hook-functions. In the following, we will use those hooks more seriously.
     228
     229
     230Detecting sequences with same number of `0` and `1`
     231---------------------------------------------------
     232
     233Suppose we have a binary input and want to accept all sequences with
     234the same number of `0` and `1`. This cannot be done with a finite
     235automaton. Anyhow, we can make usage of the hook functions to extend
     236our finite automaton by a counter::
     237
     238    sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     239    sage: C = Automaton()
     240    sage: def update_counter(state, process):
     241    ....:     l = process.read_letter()
     242    ....:     process.fsm.counter += 1 if l == 1 else -1
     243    ....:     if process.fsm.counter > 0:
     244    ....:         next_state = 'positive'
     245    ....:     elif process.fsm.counter < 0:
     246    ....:         next_state = 'negative'
     247    ....:     else:
     248    ....:         next_state = 'zero'
     249    ....:     return FSMTransition(state, process.fsm.state(next_state),
     250    ....:                          l, process.fsm.counter)
     251    sage: C.add_state(FSMState('zero', hook=update_counter,
     252    ....:             is_initial=True, is_final=True))
     253    'zero'
     254    sage: C.add_state(FSMState('positive', hook=update_counter))
     255    'positive'
     256    sage: C.add_state(FSMState('negative', hook=update_counter))
     257    'negative'
     258
     259Now, let's input some sequence::
     260
     261    sage: C.counter = 0; C([1, 1, 1, 1, 0, 0])
     262    (False, 'positive', [1, 2, 3, 4, 3, 2])
     263
     264The result is False, since there are four `1` but only two `0`. We
     265land in the state ``positive`` and we can also see the values of the
     266counter in each step.
     267
     268Let's try some other examples::
     269
     270    sage: C.counter = 0; C([1, 1, 0, 0])
     271    (True, 'zero', [1, 2, 1, 0])
     272    sage: C.counter = 0; C([0, 1, 0, 0])
     273    (False, 'negative', [-1, 0, -1, -2])
     274
     275
     276AUTHORS:
     277
     278- Daniel Krenn (2012-03-27): initial version
     279- Clemens Heuberger (2012-04-05): initial version
     280- Sara Kropf (2012-04-17): initial version
     281- Clemens Heuberger (2013-08-21): release candidate for Sage patch
     282- Daniel Krenn (2013-08-21): release candidate for Sage patch
     283- Sara Kropf (2013-08-21): release candidate for Sage patch
     284- Clemens Heuberger (2013-09-02): documentation improved
     285- Daniel Krenn (2013-09-13): comments from trac worked in
     286- Clemens Heuberger (2013-11-03): output (labels) of determinisation,
     287    product, composition, etc. changed (for consistency),
     288    representation of state changed, documentation improved
     289- Daniel Krenn (2013-11-04): whitespaces in documentation corrected
     290- Clemens Heuberger (2013-11-04): full_group_by added
     291- Daniel Krenn (2013-11-04): next release candidate for Sage patch
     292- Sara Kropf (2013-11-08): fix for adjacency matrix
     293- Clemens Heuberger (2013-11-11): fix for prepone_output
     294- Daniel Krenn (2013-11-11): comments from trac 15078 included:
     295    docstring of FiniteStateMachine rewritten, Automaton and Transducer
     296    inherited from FiniteStateMachine
     297- Daniel Krenn (2013-11-25): documentation improved according to
     298    comments from trac 15078
     299
     300ACKNOWLEDGEMENT:
     301
     302- Daniel Krenn, Clemens Heuberger and Sara Kropf are supported by the
     303  Austrian Science Fund (FWF): P 24644-N26.
     304
     305"""
     306
     307#*****************************************************************************
     308#  Copyright (C) 2012, 2013 Daniel Krenn <math+sage@danielkrenn.at>
     309#                2012, 2013 Clemens Heuberger <clemens.heuberger@aau.at>
     310#                2012, 2013 Sara Kropf <sara.kropf@aau.at>
     311#
     312#  Distributed under the terms of the GNU General Public License (GPL)
     313#  as published by the Free Software Foundation; either version 2 of
     314#  the License, or (at your option) any later version.
     315#                http://www.gnu.org/licenses/
     316#*****************************************************************************
     317
     318from sage.structure.sage_object import SageObject
     319from sage.graphs.digraph import DiGraph
     320from sage.matrix.constructor import matrix
     321from sage.rings.integer_ring import ZZ
     322from sage.calculus.var import var
     323from sage.misc.latex import latex
     324from sage.functions.trig import cos, sin, atan2
     325from sage.symbolic.constants import pi
     326
     327from copy import copy
     328from copy import deepcopy
     329
     330import itertools
     331from collections import defaultdict
     332
     333
     334def full_group_by(l, key=lambda x: x):
     335    """
     336    Group iterable ``l`` by values of ``key``.
     337
     338    INPUT:
     339
     340    - iterable ``l``
     341    - key function ``key``
     342
     343    OUTPUT:
     344
     345    A list of pairs ``(k, elements)`` such that ``key(e)=k`` for all
     346    ``e`` in ``elements``.
     347
     348    This is similar to ``itertools.groupby`` except that lists are
     349    returned instead of iterables and no prior sorting is required.
     350
     351    We do not require
     352
     353    - that the keys are sortable (in contrast to the
     354      approach via ``sorted`` and ``itertools.groupby``) and
     355    - that the keys are hashable (in contrast to the
     356      implementation proposed in `<http://stackoverflow.com/a/15250161>`_).
     357
     358    However, it is required
     359
     360    - that distinct keys have distinct ``str``-representations.
     361
     362    The implementation is inspired by
     363    `<http://stackoverflow.com/a/15250161>`_, but non-hashable keys are
     364    allowed.
     365
     366    EXAMPLES::
     367
     368        sage: from sage.combinat.finite_state_machine import full_group_by
     369        sage: t = [2/x, 1/x, 2/x]
     370        sage: r = full_group_by([0,1,2], key=lambda i:t[i])
     371        sage: sorted(r, key=lambda p:p[1])
     372        [(2/x, [0, 2]), (1/x, [1])]
     373        sage: from itertools import groupby
     374        sage: for k, elements in groupby(sorted([0,1,2],
     375        ....:                            key=lambda i:t[i]),
     376        ....:                            key=lambda i:t[i]):
     377        ....:     print k, list(elements)
     378        2/x [0]
     379        1/x [1]
     380        2/x [2]
     381
     382    Note that the behavior is different from ``itertools.groupby``
     383    because neither `1/x<2/x` nor `2/x<1/x` does hold.
     384
     385    Here, the result ``r`` has been sorted in order to guarantee a
     386    consistent order for the doctest suite.
     387    """
     388    elements = defaultdict(list)
     389    original_keys = {}
     390    for item in l:
     391        k = key(item)
     392        s = str(k)
     393        if s in original_keys:
     394            if original_keys[s]!=k:
     395                raise ValueError("Two distinct elements with representation "
     396                                 "%s " % s)
     397        else:
     398            original_keys[s]=k
     399        elements[s].append(item)
     400    return [(original_keys[s], values ) for (s, values) in elements.items()]
     401
     402#*****************************************************************************
     403
     404FSMEmptyWordSymbol = '-'
     405
     406def FSMLetterSymbol(letter):
     407    """
     408    Returns a string associated to the input letter.
     409
     410    INPUT:
     411
     412    - ``letter`` -- the input letter or ``None`` (representing the
     413      empty word).
     414
     415    OUTPUT:
     416   
     417    If ``letter`` is ``None`` the symbol for the empty word
     418    ``FSMEmptyWordSymbol`` is returned, otherwise the string
     419    associated to the letter.
     420
     421    EXAMPLES::
     422
     423        sage: from sage.combinat.finite_state_machine import FSMLetterSymbol
     424        sage: FSMLetterSymbol(0)
     425        '0'
     426        sage: FSMLetterSymbol(None)
     427        '-'
     428    """
     429    return FSMEmptyWordSymbol if letter is None else repr(letter)
     430
     431
     432def FSMWordSymbol(word):
     433    """
     434    Returns a string of ``word``. It may returns the symbol of the
     435    empty word ``FSMEmptyWordSymbol``.
     436
     437    INPUT:
     438
     439    - ``word`` -- the input word.
     440
     441    OUTPUT:
     442
     443    A string of ``word``.
     444
     445    EXAMPLES::
     446
     447        sage: from sage.combinat.finite_state_machine import FSMWordSymbol
     448        sage: FSMWordSymbol([0, 1, 1])
     449        '0,1,1'
     450    """
     451    if not isinstance(word, list):
     452        return FSMLetterSymbol(word)
     453    if len(word) == 0:
     454        return FSMEmptyWordSymbol
     455    s = ''
     456    for letter in word:
     457        s += (',' if len(s) > 0 else '') + FSMLetterSymbol(letter)
     458    return s
     459
     460
     461#*****************************************************************************
     462
     463
     464def is_FSMState(S):
     465    """
     466    Tests whether or not ``S`` inherits from :class:`FSMState`.
     467
     468    TESTS::
     469
     470        sage: from sage.combinat.finite_state_machine import is_FSMState, FSMState
     471        sage: is_FSMState(FSMState('A'))
     472        True
     473    """
     474    return isinstance(S, FSMState)
     475
     476
     477class FSMState(SageObject):
     478    """
     479    Class for a state of a finite state machine.
     480
     481    INPUT:
     482
     483    - ``label`` -- the label of the state.
     484
     485    - ``word_out`` -- (default: ``None``) a word that is written when
     486      the state is reached.
     487
     488    - ``is_initial`` -- (default: ``False``)
     489
     490    - ``is_final`` -- (default: ``False``)
     491
     492    - ``hook`` -- (default: ``None``) A function which is called when
     493      the state is reached during processing input.
     494
     495    OUTPUT:
     496
     497    Returns a state of a finite state machine.
     498
     499    EXAMPLES::
     500
     501        sage: from sage.combinat.finite_state_machine import FSMState
     502        sage: A = FSMState('state 1', word_out=0, is_initial=True)
     503        sage: A
     504        'state 1'
     505        sage: A.label()
     506        'state 1'
     507        sage: B = FSMState('state 2')
     508        sage: A == B
     509        False
     510
     511    """
     512    def __init__(self, label, word_out=None,
     513                 is_initial=False, is_final=False,
     514                 hook=None):
     515        """
     516        See :class:`FSMState` for more information.
     517
     518        EXAMPLES::
     519
     520            sage: from sage.combinat.finite_state_machine import FSMState
     521            sage: FSMState('final', is_final=True)
     522            'final'
     523        """
     524        if label is None or label == "":
     525            raise ValueError, "You have to specify a label for the state."
     526        self._label_ = label
     527
     528        if isinstance(word_out, list):
     529            self.word_out = word_out
     530        elif word_out is not None:
     531            self.word_out = [word_out]
     532        else:
     533            self.word_out = []
     534
     535        self.is_initial = is_initial
     536        self.is_final = is_final
     537        if hook is not None:
     538            if hasattr(hook, '__call__'):
     539                self.hook = hook
     540            else:
     541                raise TypeError, 'Wrong argument for hook.'
     542
     543
     544    def label(self):
     545        """
     546        Returns the label of the state.
     547
     548        INPUT:
     549
     550        Nothing.
     551
     552        OUTPUT:
     553
     554        The label of the state.
     555
     556        EXAMPLES::
     557
     558            sage: from sage.combinat.finite_state_machine import FSMState
     559            sage: A = FSMState('state')
     560            sage: A.label()
     561            'state'
     562        """
     563        return self._label_
     564
     565
     566    def __copy__(self):
     567        """
     568        Returns a (shallow) copy of the state.
     569
     570        INPUT:
     571
     572        Nothing.
     573       
     574        OUTPUT:
     575
     576        A new state.
     577
     578        EXAMPLES::
     579
     580            sage: from sage.combinat.finite_state_machine import FSMState
     581            sage: A = FSMState('A')
     582            sage: copy(A)
     583            'A'
     584        """
     585        new = FSMState(self.label(), self.word_out,
     586                       self.is_initial, self.is_final)
     587        if hasattr(self, 'hook'):
     588            new.hook = self.hook
     589        return new
     590
     591
     592    copy = __copy__
     593
     594
     595    def __deepcopy__(self, memo):
     596        """
     597        Returns a deep copy of the state.
     598
     599        INPUT:
     600
     601        - ``memo`` -- a dictionary storing already processed elements.
     602
     603        OUTPUT:
     604       
     605        A new state.
     606
     607        EXAMPLES::
     608
     609            sage: from sage.combinat.finite_state_machine import FSMState
     610            sage: A = FSMState('A')
     611            sage: deepcopy(A)
     612            'A'
     613        """
     614        try:
     615            label = self._deepcopy_relabel_
     616        except AttributeError:
     617            label = deepcopy(self.label(), memo)
     618        new = FSMState(label, deepcopy(self.word_out, memo),
     619                       self.is_initial, self.is_final)
     620        if hasattr(self, 'hook'):
     621            new.hook = deepcopy(self.hook, memo)
     622        return new
     623
     624
     625    def deepcopy(self, memo=None):
     626        """
     627        Returns a deep copy of the state.
     628
     629        INPUT:
     630
     631        - ``memo`` -- (default: ``None``) a dictionary storing already
     632          processed elements.
     633
     634        OUTPUT:
     635       
     636        A new state.
     637
     638        EXAMPLES::
     639
     640            sage: from sage.combinat.finite_state_machine import FSMState
     641            sage: A = FSMState('A')
     642            sage: deepcopy(A)
     643            'A'
     644        """
     645        return deepcopy(self, memo)
     646
     647
     648    def relabeled(self, label, memo=None):
     649        """
     650        Returns a deep copy of the state with a new label.
     651
     652        INPUT:
     653
     654        - ``label`` -- the label of new state.
     655
     656        - ``memo`` -- (default: ``None``) a dictionary storing already
     657          processed elements.
     658
     659        OUTPUT:
     660       
     661        A new state.
     662
     663        EXAMPLES::
     664
     665            sage: from sage.combinat.finite_state_machine import FSMState
     666            sage: A = FSMState('A')
     667            sage: A.relabeled('B')
     668            'B'
     669
     670        """
     671        self._deepcopy_relabel_ = label
     672        new = deepcopy(self, memo)
     673        del self._deepcopy_relabel_
     674        return new
     675
     676
     677    def __hash__(self):
     678        """
     679        Returns a hash value for the object.
     680
     681        INPUT:
     682
     683        Nothing.
     684
     685        OUTPUT:
     686
     687        The hash of this state.
     688
     689        TESTS::
     690
     691            sage: from sage.combinat.finite_state_machine import FSMState
     692            sage: A = FSMState('A')
     693            sage: hash(A) #random
     694            -269909568
     695        """
     696        return hash(self.label())
     697
     698
     699    def _repr_(self):
     700        """
     701        Returns the string "label".
     702
     703        INPUT:
     704
     705        Nothing.
     706
     707        OUTPUT:
     708       
     709        A string.
     710
     711        TESTS:
     712
     713            sage: from sage.combinat.finite_state_machine import FSMState
     714            sage: FSMState('A')._repr_()
     715            "'A'"
     716        """
     717        return repr(self.label())
     718
     719
     720    def __eq__(left, right):
     721        """
     722        Returns True if two states are the same, i.e., if they have
     723        the same labels.
     724
     725        Note that the hooks and whether the states are initial or
     726        final are not checked.
     727
     728        INPUT:
     729
     730        - ``left`` -- a state.
     731
     732        - ``right`` -- a state.
     733
     734        OUTPUT:
     735
     736        True or False.
     737
     738        EXAMPLES::
     739
     740            sage: from sage.combinat.finite_state_machine import FSMState
     741            sage: A = FSMState('A')
     742            sage: B = FSMState('A', is_initial=True)
     743            sage: A == B
     744            True
     745        """
     746        if not is_FSMState(right):
     747            return False
     748        return left.label() == right.label()
     749
     750
     751    def __ne__(left, right):
     752        """
     753        Tests for inequality, complement of __eq__.
     754
     755        INPUT:
     756
     757        - ``left`` -- a state.
     758
     759        - ``right`` -- a state.
     760
     761        OUTPUT:
     762
     763        True or False.
     764
     765        EXAMPLES::
     766
     767            sage: from sage.combinat.finite_state_machine import FSMState
     768            sage: A = FSMState('A', is_initial=True)
     769            sage: B = FSMState('A', is_final=True)
     770            sage: A != B
     771            False
     772        """
     773        return (not (left == right))
     774
     775
     776    def __nonzero__(self):
     777        """
     778        Returns True.
     779
     780        INPUT:
     781
     782        Nothing.
     783
     784        OUTPUT:
     785
     786        True or False.
     787
     788        TESTS::
     789
     790            sage: from sage.combinat.finite_state_machine import FSMState
     791            sage: FSMState('A').__nonzero__()
     792            True
     793        """
     794        return True  # A state cannot be zero (see __init__)
     795
     796
     797#*****************************************************************************
     798
     799
     800def is_FSMTransition(T):
     801    """
     802    Tests whether or not ``T`` inherits from :class:`FSMTransition`.
     803
     804    TESTS::
     805
     806        sage: from sage.combinat.finite_state_machine import is_FSMTransition, FSMTransition
     807        sage: is_FSMTransition(FSMTransition('A', 'B'))
     808        True
     809    """
     810    return isinstance(T, FSMTransition)
     811
     812
     813class FSMTransition(SageObject):
     814    """
     815    Class for a transition of a finite state machine.
     816
     817    INPUT:
     818
     819    - ``from_state`` -- state from which transition starts.
     820
     821    - ``to_state`` -- state in which transition ends.
     822
     823    - ``word_in`` -- the input word of the transitions (when the
     824      finite state machine is used as automaton)
     825
     826    - ``word_out`` -- the output word of the transitions (when the
     827      finite state machine is used as transducer)
     828
     829    OUTPUT:
     830
     831    A transition of a finite state machine.
     832
     833    EXAMPLES::
     834
     835        sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     836        sage: A = FSMState('A')
     837        sage: B = FSMState('B')
     838        sage: S = FSMTransition(A, B, 0, 1)
     839        sage: T = FSMTransition('A', 'B', 0, 1)
     840        sage: T == S
     841        True
     842        sage: U = FSMTransition('A', 'B', 0)
     843        sage: U == T
     844        False
     845
     846    """
     847    def __init__(self, from_state, to_state,
     848                 word_in=None, word_out=None,
     849                 hook=None):
     850        """
     851        See :class:`FSMTransition` for more information.
     852
     853        EXAMPLES::
     854
     855            sage: from sage.combinat.finite_state_machine import FSMTransition
     856            sage: FSMTransition('A', 'B', 0, 1)
     857            Transition from 'A' to 'B': 0|1
     858        """
     859        if is_FSMState(from_state):
     860            self.from_state = from_state
     861        else:
     862            self.from_state = FSMState(from_state)
     863        if is_FSMState(to_state):
     864            self.to_state = to_state
     865        else:
     866            self.to_state = FSMState(to_state)
     867
     868        if isinstance(word_in, list):
     869            self.word_in = word_in
     870        elif word_in is not None:
     871            self.word_in = [word_in]
     872        else:
     873            self.word_in = []
     874
     875        if isinstance(word_out, list):
     876            self.word_out = word_out
     877        elif word_out is not None:
     878            self.word_out = [word_out]
     879        else:
     880            self.word_out = []
     881
     882        if hook is not None:
     883            if hasattr(hook, '__call__'):
     884                self.hook = hook
     885            else:
     886                raise TypeError, 'Wrong argument for hook.'
     887
     888
     889    def __copy__(self):
     890        """
     891        Returns a (shallow) copy of the transition.
     892
     893        INPUT:
     894
     895        Nothing.
     896
     897        OUTPUT:
     898
     899        A new transition.
     900
     901        EXAMPLES::
     902
     903            sage: from sage.combinat.finite_state_machine import FSMTransition
     904            sage: t = FSMTransition('A', 'B', 0)
     905            sage: copy(t)
     906            Transition from 'A' to 'B': 0|-
     907        """
     908        new = FSMTransition(self.from_state, self.to_state,
     909                            self.word_in, self.word_out)
     910        if hasattr(self, 'hook'):
     911            new.hook = self.hook
     912        return new
     913
     914
     915    copy = __copy__
     916
     917    def __deepcopy__(self, memo):
     918        """
     919        Returns a deep copy of the transition.
     920
     921        INPUT:
     922
     923        - ``memo`` -- a dictionary storing already processed elements.
     924
     925        OUTPUT:
     926       
     927        A new transition.
     928
     929        EXAMPLES::
     930
     931            sage: from sage.combinat.finite_state_machine import FSMTransition
     932            sage: t = FSMTransition('A', 'B', 0)
     933            sage: deepcopy(t)
     934            Transition from 'A' to 'B': 0|-
     935        """
     936        new = FSMTransition(deepcopy(self.from_state, memo),
     937                            deepcopy(self.to_state, memo),
     938                            deepcopy(self.word_in, memo),
     939                            deepcopy(self.word_out, memo))
     940        if hasattr(self, 'hook'):
     941            new.hook = deepcopy(self.hook, memo)
     942        return new
     943
     944
     945    def deepcopy(self, memo=None):
     946        """
     947        Returns a deep copy of the transition.
     948
     949        INPUT:
     950
     951        - ``memo`` -- (default: ``None``) a dictionary storing already
     952          processed elements.
     953
     954        OUTPUT:
     955       
     956        A new transition.
     957
     958        EXAMPLES::
     959
     960            sage: from sage.combinat.finite_state_machine import FSMTransition
     961            sage: t = FSMTransition('A', 'B', 0)
     962            sage: deepcopy(t)
     963            Transition from 'A' to 'B': 0|-
     964        """
     965        return deepcopy(self, memo)
     966
     967
     968    def __hash__(self):
     969        """
     970        Since transitions are mutable, they should not be hashable, so
     971        we return a type error.
     972
     973        INPUT:
     974
     975        Nothing.
     976
     977        OUTPUT:
     978
     979        The hash of this transition.
     980
     981        EXAMPLES::
     982
     983            sage: from sage.combinat.finite_state_machine import FSMTransition
     984            sage: hash(FSMTransition('A', 'B'))
     985            Traceback (most recent call last):
     986            ...
     987            TypeError: Transitions are mutable, and thus not hashable.
     988
     989        """
     990        raise TypeError, "Transitions are mutable, and thus not hashable."
     991
     992
     993    def _repr_(self):
     994        """
     995        Represents a transitions as from state to state and input, output.
     996
     997        INPUT:
     998
     999        Nothing.
     1000
     1001        OUTPUT:
     1002
     1003        A string.
     1004
     1005        EXAMPLES::
     1006
     1007            sage: from sage.combinat.finite_state_machine import FSMTransition
     1008            sage: FSMTransition('A', 'B', 0, 0)._repr_()
     1009            "Transition from 'A' to 'B': 0|0"
     1010
     1011        """
     1012        return "Transition from %s to %s: %s" % (repr(self.from_state),
     1013                                                 repr(self.to_state),
     1014                                                 self._in_out_label_())
     1015
     1016
     1017    def _in_out_label_(self):
     1018        """
     1019        Returns the input and output of a transition as
     1020        "word_in|word_out".
     1021
     1022        INPUT:
     1023
     1024        Nothing.
     1025
     1026        OUTPUT:
     1027
     1028        A string of the input and output labels.
     1029
     1030        EXAMPLES::
     1031
     1032            sage: from sage.combinat.finite_state_machine import FSMTransition
     1033            sage: FSMTransition('A', 'B', 0, 1)._in_out_label_()
     1034            '0|1'
     1035        """
     1036        return "%s|%s" % (FSMWordSymbol(self.word_in),
     1037                          FSMWordSymbol(self.word_out))
     1038
     1039
     1040    def __eq__(left, right):
     1041        """
     1042        Returns True if the two transitions are the same, i.e., if the
     1043        both go from the same states to the same states and read and
     1044        write the same words.
     1045
     1046        Note that the hooks are not checked.
     1047
     1048        INPUT:
     1049
     1050        - ``left`` -- a transition.
     1051
     1052        - ``right`` -- a transition.
     1053
     1054        OUTPUT:
     1055
     1056        True or False.
     1057
     1058        EXAMPLES::
     1059
     1060            sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     1061            sage: A = FSMState('A', is_initial=True)
     1062            sage: t1 = FSMTransition('A', 'B', 0, 1)
     1063            sage: t2 = FSMTransition(A, 'B', 0, 1)
     1064            sage: t1 == t2
     1065            True
     1066        """
     1067        if not is_FSMTransition(right):
     1068            raise TypeError, 'Only instances of FSMTransition ' \
     1069                'can be compared.'
     1070        return left.from_state == right.from_state \
     1071            and left.to_state == right.to_state \
     1072            and left.word_in == right.word_in \
     1073            and left.word_out == right.word_out
     1074
     1075
     1076    def __ne__(left, right):
     1077        """
     1078
     1079        INPUT:
     1080
     1081        - ``left`` -- a transition.
     1082
     1083        - ``right`` -- a transition.
     1084
     1085        OUTPUT:
     1086
     1087        True or False.
     1088        Tests for inequality, complement of __eq__.
     1089
     1090        EXAMPLES::
     1091
     1092            sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     1093            sage: A = FSMState('A', is_initial=True)
     1094            sage: t1 = FSMTransition('A', 'B', 0, 1)
     1095            sage: t2 = FSMTransition(A, 'B', 0, 1)
     1096            sage: t1 != t2
     1097            False
     1098        """
     1099        return (not (left == right))
     1100
     1101
     1102    def __nonzero__(self):
     1103        """
     1104        Returns True.
     1105
     1106        INPUT:
     1107
     1108        Nothing.
     1109
     1110        OUTPUT:
     1111
     1112        True or False.
     1113
     1114        EXAMPLES::
     1115
     1116            sage: from sage.combinat.finite_state_machine import FSMTransition
     1117            sage: FSMTransition('A', 'B', 0).__nonzero__()
     1118            True
     1119        """
     1120        return True  # A transition cannot be zero (see __init__)
     1121
     1122
     1123#*****************************************************************************
     1124
     1125
     1126def is_FiniteStateMachine(FSM):
     1127    """
     1128    Tests whether or not ``FSM`` inherits from :class:`FiniteStateMachine`.
     1129
     1130    TESTS::
     1131
     1132        sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine
     1133        sage: is_FiniteStateMachine(FiniteStateMachine())
     1134        True
     1135        sage: is_FiniteStateMachine(Automaton())
     1136        True
     1137        sage: is_FiniteStateMachine(Transducer())
     1138        True
     1139    """
     1140    return isinstance(FSM, FiniteStateMachine)
     1141
     1142
     1143class FiniteStateMachine(SageObject):
     1144    """
     1145    Class for a finite state machine.
     1146
     1147    A finite state machine is a finite set of states connected by
     1148    transitions.
     1149
     1150    INPUT:
     1151
     1152    - ``data`` -- can be any of the following:
     1153
     1154      #. a dictionary of dictionaries (of transitions),
     1155
     1156      #. a dictionary of lists (of states or transitions),
     1157
     1158      #. a list (of transitions),
     1159
     1160      #. a function (transition function),
     1161
     1162      #. an other instance of a finite state machine.
     1163
     1164    - ``initial_states`` and ``final_states`` -- the initial and
     1165      final states of this machine
     1166
     1167    - ``input_alphabet`` and ``output_alphabet`` -- the input and
     1168      output alphabets of this machine
     1169
     1170    - ``determine_alphabets`` -- If True, then the function
     1171      ``determine_alphabets()`` is called after ``data`` was read and
     1172      processed, if ``False``, then not. If it is ``None``, then it is
     1173      decided during the construction of the finite state machine
     1174      whether ``determine_alphabets()`` should be called.
     1175
     1176    - ``store_states_dict`` -- If ``True``, then additionally the states
     1177      are stored in an interal dictionary for speed up.
     1178
     1179    OUTPUT:
     1180
     1181    A finite state machine.
     1182
     1183    The object creation of :class:`Automaton` and :class:`Transducer`
     1184    is the same as the one described here (i.e. just replace the word
     1185    ``FiniteStateMachine`` by ``Automaton`` or ``Transducer``).
     1186
     1187    Each transition of an automaton has an input label. Automata can,
     1188    for example, be determinised (see
     1189    :meth:`Automaton.determinisation`) and minimized (see
     1190    :meth:`Automaton.minimization`). Each transition of a transducer
     1191    has an input and an output label. Transducers can, for example, be
     1192    simplified (see :meth:`Transducer.simplification`).
     1193
     1194    EXAMPLES::
     1195
     1196        sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     1197
     1198    See documentation for more examples.
     1199
     1200    We illustrate the different input formats:
     1201
     1202    #.  The input-data can be a dictionary of dictionaries, where
     1203
     1204        - the keys of the outer dictionary are state-labels (from-states of
     1205          transitions),
     1206        - the keys of the inner dictionaries are state-labels (to-states of
     1207          transitions),
     1208        - the values of the inner dictionaries specify the transition
     1209          more precisely.
     1210
     1211        The easiest is to use a tuple consisting of an input and an
     1212        output word::
     1213
     1214            sage: FiniteStateMachine({'a':{'b':(0, 1), 'c':(1, 1)}})
     1215            Finite state machine with 3 states
     1216
     1217        Instead of the tuple anything iterable (e.g. a list) can be
     1218        used as well.
     1219
     1220        If you want to use the arguments of ``FSMTransition``
     1221        directly, you can use a dictionary::
     1222
     1223            sage: FiniteStateMachine({'a':{'b':{'word_in':0, 'word_out':1},
     1224            ....:                          'c':{'word_in':1, 'word_out':1}}})
     1225            Finite state machine with 3 states
     1226
     1227        In the case you already have instances of ``FSMTransition``, it is
     1228        possible to use them directly::
     1229
     1230            sage: FiniteStateMachine({'a':{'b':FSMTransition('a', 'b', 0, 1),
     1231            ....:                          'c':FSMTransition('a', 'c', 1, 1)}})
     1232            Finite state machine with 3 states
     1233
     1234    #.  The input-data can be a dictionary of lists, where the keys
     1235        are states or label of states.
     1236
     1237        The list-elements can be states::
     1238
     1239            sage: a = FSMState('a')
     1240            sage: b = FSMState('b')
     1241            sage: c = FSMState('c')
     1242            sage: FiniteStateMachine({a:[b, c]})
     1243            Finite state machine with 3 states
     1244
     1245        Or the list-elements can simply be labels of states::
     1246
     1247            sage: FiniteStateMachine({'a':['b', 'c']})
     1248            Finite state machine with 3 states
     1249
     1250        The list-elements can also be transitions::
     1251
     1252            sage: FiniteStateMachine({'a':[FSMTransition('a', 'b', 0, 1),
     1253            ....:                          FSMTransition('a', 'c', 1, 1)]})
     1254            Finite state machine with 3 states
     1255
     1256        Or they can be tuples of a label, an input word and an output
     1257        word specifying a transition::
     1258
     1259            sage: FiniteStateMachine({'a':[('b', 0, 1), ('c', 1, 1)]})
     1260            Finite state machine with 3 states
     1261
     1262    #.  The input-data can be a list, where its elements specify
     1263        transitions::
     1264
     1265            sage: FiniteStateMachine([FSMTransition('a', 'b', 0, 1),
     1266            ....:                     FSMTransition('a', 'c', 1, 1)])
     1267            Finite state machine with 3 states
     1268
     1269        It is possible to skip ``FSMTransition`` in the example above::
     1270
     1271            sage: FiniteStateMachine([('a', 'b', 0, 1), ('a', 'c', 1, 1)])
     1272            Finite state machine with 3 states
     1273
     1274        The parameters of the transition are given in tuples. Anyhow,
     1275        anything iterable (e.g. a list) is possible.
     1276   
     1277        You can also name the parameters of the transition. For this
     1278        purpose you take a dictionary::
     1279           
     1280            sage: FiniteStateMachine([{'from_state':'a', 'to_state':'b',
     1281            ....:                      'word_in':0, 'word_out':1},
     1282            ....:                     {'from_state':'a', 'to_state':'c',
     1283            ....:                      'word_in':1, 'word_out':1}])
     1284            Finite state machine with 3 states
     1285
     1286        Other arguments, which :class:`FSMTransition` accepts, can be
     1287        added, too.
     1288
     1289    #.  The input-data can also be function acting as transition
     1290        function:
     1291
     1292        This function has two input arguments:
     1293       
     1294        #. a label of a state (from which the transition starts),
     1295
     1296        #. a letter of the (input-)alphabet (as input-label of the transition).
     1297
     1298        It returns a tuple with the following entries:
     1299
     1300        #. a label of a state (to which state the transition goes),
     1301
     1302        #. a letter of or a word over the (output-)alphabet (as
     1303           output-label of the transition).
     1304
     1305        It may also output a list of such tuples if several
     1306        transitions from the from-state and the input letter exist
     1307        (this means that the finite state machine is
     1308        non-deterministic).
     1309
     1310        If the transition does not exist, the function should raise a
     1311        ``LookupError`` or return an empty list.
     1312
     1313        When constructing a finite state machine in this way, some
     1314        inital states and an input alphabet have to be specified.
     1315
     1316        ::
     1317
     1318            sage: def f(state_from, read):
     1319            ....:     if int(state_from) + read <= 2:
     1320            ....:         state_to = 2*int(state_from)+read
     1321            ....:         write = 0
     1322            ....:     else:
     1323            ....:         state_to = 2*int(state_from) + read - 5
     1324            ....:         write = 1
     1325            ....:     return (str(state_to), write)
     1326            sage: F = FiniteStateMachine(f, input_alphabet=[0, 1],
     1327            ....:                        initial_states=['0'],
     1328            ....:                        final_states=['0'])
     1329            sage: F([1, 0, 1])
     1330            (True, '0', [0, 0, 1])
     1331
     1332    #.  The input-data can be an other instance of a finite state machine::
     1333
     1334            sage: FiniteStateMachine(FiniteStateMachine([]))
     1335            Traceback (most recent call last):
     1336            ...
     1337            NotImplementedError
     1338
     1339
     1340        TESTS::
     1341
     1342            sage: a = FSMState('S_a', 'a')
     1343            sage: b = FSMState('S_b', 'b')
     1344            sage: c = FSMState('S_c', 'c')
     1345            sage: d = FSMState('S_d', 'd')
     1346            sage: FiniteStateMachine({a:[b, c], b:[b, c, d],
     1347            ....:                     c:[a, b], d:[a, c]})
     1348            Finite state machine with 4 states
     1349
     1350        We have several constructions which lead to the same finite
     1351        state machine::
     1352
     1353            sage: A = FSMState('A')
     1354            sage: B = FSMState('B')
     1355            sage: C = FSMState('C')
     1356            sage: FSM1 = FiniteStateMachine(
     1357            ....:  {A:{B:{'word_in':0, 'word_out':1},
     1358            ....:   C:{'word_in':1, 'word_out':1}}})
     1359            sage: FSM2 = FiniteStateMachine({A:{B:(0, 1), C:(1, 1)}})
     1360            sage: FSM3 = FiniteStateMachine(
     1361            ....:  {A:{B:FSMTransition(A, B, 0, 1),
     1362            ....:      C:FSMTransition(A, C, 1, 1)}})
     1363            sage: FSM4 = FiniteStateMachine({A:[(B, 0, 1), (C, 1, 1)]})
     1364            sage: FSM5 = FiniteStateMachine(
     1365            ....:  {A:[FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)]})
     1366            sage: FSM6 = FiniteStateMachine(
     1367            ....:  [{'from_state':A, 'to_state':B, 'word_in':0, 'word_out':1},
     1368            ....:   {'from_state':A, 'to_state':C, 'word_in':1, 'word_out':1}])
     1369            sage: FSM7 = FiniteStateMachine([(A, B, 0, 1), (A, C, 1, 1)])
     1370            sage: FSM8 = FiniteStateMachine(
     1371            ....:  [FSMTransition(A, B, 0, 1), FSMTransition(A, C, 1, 1)])
     1372
     1373            sage: FSM1 == FSM2 == FSM3 == FSM4 == FSM5 == FSM6 == FSM7 == FSM8
     1374            True
     1375
     1376        It is possible to skip ``FSMTransition`` in the example above.
     1377
     1378        Some more tests for different input-data::
     1379
     1380            sage: FiniteStateMachine({'a':{'a':[0, 0], 'b':[1, 1]},
     1381            ....:                     'b':{'b':[1, 0]}})
     1382            Finite state machine with 2 states         
     1383
     1384            sage: a = FSMState('S_a', 'a')
     1385            sage: b = FSMState('S_b', 'b')
     1386            sage: c = FSMState('S_c', 'c')
     1387            sage: d = FSMState('S_d', 'd')
     1388            sage: t1 = FSMTransition(a, b)
     1389            sage: t2 = FSMTransition(b, c)
     1390            sage: t3 = FSMTransition(b, d)
     1391            sage: t4 = FSMTransition(c, d)
     1392            sage: FiniteStateMachine([t1, t2, t3, t4])
     1393            Finite state machine with 4 states
     1394    """
     1395
     1396    #*************************************************************************
     1397    # init
     1398    #*************************************************************************
     1399
     1400
     1401    def __init__(self,
     1402                 data=None,
     1403                 initial_states=None, final_states=None,
     1404                 input_alphabet=None, output_alphabet=None,
     1405                 determine_alphabets=None,
     1406                 store_states_dict=True):
     1407        """
     1408        See :class:`FiniteStateMachine` for more information.
     1409
     1410        TEST::
     1411
     1412            sage: FiniteStateMachine()
     1413            Finite state machine with 0 states
     1414        """
     1415        self._states_ = []  # List of states in the finite state
     1416                            # machine.  Each state stores a list of
     1417                            # outgoing transitions.
     1418        if store_states_dict:
     1419            self._states_dict_ = {}
     1420
     1421        if initial_states is not None:
     1422            if not hasattr(initial_states, '__iter__'):
     1423                raise TypeError, 'Initial states must be iterable ' \
     1424                    '(e.g. a list of states).'
     1425            for s in initial_states:
     1426                state = self.add_state(s)
     1427                state.is_initial = True
     1428
     1429        if final_states is not None:
     1430            if not hasattr(final_states, '__iter__'):
     1431                raise TypeError, 'Final states must be iterable ' \
     1432                    '(e.g. a list of states).'
     1433            for s in final_states:
     1434                state = self.add_state(s)
     1435                state.is_final = True
     1436
     1437        self.input_alphabet = input_alphabet
     1438        self.output_alphabet = output_alphabet
     1439
     1440        if data is None:
     1441            pass
     1442        elif is_FiniteStateMachine(data):
     1443            raise NotImplementedError
     1444        elif hasattr(data, 'iteritems'):
     1445            # data is a dict (or something similar),
     1446            # format: key = from_state, value = iterator of transitions
     1447            for (sf, iter_transitions) in data.iteritems():
     1448                self.add_state(sf)
     1449                if hasattr(iter_transitions, 'iteritems'):
     1450                    for (st, transition) in iter_transitions.iteritems():
     1451                        self.add_state(st)
     1452                        if is_FSMTransition(transition):
     1453                            self.add_transition(transition)
     1454                        elif hasattr(transition, 'iteritems'):
     1455                            self.add_transition(sf, st, **transition)
     1456                        elif hasattr(transition, '__iter__'):
     1457                            self.add_transition(sf, st, *transition)
     1458                        else:
     1459                            self.add_transition(sf, st, transition)
     1460                elif hasattr(iter_transitions, '__iter__'):
     1461                    for transition in iter_transitions:
     1462                        if hasattr(transition, '__iter__'):
     1463                            L = [sf]
     1464                            L.extend(transition)
     1465                        elif is_FSMTransition(transition):
     1466                            L = transition
     1467                        else:
     1468                            L = [sf, transition]
     1469                        self.add_transition(L)
     1470                else:
     1471                    raise TypeError, 'Wrong input data for transition.'
     1472            if determine_alphabets is None and input_alphabet is None \
     1473                    and output_alphabet is None:
     1474                determine_alphabets = True
     1475        elif hasattr(data, '__iter__'):
     1476            # data is a something that is iterable,
     1477            # items are transitions
     1478            for transition in data:
     1479                if is_FSMTransition(transition):
     1480                    self.add_transition(transition)
     1481                elif hasattr(transition, 'iteritems'):
     1482                    self.add_transition(transition)
     1483                elif hasattr(transition, '__iter__'):
     1484                    self.add_transition(transition)
     1485                else:
     1486                    raise TypeError, 'Wrong input data for transition.'
     1487            if determine_alphabets is None and input_alphabet is None \
     1488                    and output_alphabet is None:
     1489                determine_alphabets = True
     1490        elif hasattr(data, '__call__'):
     1491            self.add_from_transition_function(data)
     1492        else:
     1493            raise TypeError, 'Cannot decide what to do with data.'
     1494
     1495        if determine_alphabets:
     1496            self.determine_alphabets()
     1497
     1498
     1499    #*************************************************************************
     1500    # copy and hash
     1501    #*************************************************************************
     1502
     1503
     1504    def __copy__(self):
     1505        """
     1506        Returns a (shallow) copy of the finite state machine.
     1507
     1508        INPUT:
     1509
     1510        Nothing.
     1511
     1512        OUTPUT:
     1513       
     1514        A new finite state machine.
     1515
     1516        TESTS::
     1517
     1518            sage: copy(FiniteStateMachine())
     1519            Traceback (most recent call last):
     1520            ...
     1521            NotImplementedError
     1522        """
     1523        raise NotImplementedError
     1524
     1525
     1526    copy = __copy__
     1527
     1528    def empty_copy(self, memo=None):
     1529        """
     1530        Returns an empty deep copy of the finite state machine, i.e.,
     1531        input_alphabet, output_alphabet are preserved, but states and
     1532        transitions are not.
     1533
     1534        INPUT:
     1535
     1536        - ``memo`` -- a dictionary storing already processed elements.
     1537
     1538        OUTPUT:
     1539       
     1540        A new finite state machine.
     1541
     1542        EXAMPLES::
     1543
     1544            sage: F = FiniteStateMachine([('A', 'A', 0, 2), ('A', 'A', 1, 3)],
     1545            ....:                        input_alphabet=[0,1],
     1546            ....:                        output_alphabet=[2,3])
     1547            sage: FE = F.empty_copy(); FE
     1548            Finite state machine with 0 states
     1549            sage: FE.input_alphabet
     1550            [0, 1]
     1551            sage: FE.output_alphabet
     1552            [2, 3]
     1553        """
     1554        new = self.__class__()
     1555        new.input_alphabet = deepcopy(self.input_alphabet, memo)
     1556        new.output_alphabet = deepcopy(self.output_alphabet, memo)
     1557        return new
     1558
     1559    def __deepcopy__(self, memo):
     1560        """
     1561        Returns a deep copy of the finite state machine.
     1562
     1563        INPUT:
     1564
     1565        - ``memo`` -- a dictionary storing already processed elements.
     1566
     1567        OUTPUT:
     1568       
     1569        A new finite state machine.
     1570
     1571        EXAMPLES::
     1572
     1573            sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)])
     1574            sage: deepcopy(F)
     1575            Finite state machine with 1 states
     1576        """
     1577        relabel = hasattr(self, '_deepcopy_relabel_')
     1578        new = self.empty_copy(memo=memo)
     1579        relabel_iter = itertools.count(0)
     1580        for state in self.iter_states():
     1581            if relabel:
     1582                state._deepcopy_relabel_ = relabel_iter.next()
     1583            s = deepcopy(state, memo)
     1584            if relabel:
     1585                del state._deepcopy_relabel_
     1586            new.add_state(s)
     1587        for transition in self.iter_transitions():
     1588            new.add_transition(deepcopy(transition, memo))
     1589        return new
     1590
     1591
     1592    def deepcopy(self, memo=None):
     1593        """
     1594        Returns a deep copy of the finite state machine.
     1595
     1596        INPUT:
     1597
     1598        - ``memo`` -- (default: ``None``) a dictionary storing already
     1599          processed elements.
     1600
     1601        OUTPUT:
     1602       
     1603        A new finite state machine.
     1604
     1605        EXAMPLES::
     1606
     1607            sage: F = FiniteStateMachine([('A', 'A', 0, 1), ('A', 'A', 1, 0)])
     1608            sage: deepcopy(F)
     1609            Finite state machine with 1 states
     1610        """
     1611        return deepcopy(self, memo)
     1612
     1613
     1614    def relabeled(self, memo=None):
     1615        """
     1616        Returns a deep copy of the finite state machine, but the
     1617        states are relabeled by integers starting with 0.
     1618
     1619        INPUT:
     1620
     1621        - ``memo`` -- (default: ``None``) a dictionary storing already
     1622          processed elements.
     1623
     1624        OUTPUT:
     1625       
     1626        A new finite state machine.
     1627
     1628        EXAMPLES::
     1629
     1630            sage: FSM1 = FiniteStateMachine([('A', 'B'), ('B', 'C'), ('C', 'A')])
     1631            sage: FSM1.states()
     1632            ['A', 'B', 'C']
     1633            sage: FSM2 = FSM1.relabeled()
     1634            sage: FSM2.states()
     1635            [0, 1, 2]
     1636        """
     1637        self._deepcopy_relabel_ = True
     1638        new = deepcopy(self, memo)
     1639        del self._deepcopy_relabel_
     1640        return new
     1641
     1642
     1643    def __hash__(self):
     1644        """
     1645        Since finite state machines are mutable, they should not be
     1646        hashable, so we return a type error.
     1647
     1648        INPUT:
     1649
     1650        Nothing.
     1651
     1652        OUTPUT:
     1653
     1654        The hash of this finite state machine.
     1655
     1656        EXAMPLES::
     1657
     1658            sage: hash(FiniteStateMachine())
     1659            Traceback (most recent call last):
     1660            ...
     1661            TypeError: Finite state machines are mutable, and thus not hashable.
     1662        """
     1663        if getattr(self, "_immutable", False):
     1664            return hash((tuple(self.states()), tuple(self.transitions())))
     1665        raise TypeError, "Finite state machines are mutable, " \
     1666            "and thus not hashable."
     1667
     1668
     1669    #*************************************************************************
     1670    # operators
     1671    #*************************************************************************
     1672
     1673
     1674    def __add__(self, other):
     1675        """
     1676        Returns the disjoint union of the finite state machines self and other.
     1677
     1678        INPUT:
     1679       
     1680        - ``other`` -- a finite state machine.
     1681
     1682        OUTPUT:
     1683       
     1684        A new finite state machine.
     1685
     1686        TESTS::
     1687
     1688            sage: FiniteStateMachine() + FiniteStateMachine([('A', 'B')])
     1689            Traceback (most recent call last):
     1690            ...
     1691            NotImplementedError
     1692        """
     1693        if is_FiniteStateMachine(other):
     1694            return self.disjoint_union(other)
     1695
     1696
     1697    def __iadd__(self, other):
     1698        """
     1699        TESTS::
     1700
     1701            sage: F = FiniteStateMachine()
     1702            sage: F += FiniteStateMachine()
     1703            Traceback (most recent call last):
     1704            ...
     1705            NotImplementedError
     1706        """
     1707        raise NotImplementedError
     1708
     1709
     1710    def __mul__(self, other):
     1711        """
     1712        TESTS::
     1713
     1714            sage: FiniteStateMachine() * FiniteStateMachine([('A', 'B')])
     1715            Traceback (most recent call last):
     1716            ...
     1717            NotImplementedError
     1718        """
     1719        if is_FiniteStateMachine(other):
     1720            return self.intersection(other)
     1721
     1722
     1723    def __imul__(self, other):
     1724        """
     1725        TESTS::
     1726
     1727            sage: F = FiniteStateMachine()
     1728            sage: F *= FiniteStateMachine()
     1729            Traceback (most recent call last):
     1730            ...
     1731            NotImplementedError
     1732        """
     1733        raise NotImplementedError
     1734
     1735
     1736    def __call__(self, *args, **kwargs):
     1737        """
     1738        Calls either method :meth:`.composition` or :meth:`.process`.
     1739
     1740        EXAMPLES::
     1741
     1742            sage: from sage.combinat.finite_state_machine import FSMState
     1743            sage: A = FSMState('A', is_initial=True, is_final=True)
     1744            sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]})
     1745            sage: binary_inverter([0, 1, 0, 0, 1, 1])
     1746            (True, 'A', [1, 0, 1, 1, 0, 0])
     1747
     1748        ::
     1749
     1750            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'B', 1, 1),
     1751            ....:                 ('B', 'B', 0, 0)],
     1752            ....:                initial_states=['A'], final_states=['B'])
     1753            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     1754            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     1755            ....:                initial_states=[1], final_states=[1])
     1756            sage: H = G(F)
     1757            sage: H.states()
     1758            [('A', 1), ('B', 1), ('B', 2)]
     1759        """
     1760        if len(args) == 0:
     1761            raise TypeError, "Called with too few arguments."
     1762        if is_FiniteStateMachine(args[0]):
     1763            return self.composition(*args, **kwargs)
     1764        if hasattr(args[0], '__iter__'):
     1765            return self.process(*args, **kwargs)
     1766        raise TypeError, "Do not know what to do with that arguments."
     1767
     1768
     1769    #*************************************************************************
     1770    # tests
     1771    #*************************************************************************
     1772
     1773
     1774    def __nonzero__(self):
     1775        """
     1776        Returns True if the finite state machine consists of at least
     1777        one state.
     1778
     1779        INPUT:
     1780
     1781        Nothing.
     1782
     1783        OUTPUT:
     1784
     1785        True or False.
     1786
     1787        TESTS::
     1788
     1789            sage: FiniteStateMachine().__nonzero__()
     1790            False
     1791        """
     1792        return len(self._states_) > 0
     1793
     1794
     1795    def __eq__(left, right):
     1796        """
     1797        Returns True if the two finite state machines are equal, i.e.,
     1798        if they have the same states and the same transitions.
     1799
     1800        INPUT:
     1801
     1802        - ``left`` -- a finite state machine.
     1803
     1804        - ``right`` -- a finite state machine.
     1805
     1806        OUTPUT:
     1807
     1808        True or False.
     1809
     1810        EXAMPLES::
     1811
     1812            sage: F = FiniteStateMachine([('A', 'B', 1)])
     1813            sage: F == FiniteStateMachine()
     1814            False
     1815        """
     1816        if not is_FiniteStateMachine(right):
     1817            raise TypeError, 'Only instances of FiniteStateMachine ' \
     1818                'can be compared.'
     1819        if len(left._states_) != len(right._states_):
     1820            return False
     1821        for state in left.iter_states():
     1822            if state not in right._states_:
     1823                return False
     1824            left_transitions = state.transitions
     1825            right_transitions = right.state(state).transitions
     1826            if len(left_transitions) != len(right_transitions):
     1827                return False
     1828            for t in left_transitions:
     1829                if t not in right_transitions:
     1830                    return False
     1831        return True
     1832
     1833
     1834    def __ne__(left, right):
     1835        """
     1836        Tests for inequality, complement of :meth:`.__eq__`.
     1837
     1838        INPUT:
     1839
     1840        - ``left`` -- a finite state machine.
     1841
     1842        - ``right`` -- a finite state machine.
     1843
     1844        OUTPUT:
     1845
     1846        True or False.
     1847
     1848        EXAMPLES::
     1849
     1850            sage: E = FiniteStateMachine([('A', 'B', 0)])
     1851            sage: F = Automaton([('A', 'B', 0)])
     1852            sage: G = Transducer([('A', 'B', 0, 1)])
     1853            sage: E == F
     1854            True
     1855            sage: E == G
     1856            False
     1857        """
     1858        return (not (left == right))
     1859
     1860
     1861    def __contains__(self, item):
     1862        """
     1863        Returns true, if the finite state machine contains the
     1864        state or transition item. Note that only the labels of the
     1865        states and the input and output words are tested.
     1866
     1867        INPUT:
     1868
     1869        - ``item`` -- a state or a transition.
     1870
     1871        OUTPUT:
     1872
     1873        True or False.
     1874
     1875        EXAMPLES::
     1876
     1877            sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     1878            sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)])
     1879            sage: FSMState('A', is_initial=True) in F
     1880            True
     1881            sage: 'A' in F
     1882            False
     1883            sage: FSMTransition('A', 'B', 0) in F
     1884            True
     1885        """
     1886        if is_FSMState(item):
     1887            return self.has_state(item)
     1888        if is_FSMTransition(item):
     1889            return self.has_transition(item)
     1890        return False
     1891
     1892
     1893    #*************************************************************************
     1894    # representations / LaTeX
     1895    #*************************************************************************
     1896
     1897
     1898    def _repr_(self):
     1899        """
     1900        Represents the finite state machine as "Finite state machine
     1901        with n states" where n is the number of states.
     1902
     1903        INPUT:
     1904
     1905        Nothing.
     1906
     1907        OUTPUT:
     1908
     1909        A string.
     1910
     1911        EXAMPLES::
     1912
     1913            sage: FiniteStateMachine()._repr_()
     1914            'Finite state machine with 0 states'
     1915        """
     1916        return "Finite state machine with %s states" % len(self._states_)
     1917
     1918
     1919    def _latex_(self):
     1920        r"""
     1921        Returns a LaTeX code for the graph of the finite state machine.
     1922
     1923        INPUT:
     1924
     1925        Nothing.
     1926
     1927        OUTPUT:
     1928
     1929        A string.
     1930
     1931        EXAMPLES::
     1932
     1933            sage: F = FiniteStateMachine([('A', 'B', 1, 2)])
     1934            sage: F._latex_()
     1935            '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\text{\\texttt{A}}}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\text{\\texttt{B}}}\n;\\path[->] (v0) edge node {$ $} (v1);\n\\end{tikzpicture}'
     1936        """
     1937        result = "\\begin{tikzpicture}[auto]\n"
     1938        j = 0;
     1939        for vertex in self.states():
     1940            if not hasattr(vertex, "coordinates"):
     1941                vertex.coordinates = (3*cos(2*pi*j/len(self.states())),
     1942                                      3*sin(2*pi*j/len(self.states())))
     1943            options = ""
     1944            if vertex in self.final_states():
     1945                options += ",accepting"
     1946            if hasattr(vertex, "format_label"):
     1947                label = vertex.format_label()
     1948            elif hasattr(self, "format_state_label"):
     1949                label = self.format_state_label(vertex)
     1950            else:
     1951                label = latex(vertex.label())
     1952            result += "\\node[state%s] (v%d) at (%f,%f) {%s}\n;" % (
     1953                options, j, vertex.coordinates[0],
     1954                vertex.coordinates[1], label)
     1955            vertex._number_ = j
     1956            j += 1
     1957        adjacent = {}
     1958        for source in self.states():
     1959            for target in self.states():
     1960                transitions = filter(lambda transition: \
     1961                                         transition.to_state == target,
     1962                                     source.transitions)
     1963                adjacent[source, target] = transitions
     1964
     1965        for ((source, target), transitions) in adjacent.iteritems():
     1966            if len(transitions) > 0:
     1967                labels = []
     1968                for transition in transitions:
     1969                    if hasattr(transition, "format_label"):
     1970                        labels.append(transition.format_label())
     1971                        continue
     1972                    elif hasattr(self, "format_transition_label"):
     1973                        format_transition_label = self.format_transition_label
     1974                    else:
     1975                        format_transition_label = latex
     1976                    labels.append(self._latex_transition_label_(
     1977                            transition, format_transition_label))
     1978                label = ", ".join(labels)
     1979                if source != target:
     1980                    if len(adjacent[target, source]) > 0:
     1981                        angle = atan2(
     1982                            target.coordinates[1] - source.coordinates[1],
     1983                            target.coordinates[0]-source.coordinates[0])*180/pi
     1984                        angle_source = ".%.2f" % ((angle+5).n(),)
     1985                        angle_target = ".%.2f" % ((angle+175).n(),)
     1986                    else:
     1987                        angle_source = ""
     1988                        angle_target = ""
     1989                    result += "\\path[->] (v%d%s) edge node {$%s$} (v%d%s);\n" % (
     1990                        source._number_, angle_source, label,
     1991                        target._number_, angle_target)
     1992                else:
     1993                    result += "\\path[->] (v%d) edge[loop above] node {$%s$} ();\n" % (
     1994                        source._number_, label)
     1995
     1996        result += "\\end{tikzpicture}"
     1997        return result
     1998
     1999
     2000    def _latex_transition_label_(self, transition, format_function=latex):
     2001        r"""
     2002        Returns the proper transition label.
     2003
     2004        INPUT:
     2005
     2006        - ``transition`` - a transition
     2007
     2008        - ``format_function'' - a function formatting the labels
     2009
     2010        OUTPUT:
     2011
     2012        A string.
     2013
     2014        TESTS::
     2015       
     2016            sage: F = FiniteStateMachine([('A', 'B', 0, 1)])
     2017            sage: t = F.transitions()[0]
     2018            sage: F._latex_transition_label_(t)
     2019            ' '
     2020        """
     2021        return ' '
     2022
     2023
     2024    #*************************************************************************
     2025    # other
     2026    #*************************************************************************
     2027
     2028
     2029    def _matrix_(self, R=None):
     2030        """
     2031        Returns the adjacency matrix of the finite state machine.
     2032        See :meth:`.adjacency_matrix` for more information.
     2033
     2034        EXAMPLES::
     2035
     2036            sage: B = FiniteStateMachine({0: {0: (0, 0), 'a': (1, 0)},
     2037            ....:                         'a': {2: (0, 0), 3: (1, 0)},
     2038            ....:                         2:{0:(1, 1), 4:(0, 0)},
     2039            ....:                         3:{'a':(0, 1), 2:(1, 1)},
     2040            ....:                         4:{4:(1, 1), 3:(0, 1)}},
     2041            ....:                        initial_states=[0])
     2042            sage: B._matrix_()
     2043            [1 1 0 0 0]
     2044            [0 0 1 1 0]
     2045            [x 0 0 0 1]
     2046            [0 x x 0 0]
     2047            [0 0 0 x x]
     2048        """
     2049        return self.adjacency_matrix()
     2050
     2051
     2052    def adjacency_matrix(self, input=None,
     2053                         entry=(lambda transition:var('x')**transition.word_out[0])):
     2054        """
     2055        Returns the adjacency matrix of the underlying graph.
     2056
     2057        INPUT:
     2058
     2059        - ``input`` -- Only transitions with input label ``input`` are
     2060          respected.
     2061
     2062        - ``entry`` -- The function ``entry`` takes a transition and
     2063          the return value is written in the matrix as the entry
     2064          ``(transition.from_state, transition.to_state)``.
     2065         
     2066        OUTPUT:
     2067
     2068        A matrix.
     2069
     2070        If any label of a state is not an integer, the finite state
     2071        machine is relabeled at the beginning.  If there are more than
     2072        one transitions between two states, then the different return
     2073        values of ``entry`` are added up.
     2074
     2075        The default value of entry takes the variable ``x`` to the
     2076        power of the output word of the transition.
     2077
     2078        EXAMPLES::
     2079
     2080            sage: B = FiniteStateMachine({0:{0:(0, 0), 'a':(1, 0)},
     2081            ....:                         'a':{2:(0, 0), 3:(1, 0)},
     2082            ....:                         2:{0:(1, 1), 4:(0, 0)},
     2083            ....:                         3:{'a':(0, 1), 2:(1, 1)},
     2084            ....:                         4:{4:(1, 1), 3:(0, 1)}},
     2085            ....:                        initial_states=[0])
     2086            sage: B.adjacency_matrix()
     2087            [1 1 0 0 0]
     2088            [0 0 1 1 0]
     2089            [x 0 0 0 1]
     2090            [0 x x 0 0]
     2091            [0 0 0 x x]
     2092            sage: B.adjacency_matrix(entry=(lambda transition: 1))
     2093            [1 1 0 0 0]
     2094            [0 0 1 1 0]
     2095            [1 0 0 0 1]
     2096            [0 1 1 0 0]
     2097            [0 0 0 1 1]
     2098            sage: B.adjacency_matrix(1, entry=(lambda transition:
     2099            ....:     exp(I*transition.word_out[0]*var('t'))))
     2100            [      0       1       0       0       0]
     2101            [      0       0       0       1       0]
     2102            [e^(I*t)       0       0       0       0]
     2103            [      0       0 e^(I*t)       0       0]
     2104            [      0       0       0       0 e^(I*t)]
     2105
     2106        """
     2107        relabeledFSM = self
     2108        l = len(relabeledFSM.states())
     2109        for state in self.states():
     2110            if state.label() not in ZZ or state.label() >= l or state.label() < 0:
     2111                relabeledFSM = self.relabeled()
     2112                break
     2113        dictionary = {}
     2114        for transition in relabeledFSM.iter_transitions():
     2115            if input is None or transition.word_in == [input]:
     2116                if (transition.from_state.label(), transition.to_state.label()) in dictionary:
     2117                    dictionary[(transition.from_state.label(), transition.to_state.label())] += entry(transition)
     2118                else:
     2119                    dictionary[(transition.from_state.label(), transition.to_state.label())] = entry(transition)
     2120        return matrix(len(relabeledFSM.states()), dictionary)
     2121
     2122
     2123    def determine_alphabets(self, reset=True):
     2124        """
     2125        Determines the input and output alphabet according to the
     2126        transitions in self.
     2127
     2128        INPUT:
     2129
     2130        - ``reset`` -- If reset is True, then the existing input
     2131          alphabet is erased, otherwise new letters are appended to
     2132          the existing alphabet.
     2133
     2134        OUTPUT:
     2135
     2136        Nothing.
     2137
     2138        After this operation the input alphabet and the output
     2139        alphabet of self are a list of letters.
     2140
     2141        EXAMPLES::
     2142
     2143            sage: T = Transducer([(1, 1, 1, 0), (1, 2, 2, 1),
     2144            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     2145            ....:                determine_alphabets=False)
     2146            sage: (T.input_alphabet, T.output_alphabet)
     2147            (None, None)
     2148            sage: T.determine_alphabets()
     2149            sage: (T.input_alphabet, T.output_alphabet)
     2150            ([0, 1, 2], [0, 1])
     2151       """
     2152        if reset:
     2153            ain = set()
     2154            aout = set()
     2155        else:
     2156            ain = set(self.input_alphabet)
     2157            aout = set(self.output_alphabet)
     2158
     2159        for t in self.iter_transitions():
     2160            for letter in t.word_in:
     2161                ain.add(letter)
     2162            for letter in t.word_out:
     2163                aout.add(letter)
     2164        self.input_alphabet = list(ain)
     2165        self.output_alphabet = list(aout)
     2166
     2167
     2168    #*************************************************************************
     2169    # get states and transitions
     2170    #*************************************************************************
     2171
     2172
     2173    def states(self):
     2174        """
     2175        Returns the states of the finite state machine.
     2176
     2177        INPUT:
     2178
     2179        Nothing.
     2180
     2181        OUTPUT:
     2182
     2183        The states of the finite state machine as list.
     2184
     2185        EXAMPLES::
     2186
     2187            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     2188            sage: FSM.states()
     2189            ['1', '2']
     2190
     2191        """
     2192        return copy(self._states_)
     2193
     2194
     2195    def iter_states(self):
     2196        """
     2197        Returns an iterator of the states.
     2198
     2199        INPUT:
     2200
     2201        Nothing.
     2202
     2203        OUTPUT:
     2204
     2205        An iterator of the states of the finite state machine.
     2206
     2207        EXAMPLES::
     2208
     2209            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     2210            sage: [s.label() for s in FSM.iter_states()]
     2211            ['1', '2']
     2212        """
     2213        return iter(self._states_)
     2214
     2215
     2216    def transitions(self, from_state=None):
     2217        """
     2218        Returns a list of all transitions.
     2219
     2220        INPUT:
     2221       
     2222        - ``from_state`` -- (default: ``None``) If ``from_state`` is
     2223          given, then a list of transitions starting there is given.
     2224
     2225        OUTPUT:
     2226
     2227        A list of all transitions.
     2228
     2229        EXAMPLES::
     2230
     2231            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     2232            sage: FSM.transitions()
     2233            [Transition from '1' to '2': 1|-,
     2234             Transition from '2' to '2': 0|-]
     2235        """
     2236        return list(self.iter_transitions(from_state))
     2237
     2238
     2239    def iter_transitions(self, from_state=None):
     2240        """
     2241        Returns an iterator of all transitions.
     2242
     2243        INPUT:
     2244       
     2245        - ``from_state`` -- (default: ``None``) If ``from_state`` is
     2246          given, then a list of transitions starting there is given.
     2247
     2248        OUTPUT:
     2249
     2250        An iterator of all transitions.
     2251
     2252        EXAMPLES::
     2253
     2254            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     2255            sage: [(t.from_state.label(), t.to_state.label())
     2256            ....:     for t in FSM.iter_transitions('1')]
     2257            [('1', '2')]
     2258            sage: [(t.from_state.label(), t.to_state.label())
     2259            ....:     for t in FSM.iter_transitions('2')]
     2260            [('2', '2')]
     2261            sage: [(t.from_state.label(), t.to_state.label())
     2262            ....:     for t in FSM.iter_transitions()]
     2263            [('1', '2'), ('2', '2')]
     2264        """
     2265        if from_state is None:
     2266            return self._iter_transitions_all_()
     2267        else:
     2268            return iter(self.state(from_state).transitions)
     2269
     2270
     2271    def _iter_transitions_all_(self):
     2272        """
     2273        Returns an iterator over all transitions.
     2274
     2275        INPUT:
     2276
     2277        Nothing.
     2278
     2279        OUTPUT:
     2280
     2281        An iterator over all transitions.
     2282
     2283        EXAMPLES::
     2284
     2285            sage: FSM = Automaton([('1', '2', 1), ('2', '2', 0)])
     2286            sage: [(t.from_state.label(), t.to_state.label())
     2287            ....:     for t in FSM._iter_transitions_all_()]
     2288            [('1', '2'), ('2', '2')]
     2289        """
     2290        for state in self.iter_states():
     2291            for t in state.transitions:
     2292                yield t
     2293
     2294
     2295    def initial_states(self):
     2296        """
     2297        Returns a list of all initial states.
     2298
     2299        INPUT:
     2300
     2301        Nothing.
     2302
     2303        OUTPUT:
     2304
     2305        A list of all initial states.
     2306
     2307        EXAMPLES::
     2308
     2309            sage: from sage.combinat.finite_state_machine import FSMState
     2310            sage: A = FSMState('A', is_initial=True)
     2311            sage: B = FSMState('B')
     2312            sage: F = FiniteStateMachine([(A, B, 1, 0)])
     2313            sage: F.initial_states()
     2314            ['A']
     2315        """
     2316        return list(self.iter_initial_states())
     2317
     2318
     2319    def iter_initial_states(self):
     2320        """
     2321        Returns an iterator of the initial states.
     2322
     2323        INPUT:
     2324
     2325        Nothing.
     2326
     2327        OUTPUT:
     2328
     2329        An iterator over all initial states.
     2330
     2331        EXAMPLES::
     2332
     2333            sage: from sage.combinat.finite_state_machine import FSMState
     2334            sage: A = FSMState('A', is_initial=True)
     2335            sage: B = FSMState('B')
     2336            sage: F = FiniteStateMachine([(A, B, 1, 0)])
     2337            sage: [s.label() for s in F.iter_initial_states()]
     2338            ['A']
     2339        """
     2340        return itertools.ifilter(lambda s:s.is_initial, self.iter_states())
     2341
     2342
     2343    def final_states(self):
     2344        """
     2345        Returns a list of all final states.
     2346
     2347        INPUT:
     2348
     2349        Nothing.
     2350
     2351        OUTPUT:
     2352
     2353        A list of all final states.
     2354
     2355        EXAMPLES::
     2356
     2357            sage: from sage.combinat.finite_state_machine import FSMState
     2358            sage: A = FSMState('A', is_final=True)
     2359            sage: B = FSMState('B', is_initial=True)
     2360            sage: C = FSMState('C', is_final=True)
     2361            sage: F = FiniteStateMachine([(A, B), (A, C)])
     2362            sage: F.final_states()
     2363            ['A', 'C']
     2364        """
     2365        return list(self.iter_final_states())
     2366
     2367
     2368    def iter_final_states(self):
     2369        """
     2370        Returns an iterator of the final states.
     2371
     2372        INPUT:
     2373
     2374        Nothing.
     2375
     2376        OUTPUT:
     2377
     2378        An iterator over all initial states.
     2379
     2380        EXAMPLES::
     2381
     2382            sage: from sage.combinat.finite_state_machine import FSMState
     2383            sage: A = FSMState('A', is_final=True)
     2384            sage: B = FSMState('B', is_initial=True)
     2385            sage: C = FSMState('C', is_final=True)
     2386            sage: F = FiniteStateMachine([(A, B), (A, C)])
     2387            sage: [s.label() for s in F.iter_final_states()]
     2388            ['A', 'C']
     2389        """
     2390        return itertools.ifilter(lambda s:s.is_final, self.iter_states())
     2391
     2392
     2393    def state(self, state):
     2394        """
     2395        Returns the state of the finite state machine.
     2396
     2397        INPUT:
     2398
     2399        - ``state`` -- If ``state`` is not an instance of
     2400          :class:`FSMState`, then it is assumed that it is the label
     2401          of a state.
     2402
     2403        OUTPUT:
     2404
     2405        Returns the state of the finite state machine corresponding to
     2406        ``state``.
     2407
     2408        If no state is found, then a ``LookupError`` is thrown.
     2409
     2410        EXAMPLES::
     2411
     2412            sage: from sage.combinat.finite_state_machine import FSMState
     2413            sage: A = FSMState('A')
     2414            sage: FSM = FiniteStateMachine([(A, 'B'), ('C', A)])
     2415            sage: FSM.state('A') == A
     2416            True
     2417            sage: FSM.state('xyz')
     2418            Traceback (most recent call last):
     2419            ...
     2420            LookupError: No state with label xyz found.
     2421        """
     2422        def what(s, switch):
     2423            if switch:
     2424                return s.label()
     2425            else:
     2426                return s
     2427        switch = is_FSMState(state)
     2428
     2429        try:
     2430            return self._states_dict_[what(state, switch)]
     2431        except AttributeError:
     2432            for s in self.iter_states():
     2433                if what(s, not switch) == state:
     2434                    return s
     2435        except KeyError:
     2436            pass
     2437        raise LookupError, \
     2438            "No state with label %s found." % (what(state, switch),)
     2439
     2440
     2441    def transition(self, transition):
     2442        """
     2443        Returns the transition of the finite state machine.
     2444
     2445        INPUT:
     2446
     2447        - ``transition`` -- If ``transition`` is not an instance of
     2448          :class:`FSMTransition`, then it is assumed that it is a
     2449          tuple ``(from_state, to_state, word_in, word_out)``.
     2450
     2451        OUTPUT:
     2452
     2453        Returns the transition of the finite state machine
     2454        corresponding to ``transition``.
     2455
     2456        If no transition is found, then a ``LookupError`` is thrown.
     2457
     2458        EXAMPLES::
     2459
     2460            sage: from sage.combinat.finite_state_machine import FSMTransition
     2461            sage: t = FSMTransition('A', 'B', 0)
     2462            sage: F = FiniteStateMachine([t])
     2463            sage: F.transition(('A', 'B', 0))
     2464            Transition from 'A' to 'B': 0|-
     2465            sage: id(t) == id(F.transition(('A', 'B', 0)))
     2466            True
     2467       """
     2468        if not is_FSMTransition(transition):
     2469            transition = FSMTransition(*transition)
     2470        for s in self.iter_transitions(transition.from_state):
     2471            if s == transition:
     2472                return s
     2473        raise LookupError, "No transition found."
     2474
     2475
     2476    #*************************************************************************
     2477    # properties (state and transitions)
     2478    #*************************************************************************
     2479
     2480
     2481    def has_state(self, state):
     2482        """
     2483        Returns whether ``state`` is one of the states of the finite
     2484        state machine.
     2485
     2486        INPUT:
     2487
     2488        - ``state`` can be a :class:`FSMState` or a label of a state.
     2489
     2490        OUTPUT:
     2491
     2492        True or False.
     2493
     2494        EXAMPLES::
     2495
     2496            sage: FiniteStateMachine().has_state('A')
     2497            False
     2498        """
     2499        try:
     2500            self.state(state)
     2501            return True
     2502        except LookupError:
     2503            return False
     2504
     2505
     2506    def has_transition(self, transition):
     2507        """
     2508        Returns whether ``transition`` is one of the transitions of
     2509        the finite state machine.
     2510
     2511        INPUT:
     2512
     2513        - ``transition`` has to be a :class:`FSMTransition`.
     2514
     2515        OUTPUT:
     2516
     2517        True or False.
     2518
     2519        EXAMPLES::
     2520
     2521            sage: from sage.combinat.finite_state_machine import FSMTransition
     2522            sage: t = FSMTransition('A', 'A', 0, 1)
     2523            sage: FiniteStateMachine().has_transition(t)
     2524            False
     2525            sage: FiniteStateMachine().has_transition(('A', 'A', 0, 1))
     2526            Traceback (most recent call last):
     2527            ...
     2528            TypeError: Transition is not an instance of FSMTransition.
     2529        """
     2530        if is_FSMTransition(transition):
     2531            return transition in self.iter_transitions()
     2532        raise TypeError, "Transition is not an instance of FSMTransition."
     2533
     2534
     2535    def has_initial_state(self, state):
     2536        """
     2537        Returns whether ``state`` is one of the initial states of the
     2538        finite state machine.
     2539
     2540        INPUT:
     2541
     2542        - ``state`` can be a :class:`FSMState` or a label.
     2543
     2544        OUTPUT:
     2545
     2546        True or False.
     2547
     2548        EXAMPLES::
     2549
     2550            sage: F = FiniteStateMachine([('A', 'A')], initial_states=['A'])
     2551            sage: F.has_initial_state('A')
     2552            True
     2553        """
     2554        try:
     2555            return self.state(state).is_initial
     2556        except LookupError:
     2557            return False
     2558
     2559
     2560    def has_initial_states(self):
     2561        """
     2562        Returns whether the finite state machine has an initial state.
     2563
     2564        INPUT:
     2565
     2566        Nothing.
     2567
     2568        OUTPUT:
     2569
     2570        True or False.
     2571
     2572        EXAMPLES::
     2573
     2574            sage: FiniteStateMachine().has_initial_states()
     2575            False
     2576        """
     2577        return len(self.initial_states()) > 0
     2578
     2579
     2580    def has_final_state(self, state):
     2581        """
     2582        Returns whether ``state`` is one of the final states of the
     2583        finite state machine.
     2584
     2585        INPUT:
     2586
     2587        - ``state`` can be a :class:`FSMState` or a label.
     2588
     2589        OUTPUT:
     2590
     2591        True or False.
     2592
     2593        EXAMPLES::
     2594
     2595            sage: FiniteStateMachine(final_states=['A']).has_final_state('A')
     2596            True
     2597        """
     2598        try:
     2599            return self.state(state).is_final
     2600        except LookupError:
     2601            return False
     2602
     2603
     2604    def has_final_states(self):
     2605        """
     2606        Returns whether the finite state machine has a final state.
     2607
     2608        INPUT:
     2609
     2610        Nothing.
     2611
     2612        OUTPUT:
     2613
     2614        True or False.
     2615
     2616        EXAMPLES::
     2617
     2618            sage: FiniteStateMachine().has_final_states()
     2619            False
     2620        """
     2621        return len(self.final_states()) > 0
     2622
     2623
     2624    #*************************************************************************
     2625    # properties
     2626    #*************************************************************************
     2627
     2628
     2629    def is_deterministic(self):
     2630        """
     2631        Returns whether the finite finite state machine is deterministic.
     2632
     2633        INPUT:
     2634
     2635        Nothing.
     2636
     2637        OUTPUT:
     2638
     2639        True or False.
     2640
     2641        A finite state machine is considered to be deterministic if
     2642        each transition has input label of length one and for each
     2643        pair `(q,a)` where `q` is a state and `a` is an element of the
     2644        input alphabet, there is at most one transition from `q` with
     2645        input label `a`.
     2646
     2647        TESTS::
     2648
     2649            sage: fsm = FiniteStateMachine()
     2650            sage: fsm.add_transition(('A', 'B', 0, []))
     2651            Transition from 'A' to 'B': 0|-
     2652            sage: fsm.is_deterministic()
     2653            True
     2654            sage: fsm.add_transition(('A', 'C', 0, []))
     2655            Transition from 'A' to 'C': 0|-
     2656            sage: fsm.is_deterministic()
     2657            False
     2658            sage: fsm.add_transition(('A', 'B', [0,1], []))
     2659            Transition from 'A' to 'B': 0,1|-
     2660            sage: fsm.is_deterministic()
     2661            False
     2662        """
     2663        for state in self.states():
     2664            for transition in state.transitions:
     2665                if len(transition.word_in) != 1:
     2666                    return False
     2667
     2668            transition_classes_by_word_in = full_group_by(
     2669                state.transitions,
     2670                key=lambda t:t.word_in)
     2671
     2672            for key,transition_class in transition_classes_by_word_in:
     2673                if len(transition_class) > 1:
     2674                    return False
     2675        return True
     2676
     2677
     2678    def is_connected(self):
     2679        """
     2680        TESTS::
     2681
     2682            sage: FiniteStateMachine().is_connected()
     2683            Traceback (most recent call last):
     2684            ...
     2685            NotImplementedError
     2686        """
     2687        raise NotImplementedError
     2688
     2689
     2690    #*************************************************************************
     2691    # let the finite state machine work
     2692    #*************************************************************************
     2693
     2694
     2695    def process(self, *args, **kwargs):
     2696        """
     2697        Returns whether the finite state machine accepts the input, the state
     2698        where the computation stops and which output is generated.
     2699
     2700        INPUT:
     2701
     2702        - ``input_tape`` -- The input tape can be a list with entries from
     2703          the input alphabet.
     2704       
     2705        - ``initial_state`` -- (default: ``None``) The state in which
     2706          to start. If this parameter is ``None`` and there is only
     2707          one initial state in the machine, then this state is taken.
     2708
     2709        OUTPUT:
     2710
     2711        A triple, where
     2712
     2713        - the first entry is true if the input string is accepted,
     2714
     2715        - the second gives the reached state after processing the
     2716          input tape, and
     2717
     2718        - the third gives a list of the output labels used during
     2719          processing (in the case the finite state machine runs as
     2720          transducer).
     2721
     2722        Note that in the case the finite state machine is not
     2723        deterministic, one possible path is gone. This means, that in
     2724        this case the output can be wrong. Use
     2725        :meth:`.determinisation` to get a deterministic finite state
     2726        machine and try again.
     2727
     2728        EXAMPLES::
     2729
     2730            sage: from sage.combinat.finite_state_machine import FSMState
     2731            sage: A = FSMState('A', is_initial = True, is_final = True)
     2732            sage: binary_inverter = Transducer({A:[(A, 0, 1), (A, 1, 0)]})
     2733            sage: binary_inverter.process([0, 1, 0, 0, 1, 1])
     2734            (True, 'A', [1, 0, 1, 1, 0, 0])
     2735
     2736        Alternatively, we can invoke this function by::
     2737
     2738            sage: binary_inverter([0, 1, 0, 0, 1, 1])
     2739            (True, 'A', [1, 0, 1, 1, 0, 0])
     2740
     2741        ::
     2742
     2743            sage: NAF_ = FSMState('_', is_initial = True, is_final = True)
     2744            sage: NAF1 = FSMState('1', is_final = True)
     2745            sage: NAF = Automaton(
     2746            ....:     {NAF_: [(NAF_, 0), (NAF1, 1)], NAF1: [(NAF_, 0)]})
     2747            sage: [NAF.process(w)[0] for w in [[0], [0, 1], [1, 1], [0, 1, 0, 1],
     2748            ....: [0, 1, 1, 1, 0], [1, 0, 0, 1, 1]]]
     2749            [True, True, False, True, False, False]
     2750
     2751        """
     2752        it = self.iter_process(*args, **kwargs)
     2753        for _ in it:
     2754            pass
     2755        return (it.accept_input, it.current_state, it.output_tape)
     2756
     2757
     2758    def iter_process(self, input_tape=None, initial_state=None):
     2759        """
     2760        See `process` for more informations.
     2761
     2762        EXAMPLES::
     2763
     2764            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     2765            ....:     initial_states=['A'], final_states=['A'])
     2766            sage: it = inverter.iter_process(input_tape=[0, 1, 1])
     2767            sage: for _ in it:
     2768            ....:     pass
     2769            sage: it.output_tape
     2770            [1, 0, 0]
     2771        """
     2772        return FSMProcessIterator(self, input_tape, initial_state)
     2773
     2774
     2775    #*************************************************************************
     2776    # change finite state machine (add/remove state/transitions)
     2777    #*************************************************************************
     2778
     2779
     2780    def add_state(self, state):
     2781        """
     2782        Adds a state to the finite state machine and returns the new
     2783        state. If the state already exists, that existing state is
     2784        returned.
     2785
     2786        INPUT:
     2787
     2788        - ``state`` is either an instance of FSMState or, otherwise, a
     2789          label of a state.
     2790
     2791        OUTPUT:
     2792
     2793        The new or existing state.
     2794
     2795        EXAMPLES::
     2796
     2797            sage: from sage.combinat.finite_state_machine import FSMState
     2798            sage: F = FiniteStateMachine()
     2799            sage: A = FSMState('A', is_initial=True)
     2800            sage: F.add_state(A)
     2801            'A'
     2802        """
     2803        try:
     2804            return self.state(state)
     2805        except LookupError:
     2806            pass
     2807        # at this point we know that we have a new state
     2808        if is_FSMState(state):
     2809            s = state
     2810        else:
     2811            s = FSMState(state)
     2812        s.transitions = list()
     2813        self._states_.append(s)
     2814        try:
     2815            self._states_dict_[s.label()] = s
     2816        except AttributeError:
     2817            pass
     2818        return s
     2819
     2820
     2821    def add_states(self, states):
     2822        """
     2823        Adds several states. See add_state for more information.
     2824
     2825        INPUT:
     2826
     2827        - ``states`` -- a list of states or iterator over states.
     2828
     2829        OUTPUT:
     2830
     2831        Nothing.
     2832
     2833        EXAMPLES::
     2834
     2835            sage: F = FiniteStateMachine()
     2836            sage: F.add_states(['A', 'B'])
     2837            sage: F.states()
     2838            ['A', 'B']
     2839        """
     2840        for state in states:
     2841            self.add_state(state)
     2842
     2843
     2844    def add_transition(self, *args, **kwargs):
     2845        """
     2846        Adds a transition to the finite state machine and returns the
     2847        new transition. If the transition already exists, that
     2848        existing transition is returned.
     2849
     2850        INPUT:
     2851
     2852        The following forms are all accepted:
     2853
     2854        ::
     2855
     2856            sage: from sage.combinat.finite_state_machine import FSMState, FSMTransition
     2857            sage: A = FSMState('A')
     2858            sage: B = FSMState('B')
     2859
     2860            sage: FSM = FiniteStateMachine()
     2861            sage: FSM.add_transition(FSMTransition(A, B, 0, 1))
     2862            Transition from 'A' to 'B': 0|1
     2863
     2864            sage: FSM = FiniteStateMachine()
     2865            sage: FSM.add_transition(A, B, 0, 1)
     2866            Transition from 'A' to 'B': 0|1
     2867
     2868            sage: FSM = FiniteStateMachine()
     2869            sage: FSM.add_transition(A, B, word_in=0, word_out=1)
     2870            Transition from 'A' to 'B': 0|1
     2871
     2872            sage: FSM = FiniteStateMachine()
     2873            sage: FSM.add_transition('A', 'B', {'word_in': 0, 'word_out': 1})
     2874            Transition from 'A' to 'B': {'word_in': 0, 'word_out': 1}|-
     2875
     2876            sage: FSM = FiniteStateMachine()
     2877            sage: FSM.add_transition(from_state=A, to_state=B,
     2878            ....:                    word_in=0, word_out=1)
     2879            Transition from 'A' to 'B': 0|1
     2880
     2881            sage: FSM = FiniteStateMachine()
     2882            sage: FSM.add_transition({'from_state': A, 'to_state': B,
     2883            ....:                    'word_in': 0, 'word_out': 1})
     2884            Transition from 'A' to 'B': 0|1
     2885
     2886            sage: FSM = FiniteStateMachine()
     2887            sage: FSM.add_transition((A, B, 0, 1))
     2888            Transition from 'A' to 'B': 0|1
     2889
     2890            sage: FSM = FiniteStateMachine()
     2891            sage: FSM.add_transition([A, B, 0, 1])
     2892            Transition from 'A' to 'B': 0|1
     2893
     2894        If the states ``A`` and ``B`` are not instances of FSMState, then
     2895        it is assumed that they are labels of states.
     2896
     2897        OUTPUT:
     2898
     2899        The new or existing transition.
     2900        """
     2901        if len(args) + len(kwargs) == 0:
     2902            return
     2903        if len(args) + len(kwargs) == 1:
     2904            if len(args) == 1:
     2905                d = args[0]
     2906                if is_FSMTransition(d):
     2907                    return self._add_fsm_transition_(d)
     2908            else:
     2909                d = kwargs.itervalues().next()
     2910            if hasattr(d, 'iteritems'):
     2911                args = []
     2912                kwargs = d
     2913            elif hasattr(d, '__iter__'):
     2914                args = d
     2915                kwargs = {}
     2916            else:
     2917                raise TypeError, "Cannot decide what to do with input."
     2918
     2919        data = dict(zip(
     2920                ('from_state', 'to_state', 'word_in', 'word_out', 'hook'),
     2921                args))
     2922        data.update(kwargs)
     2923
     2924        data['from_state'] = self.add_state(data['from_state'])
     2925        data['to_state'] = self.add_state(data['to_state'])
     2926
     2927        return self._add_fsm_transition_(FSMTransition(**data))
     2928
     2929
     2930    def _add_fsm_transition_(self, t):
     2931        """
     2932        Adds a transition.
     2933       
     2934        INPUT:
     2935
     2936        - ``t`` -- an instance of ``FSMTransition``.
     2937
     2938        OUTPUT:
     2939
     2940        The new transition.
     2941
     2942        TESTS::
     2943
     2944            sage: from sage.combinat.finite_state_machine import FSMTransition
     2945            sage: F = FiniteStateMachine()
     2946            sage: F._add_fsm_transition_(FSMTransition('A', 'B'))
     2947            Transition from 'A' to 'B': -|-
     2948        """
     2949        try:
     2950            return self.transition(t)
     2951        except LookupError:
     2952            pass
     2953        from_state = self.add_state(t.from_state)
     2954        self.add_state(t.to_state)
     2955        from_state.transitions.append(t)
     2956        return t
     2957
     2958
     2959    def add_from_transition_function(self, function, initial_states=None,
     2960                                     explore_existing_states=True):
     2961        """
     2962        Constructs a finite state machine from a transition function.
     2963
     2964        INPUT:
     2965       
     2966        - ``function`` may return a tuple (new_state, output_word) or a
     2967          list of such tuples.
     2968
     2969        - ``initial_states`` -- If no initial states are given, the
     2970          already existing initial states of self are taken.
     2971
     2972        - If ``explore_existing_states`` is True (default), then
     2973          already existing states in self (e.g. already given final
     2974          states) will also be processed if they are reachable from
     2975          the initial states.
     2976
     2977        OUTPUT:
     2978
     2979        Nothing.
     2980
     2981        EXAMPLES::
     2982
     2983            sage: F = FiniteStateMachine(initial_states=['A'],
     2984            ....:                        input_alphabet=[0, 1])
     2985            sage: def f(state, input):
     2986            ....:     return [('A', input), ('B', 1-input)]
     2987            sage: F.add_from_transition_function(f)
     2988            sage: F.transitions()
     2989            [Transition from 'A' to 'A': 0|0,
     2990            Transition from 'A' to 'B': 0|1,
     2991            Transition from 'A' to 'A': 1|1,
     2992            Transition from 'A' to 'B': 1|0,
     2993            Transition from 'B' to 'A': 0|0,
     2994            Transition from 'B' to 'B': 0|1,
     2995            Transition from 'B' to 'A': 1|1,
     2996            Transition from 'B' to 'B': 1|0]
     2997
     2998        Initial states can also be given as a parameter::
     2999
     3000            sage: F = FiniteStateMachine(input_alphabet=[0,1])
     3001            sage: def f(state, input):
     3002            ....:     return [('A', input), ('B', 1-input)]
     3003            sage: F.add_from_transition_function(f,initial_states=['A'])
     3004            sage: F.initial_states()
     3005            ['A']
     3006
     3007        Already existing states in the finite state machine (the final
     3008        states in the example below) are also explored::
     3009
     3010            sage: F = FiniteStateMachine(initial_states=[0],
     3011            ....:                        final_states=[1],
     3012            ....:                        input_alphabet=[0])
     3013            sage: def transition_function(state, letter):
     3014            ....:     return(1-state, [])
     3015            sage: F.add_from_transition_function(transition_function)
     3016            sage: F.transitions()
     3017            [Transition from 0 to 1: 0|-,
     3018             Transition from 1 to 0: 0|-]
     3019
     3020        If ``explore_existing_states=False``, however, this behavior
     3021        is turned off, i.e., already existing states are not
     3022        explored::
     3023           
     3024            sage: F = FiniteStateMachine(initial_states=[0],
     3025            ....:                        final_states=[1],
     3026            ....:                        input_alphabet=[0])
     3027            sage: def transition_function(state, letter):
     3028            ....:     return(1-state, [])
     3029            sage: F.add_from_transition_function(transition_function,
     3030            ....:                                explore_existing_states=False)
     3031            sage: F.transitions()
     3032            [Transition from 0 to 1: 0|-]
     3033
     3034        TEST::
     3035
     3036            sage: F = FiniteStateMachine(initial_states=['A'])
     3037            sage: def f(state, input):
     3038            ....:     return [('A', input), ('B', 1-input)]
     3039            sage: F.add_from_transition_function(f)
     3040            Traceback (most recent call last):
     3041            ...
     3042            ValueError: No input alphabet is given.
     3043            Try calling determine_alphabets().
     3044        """
     3045        if self.input_alphabet is None:
     3046            raise ValueError, ("No input alphabet is given. "
     3047                               "Try calling determine_alphabets().")
     3048
     3049        if initial_states is None:
     3050            not_done = self.initial_states()
     3051        elif hasattr(initial_states, '__iter__'):
     3052            not_done = []
     3053            for s in initial_states:
     3054                state = self.add_state(s)
     3055                state.is_initial = True
     3056                not_done.append(state)
     3057        else:
     3058            raise TypeError, 'Initial states must be iterable ' \
     3059                '(e.g. a list of states).'
     3060        if len(not_done) == 0:
     3061            raise ValueError, "No state is initial."
     3062        if explore_existing_states:
     3063            ignore_done = self.states()
     3064            for s in not_done:
     3065                try:
     3066                    ignore_done.remove(s)
     3067                except ValueError:
     3068                    pass
     3069        else:
     3070            ignore_done = []
     3071        while len(not_done) > 0:
     3072            s = not_done.pop(0)
     3073            for letter in self.input_alphabet:
     3074                try:
     3075                    return_value = function(s.label(), letter)
     3076                except LookupError:
     3077                    continue
     3078                if not hasattr(return_value, "pop"):
     3079                    return_value = [return_value]
     3080                try:
     3081                    for (st_label, word) in return_value:
     3082                        if not self.has_state(st_label):
     3083                            not_done.append(self.add_state(st_label))
     3084                        elif len(ignore_done) > 0:
     3085                            u = self.state(st_label)
     3086                            if u in ignore_done:
     3087                                not_done.append(u)
     3088                                ignore_done.remove(u)
     3089                        self.add_transition(s, st_label,
     3090                                            word_in=letter, word_out=word)
     3091                except TypeError:
     3092                    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))
     3093
     3094
     3095    def add_transitions_from_function(self, function, labels_as_input=True):
     3096        """
     3097        Adds a transition if ``function(state, state)`` says that there is one.
     3098
     3099        INPUT:
     3100
     3101        - ``function`` -- a transition function. Given two states
     3102          ``from_state`` and ``to_state`` (or their labels, if
     3103          ``label_as_input`` is true), this function shall return a
     3104          tuple ``(word_in, word_out)`` to add a transition from
     3105          ``from_state`` to ``to_state`` with input and output labels
     3106          ``word_in`` and ``word_out``, respectively. If no such
     3107          addition is to be added, the transition function shall
     3108          return ``None``.
     3109
     3110        - ``label_as_input`` -- (default: ``True``)
     3111
     3112        OUTPUT:
     3113
     3114        Nothing.
     3115
     3116        EXAMPLES::
     3117
     3118            sage: F = FiniteStateMachine()
     3119            sage: F.add_states(['A', 'B', 'C'])
     3120            sage: def f(state1, state2):
     3121            ....:     if state1 == 'C':
     3122            ....:         return None
     3123            ....:     return (0, 1)
     3124            sage: F.add_transitions_from_function(f)
     3125            sage: len(F.transitions())
     3126            6
     3127        """
     3128        for s_from in self.iter_states():
     3129            for s_to in self.iter_states():
     3130                if labels_as_input:
     3131                    t = function(s_from.label(), s_to.label())
     3132                else:
     3133                    t = function(s_from, s_to)
     3134                if hasattr(t, '__getitem__'):
     3135                    label_in = t[0]
     3136                    try:
     3137                        label_out = t[1]
     3138                    except LookupError:
     3139                        label_out = None
     3140                    self.add_transition(s_from, s_to, label_in, label_out)
     3141
     3142
     3143    def delete_transition(self, t):
     3144        """
     3145        Deletes a transition by removing it from the list of transitions of
     3146        the state, where the transition starts.
     3147
     3148        INPUT:
     3149
     3150        - ``t`` -- a transition.
     3151
     3152        OUTPUT:
     3153
     3154        Nothing.
     3155
     3156        EXAMPLES::
     3157
     3158            sage: F = FiniteStateMachine([('A', 'B', 0), ('B', 'A', 1)])
     3159            sage: F.delete_transition(('A', 'B', 0))
     3160            sage: F.transitions()
     3161            [Transition from 'B' to 'A': 1|-]
     3162        """
     3163        transition = self.transition(t)
     3164        transition.from_state.transitions.remove(transition)
     3165
     3166
     3167    def delete_state(self, s):
     3168        """
     3169        Deletes a state and all transitions coming or going to this state.
     3170       
     3171        INPUT:
     3172
     3173        - ``s`` -- s has to be a label of a state or :class:`FSMState`.
     3174
     3175        OUTPUT:
     3176
     3177        Nothing.
     3178
     3179        EXAMPLES::
     3180
     3181            sage: from sage.combinat.finite_state_machine import FSMTransition
     3182            sage: t1 = FSMTransition('A', 'B', 0)
     3183            sage: t2 = FSMTransition('B', 'B', 1)
     3184            sage: F = FiniteStateMachine([t1, t2])
     3185            sage: F.delete_state('A')
     3186            sage: F. transitions()
     3187            [Transition from 'B' to 'B': 1|-]
     3188        """
     3189        state = self.state(s)
     3190        for transition in self.transitions():
     3191            if transition.to_state == state:
     3192                self.delete_transition(transition)
     3193        self._states_.remove(state)
     3194
     3195
     3196    def remove_epsilon_transitions(self):
     3197        """
     3198        TESTS::
     3199
     3200            sage: FiniteStateMachine().remove_epsilon_transitions()
     3201            Traceback (most recent call last):
     3202            ...
     3203            NotImplementedError
     3204        """
     3205        raise NotImplementedError
     3206
     3207
     3208    def accessible_components(self):
     3209        """
     3210        Returns a new finite state machine with the accessible states
     3211        of self and all transitions between those states.
     3212
     3213        INPUT:
     3214
     3215        Nothing.
     3216
     3217        OUTPUT:
     3218
     3219        A finite state machine with the accessible states of self and
     3220        all transitions between those states.
     3221       
     3222        A state is accessible if there is a directed path from an
     3223        initial state to the state. If self has no initial states then
     3224        a copy of the finite state machine self is returned.
     3225
     3226        EXAMPLES::
     3227
     3228            sage: F = Automaton([(0, 0, 0), (0, 1, 1), (1, 1, 0), (1, 0, 1)],
     3229            ....:               initial_states=[0])
     3230            sage: F.accessible_components()
     3231            Automaton with 2 states
     3232
     3233        ::
     3234
     3235            sage: F = Automaton([(0, 0, 1), (0, 0, 1), (1, 1, 0), (1, 0, 1)],
     3236            ....:               initial_states=[0])
     3237            sage: F.accessible_components()
     3238            Automaton with 1 states
     3239        """
     3240        if len(self.initial_states()) == 0:
     3241            return deepcopy(self)
     3242
     3243        memo = {}
     3244        def accessible(sf, read):
     3245            trans = filter(lambda x: x.word_in[0] == read,
     3246                           self.transitions(sf))
     3247            return map(lambda x: (deepcopy(x.to_state, memo), x.word_out),
     3248                       trans)
     3249
     3250        new_initial_states=map(lambda x: deepcopy(x, memo),
     3251                               self.initial_states())
     3252        result = self.empty_copy()
     3253        result.add_from_transition_function(accessible,
     3254                                            initial_states=new_initial_states)
     3255        for final_state in self.iter_final_states():
     3256            try:
     3257                new_final_state=result.state(final_state.label)
     3258                new_final_state.is_final=True
     3259            except LookupError:
     3260                pass
     3261        return result
     3262
     3263
     3264    # *************************************************************************
     3265    # creating new finite state machines
     3266    # *************************************************************************
     3267
     3268
     3269    def disjoint_union(self, other):
     3270        """
     3271        TESTS::
     3272
     3273            sage: F = FiniteStateMachine([('A', 'A')])
     3274            sage: FiniteStateMachine().disjoint_union(F)
     3275            Traceback (most recent call last):
     3276            ...
     3277            NotImplementedError
     3278        """
     3279        raise NotImplementedError
     3280
     3281
     3282    def concatenation(self, other):
     3283        """
     3284        TESTS::
     3285
     3286            sage: F = FiniteStateMachine([('A', 'A')])
     3287            sage: FiniteStateMachine().concatenation(F)
     3288            Traceback (most recent call last):
     3289            ...
     3290            NotImplementedError
     3291        """
     3292        raise NotImplementedError
     3293
     3294
     3295    def Kleene_closure(self):
     3296        """
     3297        TESTS::
     3298
     3299            sage: FiniteStateMachine().Kleene_closure()
     3300            Traceback (most recent call last):
     3301            ...
     3302            NotImplementedError
     3303        """
     3304        raise NotImplementedError
     3305
     3306
     3307    def intersection(self, other):
     3308        """
     3309        TESTS::
     3310
     3311            sage: F = FiniteStateMachine([('A', 'A')])
     3312            sage: FiniteStateMachine().intersection(F)
     3313            Traceback (most recent call last):
     3314            ...
     3315            NotImplementedError
     3316        """
     3317        raise NotImplementedError
     3318
     3319
     3320    def product_FiniteStateMachine(self, other, function,
     3321                                   new_input_alphabet=None,
     3322                                   only_accessible_components=True):
     3323        """
     3324        Returns a new finite state machine whose states are
     3325        pairs of states of the original finite state machines.
     3326
     3327        INPUT:
     3328
     3329        - ``other`` -- a finite state machine.
     3330
     3331        - ``function`` has to accept two transitions from `A` to `B`
     3332          and `C` to `D` and returns a pair ``(word_in, word_out)``
     3333          which is the label of the transition `(A, C)` to `(B,
     3334          D)`. If there is no transition from `(A, C)` to `(B, D)`,
     3335          then ``function`` should raise a ``LookupError``.
     3336
     3337        - ``new_input_alphabet`` (optional)-- the new input alphabet
     3338          as a list.
     3339
     3340        - ``only_accessible_components`` -- If true (default), then
     3341          the result is piped through ``accessible_components``. If no
     3342          ``new_input_alphabet`` is given, it is determined by
     3343          ``determine_alphabets``.
     3344
     3345        OUTPUT:
     3346
     3347        A finite state machine whose states are pairs of states of the
     3348        original finite state machines.
     3349
     3350        The labels of the transitions are defined by ``function``.     
     3351
     3352        EXAMPLES::
     3353
     3354            sage: F = Automaton([('A', 'B', 1), ('A', 'A', 0), ('B', 'A', 2)],
     3355            ....:               initial_states=['A'], final_states=['B'],
     3356            ....:               determine_alphabets=True)
     3357            sage: G = Automaton([(1, 1, 1)], initial_states=[1], final_states=[1])
     3358            sage: def addition(transition1, transition2):
     3359            ....:     return (transition1.word_in[0] + transition2.word_in[0],
     3360            ....:             None)
     3361            sage: H = F.product_FiniteStateMachine(G, addition, [0, 1, 2, 3], only_accessible_components=False)
     3362            sage: H.transitions()
     3363            [Transition from ('A', 1) to ('B', 1): 2|-,
     3364             Transition from ('A', 1) to ('A', 1): 1|-,
     3365             Transition from ('B', 1) to ('A', 1): 3|-]
     3366            sage: H1 = F.product_FiniteStateMachine(G, addition, [0, 1, 2, 3], only_accessible_components=False)
     3367            sage: H1.states()[0].label()[0] is F.states()[0]
     3368            True
     3369            sage: H1.states()[0].label()[1] is G.states()[0]
     3370            True
     3371
     3372        ::
     3373
     3374            sage: F = Automaton([(0,1,1/4), (0,0,3/4), (1,1,3/4), (1,0,1/4)],
     3375            ....:                initial_states=[0] )
     3376            sage: G = Automaton([(0,0,1), (1,1,3/4), (1,0,1/4)],
     3377            ....:                initial_states=[0] )
     3378            sage: H = F.product_FiniteStateMachine(
     3379            ....:         G, lambda t1,t2: (t1.word_in[0]*t2.word_in[0], None))
     3380            sage: H.states()
     3381            [(0, 0), (1, 0)]
     3382
     3383        ::
     3384
     3385            sage: F = Automaton([(0,1,1/4), (0,0,3/4), (1,1,3/4), (1,0,1/4)],
     3386            ....:                initial_states=[0] )
     3387            sage: G = Automaton([(0,0,1), (1,1,3/4), (1,0,1/4)],
     3388            ....:                initial_states=[0] )
     3389            sage: H = F.product_FiniteStateMachine(G,
     3390            ....:                                  lambda t1,t2: (t1.word_in[0]*t2.word_in[0], None),
     3391            ....:                                  only_accessible_components=False)
     3392            sage: H.states()
     3393            [(0, 0), (1, 0), (0, 1), (1, 1)]
     3394        """
     3395        result = self.empty_copy()
     3396        if new_input_alphabet is not None:
     3397            result.input_alphabet = new_input_alphabet
     3398        else:
     3399            result.input_alphabet = None
     3400
     3401        for transition1 in self.transitions():
     3402            for transition2 in other.transitions():
     3403                try:
     3404                    word = function(transition1, transition2)
     3405                except LookupError:
     3406                    continue
     3407                result.add_transition((transition1.from_state,
     3408                                       transition2.from_state),
     3409                                      (transition1.to_state,
     3410                                       transition2.to_state),
     3411                                      word[0], word[1])
     3412        for state in result.states():
     3413            if all(map(lambda s: s.is_initial, state.label())):
     3414                state.is_initial = True
     3415            if all(map(lambda s: s.is_final, state.label())):
     3416                state.is_final = True
     3417
     3418        if only_accessible_components:
     3419            if new_input_alphabet is None:
     3420                result.determine_alphabets()
     3421            return result.accessible_components()
     3422        else:
     3423            return result
     3424
     3425
     3426    def cartesian_product(self, other, only_accessible_components=True):
     3427        """
     3428        Returns a new finite state machine, which is the cartesian
     3429        product of self and other.
     3430
     3431        INPUT:
     3432
     3433        - ``other`` -- a finite state machine.
     3434
     3435        - ``only_accessible_components``
     3436
     3437        OUTPUT:
     3438
     3439        A new finite state machine, which is the cartesian product of
     3440        self and other.
     3441
     3442        The set of states of the new automaton is the cartesian
     3443        product of the set of states of both given automata. There is
     3444        a transition `((A, B), (C, D), a)` in the new automaton if
     3445        there are transitions `(A, C, a)` and `(B, C, a)` in the old
     3446        automata.
     3447
     3448        EXAMPLES::
     3449
     3450            sage: aut1 = Automaton([('1', '2', 1), ('2', '2', 1), ('2', '2', 0)],
     3451            ....:                initial_states=['1'], final_states=['2'],
     3452            ....:                determine_alphabets=True)
     3453            sage: aut2 = Automaton([('A', 'A', 1), ('A', 'B', 1),
     3454            ....:                 ('B', 'B', 0), ('B', 'A', 0)],
     3455            ....:                initial_states=['A'], final_states=['B'],
     3456            ....:                determine_alphabets=True)
     3457            sage: res = aut1.cartesian_product(aut2)
     3458            sage: res.transitions()
     3459            [Transition from ('1', 'A') to ('2', 'A'): 1|-,
     3460             Transition from ('1', 'A') to ('2', 'B'): 1|-,
     3461             Transition from ('2', 'A') to ('2', 'A'): 1|-,
     3462             Transition from ('2', 'A') to ('2', 'B'): 1|-,
     3463             Transition from ('2', 'B') to ('2', 'B'): 0|-,
     3464             Transition from ('2', 'B') to ('2', 'A'): 0|-]
     3465        """
     3466        def function(transition1, transition2):
     3467            if transition1.word_in == transition2.word_in \
     3468                    and transition1.word_out == transition2.word_out:
     3469                return (transition1.word_in, transition1.word_out)
     3470            else:
     3471                raise LookupError
     3472
     3473        return self.product_FiniteStateMachine(
     3474            other, function,
     3475            only_accessible_components = only_accessible_components)
     3476
     3477
     3478    def composition(self, other, algorithm=None,
     3479                    only_accessible_components=True):
     3480        """
     3481        Returns a new transducer which is the composition of self and
     3482        other.
     3483
     3484        INPUT:
     3485
     3486        - ``other`` -- a transducer
     3487
     3488        - ``algorithm`` -- can be one of the following
     3489
     3490          - ``direct`` -- The composition is calculated directly.
     3491
     3492            There can be arbitrarily many initial and final states,
     3493            but the input and output labels must have length 1.
     3494
     3495
     3496            WARNING: The output of other is fed into self.
     3497
     3498          - ``explorative`` -- An explorative algorithm is used.
     3499
     3500            At least the following restrictions apply, but are not
     3501            checked:
     3502            - both self and other have exactly one initial state
     3503            - all input labels of transitions have length exactly 1
     3504
     3505            The input alphabet of self has to be specified.
     3506
     3507            This is a very limited implementation of composition.
     3508            WARNING: The output of ``other`` is fed into ``self``.
     3509
     3510          If algorithm is ``None``, then the algorithm is chosen
     3511          automatically (at the moment always ``direct``).
     3512
     3513        OUTPUT:
     3514
     3515        A new transducer.
     3516
     3517        The labels of the new finite state machine are pairs
     3518        of states of the original finite state machines.
     3519
     3520        EXAMPLES::
     3521
     3522            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     3523            ....:                initial_states=['A', 'B'], final_states=['B'],
     3524            ....:                determine_alphabets=True)
     3525            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     3526            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     3527            ....:                initial_states=[1], final_states=[2],
     3528            ....:                determine_alphabets=True)
     3529            sage: Hd = F.composition(G, algorithm='direct')
     3530            sage: Hd.initial_states()
     3531            [(1, 'B'), (1, 'A')]
     3532            sage: Hd.transitions()
     3533            [Transition from (1, 'B') to (1, 'A'): 1|1,
     3534             Transition from (1, 'A') to (2, 'B'): 0|0,
     3535             Transition from (2, 'B') to (2, 'A'): 0|1,
     3536             Transition from (2, 'A') to (2, 'B'): 1|0]
     3537
     3538        ::
     3539
     3540            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     3541            ....:                 ('B', 'B', 0, 0)],
     3542            ....:                initial_states=['A'], final_states=['B'])
     3543            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     3544            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     3545            ....:                initial_states=[1], final_states=[1])
     3546            sage: He = G.composition(F, algorithm='explorative')
     3547            sage: He.transitions()
     3548            [Transition from ('A', 1) to ('B', 2): 1|0,1,
     3549             Transition from ('B', 2) to ('B', 2): 0|1,
     3550             Transition from ('B', 2) to ('B', 1): 1|1,
     3551             Transition from ('B', 1) to ('B', 1): 0|0,
     3552             Transition from ('B', 1) to ('B', 2): 1|0]
     3553
     3554        Be aware that after composition, different transitions may
     3555        share the same output label (same python object)::
     3556
     3557            sage: F = Transducer([ ('A','B',0,0), ('B','A',0,0)],
     3558            ....:                initial_states=['A'],
     3559            ....:                final_states=['A'])
     3560            sage: F.transitions()[0].word_out is F.transitions()[1].word_out
     3561            False
     3562            sage: G = Transducer([('C','C',0,1)],)
     3563            ....:                initial_states=['C'],
     3564            ....:                final_states=['C'])
     3565            sage: H = G.composition(F)
     3566            sage: H.transitions()[0].word_out is H.transitions()[1].word_out
     3567            True
     3568
     3569        TESTS:
     3570
     3571        Due to the limitations of the two algorithms the following
     3572        (examples from above, but different algorithm used) does not
     3573        give a full answer or does not work
     3574
     3575        In the following, ``algorithm='explorative'`` is inadequate,
     3576        as ``F`` has more than one initial state::
     3577
     3578            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     3579            ....:                initial_states=['A', 'B'], final_states=['B'],
     3580            ....:                determine_alphabets=True)
     3581            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     3582            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     3583            ....:                initial_states=[1], final_states=[2],
     3584            ....:                determine_alphabets=True)
     3585            sage: He = F.composition(G, algorithm='explorative')
     3586            sage: He.initial_states()
     3587            [(1, 'A')]
     3588            sage: He.transitions()
     3589            [Transition from (1, 'A') to (2, 'B'): 0|0,
     3590             Transition from (2, 'B') to (2, 'A'): 0|1,
     3591             Transition from (2, 'A') to (2, 'B'): 1|0]
     3592
     3593        In the following example, ``algorithm='direct'`` is inappropriate
     3594        as there are edges with output labels of length greater than 1::
     3595
     3596            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     3597            ....:                 ('B', 'B', 0, 0)],
     3598            ....:                initial_states=['A'], final_states=['B'])
     3599            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     3600            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     3601            ....:                initial_states=[1], final_states=[1])
     3602            sage: Hd = G.composition(F, algorithm='direct')
     3603        """
     3604        if algorithm == None:
     3605            algorithm = 'direct'
     3606        if algorithm == 'direct':
     3607            return self._composition_direct_(other, only_accessible_components)
     3608        elif algorithm == 'explorative':
     3609            return self._composition_explorative_(other)
     3610        else:
     3611            raise ValueError, "Unknown algorithm %s." % (algorithm,)
     3612
     3613
     3614    def _composition_direct_(self, other, only_accessible_components=True):
     3615        """
     3616        See :meth:`.composition` for details.
     3617
     3618        TESTS::
     3619
     3620            sage: F = Transducer([('A', 'B', 1, 0), ('B', 'A', 0, 1)],
     3621            ....:                initial_states=['A', 'B'], final_states=['B'],
     3622            ....:                determine_alphabets=True)
     3623            sage: G = Transducer([(1, 1, 1, 0), (1, 2, 0, 1),
     3624            ....:                 (2, 2, 1, 1), (2, 2, 0, 0)],
     3625            ....:                initial_states=[1], final_states=[2],
     3626            ....:                determine_alphabets=True)
     3627            sage: Hd = F._composition_direct_(G)
     3628            sage: Hd.initial_states()
     3629            [(1, 'B'), (1, 'A')]
     3630            sage: Hd.transitions()
     3631            [Transition from (1, 'B') to (1, 'A'): 1|1,
     3632             Transition from (1, 'A') to (2, 'B'): 0|0,
     3633             Transition from (2, 'B') to (2, 'A'): 0|1,
     3634             Transition from (2, 'A') to (2, 'B'): 1|0]
     3635
     3636        """
     3637        def function(transition1, transition2):
     3638            if transition1.word_out == transition2.word_in:
     3639                return (transition1.word_in, transition2.word_out)
     3640            else:
     3641                raise LookupError
     3642
     3643        return other.product_FiniteStateMachine(
     3644            self, function,
     3645            only_accessible_components=only_accessible_components)
     3646
     3647
     3648    def _composition_explorative_(self, other):
     3649        """
     3650        See :meth:`.composition` for details.
     3651
     3652        TESTS::
     3653
     3654            sage: F = Transducer([('A', 'B', 1, [1, 0]), ('B', 'B', 1, 1),
     3655            ....:                 ('B', 'B', 0, 0)],
     3656            ....:                initial_states=['A'], final_states=['B'])
     3657            sage: G = Transducer([(1, 1, 0, 0), (1, 2, 1, 0),
     3658            ....:                 (2, 2, 0, 1), (2, 1, 1, 1)],
     3659            ....:                initial_states=[1], final_states=[1])
     3660            sage: He = G._composition_explorative_(F)
     3661            sage: He.transitions()
     3662            [Transition from ('A', 1) to ('B', 2): 1|0,1,
     3663             Transition from ('B', 2) to ('B', 2): 0|1,
     3664             Transition from ('B', 2) to ('B', 1): 1|1,
     3665             Transition from ('B', 1) to ('B', 1): 0|0,
     3666             Transition from ('B', 1) to ('B', 2): 1|0]
     3667
     3668        TODO:
     3669
     3670        The explorative algorithm should be re-implemented using the
     3671        process iterators of both finite state machines.
     3672        """
     3673        def composition_transition(state, input):
     3674            (state1, state2) = state
     3675            transition1 = None
     3676            for transition in other.iter_transitions(state1):
     3677                if transition.word_in == [input]:
     3678                    transition1 = transition
     3679                    break
     3680            if transition1 is None:
     3681                raise LookupError
     3682            new_state1 = transition1.to_state
     3683            new_state2 = state2
     3684            output = []
     3685            for o in transition1.word_out:
     3686                transition2 = None
     3687                for transition in self.iter_transitions(new_state2):
     3688                    if transition.word_in == [o]:
     3689                        transition2 = transition
     3690                        break
     3691                if transition2 is None:
     3692                    raise LookupError
     3693                new_state2 = transition2.to_state
     3694                output += transition2.word_out
     3695            return ((new_state1, new_state2), output)
     3696
     3697        F = other.empty_copy()
     3698        new_initial_states = [(other.initial_states()[0], self.initial_states()[0])]
     3699        F.add_from_transition_function(composition_transition,
     3700                                       initial_states=new_initial_states)
     3701
     3702        for state in F.states():
     3703            if all(map(lambda s: s.is_final, state.label())):
     3704                state.is_final = True
     3705
     3706        return F
     3707
     3708
     3709    def input_projection(self):
     3710        """
     3711        Returns an automaton where the output of each transition of
     3712        self is deleted.
     3713       
     3714        INPUT:
     3715
     3716        Nothing
     3717
     3718        OUTPUT:
     3719
     3720        An automaton.
     3721
     3722        EXAMPLES::
     3723
     3724            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     3725            ....:                         ('B', 'B', 1, 0)])
     3726            sage: G = F.input_projection()
     3727            sage: G.transitions()
     3728            [Transition from 'A' to 'B': 0|-,
     3729             Transition from 'A' to 'A': 1|-,
     3730             Transition from 'B' to 'B': 1|-]
     3731        """
     3732        return self.projection(what='input')
     3733
     3734
     3735    def output_projection(self):
     3736        """
     3737        Returns a automaton where the input of each transition of self
     3738        is deleted and the new input is the original output.
     3739       
     3740        INPUT:
     3741
     3742        Nothing
     3743
     3744        OUTPUT:
     3745
     3746        An automaton.
     3747
     3748        EXAMPLES::
     3749
     3750            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     3751            ....:                         ('B', 'B', 1, 0)])
     3752            sage: G = F.output_projection()
     3753            sage: G.transitions()
     3754            [Transition from 'A' to 'B': 1|-,
     3755             Transition from 'A' to 'A': 1|-,
     3756             Transition from 'B' to 'B': 0|-]
     3757        """
     3758        return self.projection(what='output')
     3759
     3760
     3761    def projection(self, what='input'):
     3762        """
     3763        Returns an Automaton which transition labels are the projection
     3764        of the transition labels of the input.
     3765       
     3766        INPUT:
     3767
     3768        - ``what`` -- (default: ``input``) either ``input`` or ``output``.
     3769
     3770        OUTPUT:
     3771
     3772        An automaton.
     3773
     3774        EXAMPLES::
     3775
     3776            sage: F = FiniteStateMachine([('A', 'B', 0, 1), ('A', 'A', 1, 1),
     3777            ....:                         ('B', 'B', 1, 0)])
     3778            sage: G = F.projection(what='output')
     3779            sage: G.transitions()
     3780            [Transition from 'A' to 'B': 1|-,
     3781             Transition from 'A' to 'A': 1|-,
     3782             Transition from 'B' to 'B': 0|-]
     3783        """
     3784        new = Automaton()
     3785
     3786        if what == 'input':
     3787            new.input_alphabet = copy(self.input_alphabet)
     3788        elif what == 'output':
     3789            new.input_alphabet = copy(self.output_alphabet)
     3790        else:
     3791            raise NotImplementedError
     3792
     3793        state_mapping = {}
     3794        for state in self.iter_states():
     3795            state_mapping[state] = new.add_state(deepcopy(state))
     3796        for transition in self.iter_transitions():
     3797            if what == 'input':
     3798                new_word_in = transition.word_in
     3799            elif what == 'output':
     3800                new_word_in = transition.word_out
     3801            else:
     3802                raise NotImplementedError
     3803            new.add_transition((state_mapping[transition.from_state],
     3804                                state_mapping[transition.to_state],
     3805                                new_word_in, None))
     3806        return new
     3807
     3808
     3809    def transposition(self):
     3810        """
     3811        Returns a new finite state machine, where all transitions of the
     3812        input finite state machine are reversed.
     3813
     3814        INPUT:
     3815
     3816        Nothing.
     3817
     3818        OUTPUT:
     3819
     3820        A new finite state machine.
     3821
     3822        EXAMPLES::
     3823
     3824            sage: aut = Automaton([('A', 'A', 0), ('A', 'A', 1), ('A', 'B', 0)],
     3825            ....:                 initial_states=['A'], final_states=['B'])
     3826            sage: aut.transposition().transitions('B')
     3827            [Transition from 'B' to 'A': 0|-]
     3828
     3829        ::
     3830
     3831            sage: aut = Automaton([('1', '1', 1), ('1', '2', 0), ('2', '2', 0)],
     3832            ....:                 initial_states=['1'], final_states=['1', '2'])
     3833            sage: aut.transposition().initial_states()
     3834            ['1', '2']
     3835        """
     3836        transposition = self.empty_copy()
     3837
     3838        for state in self.states():
     3839            transposition.add_state(deepcopy(state))
     3840
     3841        for transition in self.transitions():
     3842            transposition.add_transition(
     3843                transition.to_state.label(), transition.from_state.label(),
     3844                transition.word_in, transition.word_out)
     3845
     3846        for initial in self.initial_states():
     3847            state = transposition.state(initial.label())
     3848            if not initial.is_final:
     3849                state.is_final = True
     3850                state.is_initial = False
     3851
     3852        for final in self.final_states():
     3853            state = transposition.state(final.label())
     3854            if not final.is_initial:
     3855                state.is_final = False
     3856                state.is_initial = True
     3857
     3858        return transposition
     3859
     3860
     3861    def split_transitions(self):
     3862        """
     3863        Returns a new transducer, where all transitions in self with input
     3864        labels consisting of more than one letter
     3865        are replaced by a path of the corresponding length.
     3866
     3867        INPUT:
     3868
     3869        Nothing.
     3870
     3871        OUTPUT:
     3872
     3873        A new transducer.
     3874       
     3875        EXAMPLES::
     3876
     3877            sage: A = Transducer([('A', 'B', [1, 2, 3], 0)],
     3878            ....:                initial_states=['A'], final_states=['B'])
     3879            sage: A.split_transitions().states()
     3880            [('A', ()), ('B', ()),
     3881             ('A', (1,)), ('A', (1, 2))]
     3882        """
     3883        new = self.empty_copy()
     3884        for state in self.states():
     3885            new.add_state(FSMState((state, ()), is_initial=state.is_initial,
     3886                                   is_final=state.is_final))
     3887        for transition in self.transitions():
     3888            for j in range(len(transition.word_in)-1):
     3889                new.add_transition((
     3890                        (transition.from_state, tuple(transition.word_in[:j])),
     3891                        (transition.from_state, tuple(transition.word_in[:j+1])),
     3892                        transition.word_in[j],
     3893                        []))
     3894            new.add_transition((
     3895                    (transition.from_state, tuple(transition.word_in[:-1])),
     3896                    (transition.to_state, ()),
     3897                    transition.word_in[-1:],
     3898                    transition.word_out))
     3899        return new
     3900
     3901
     3902    # *************************************************************************
     3903    # simplifications
     3904    # *************************************************************************
     3905
     3906
     3907    def prepone_output(self):
     3908        """
     3909        Apply the following to each state `s` (except initial and
     3910        final states) of the finite state machine as often as
     3911        possible:
     3912
     3913        If the letter a is prefix of the output label of all
     3914        transitions from `s`, then remove it from all these labels and
     3915        append it to all output labels of all transitions leading to
     3916        `s`.
     3917
     3918        We assume that the states have no output labels.
     3919
     3920        INPUT:
     3921
     3922        Nothing.
     3923
     3924        OUTPUT:
     3925
     3926        Nothing.
     3927
     3928        EXAMPLES::
     3929
     3930            sage: A = Transducer([('A', 'B', 1, 1), ('B', 'B', 0, 0), ('B', 'C', 1, 0)],
     3931            ....:                initial_states=['A'], final_states=['C'])
     3932            sage: A.prepone_output()
     3933            sage: A.transitions()
     3934            [Transition from 'A' to 'B': 1|1,0,
     3935             Transition from 'B' to 'B': 0|0,
     3936             Transition from 'B' to 'C': 1|-]
     3937
     3938        ::
     3939
     3940            sage: B = Transducer([('A', 'B', 0, 1), ('B', 'C', 1, [1, 1]), ('B', 'C', 0, 1)],
     3941            ....:                initial_states=['A'], final_states=['C'])
     3942            sage: B.prepone_output()
     3943            sage: B.transitions()
     3944            [Transition from 'A' to 'B': 0|1,1,
     3945             Transition from 'B' to 'C': 1|1,
     3946             Transition from 'B' to 'C': 0|-]
     3947
     3948        If initial states are not labeled as such, unexpected results may be obtained::
     3949
     3950            sage: C = Transducer([(0,1,0,0)])
     3951            sage: C.prepone_output()
     3952            prepone_output: All transitions leaving state 0 have an
     3953            output label with prefix 0.  However, there is no inbound
     3954            transition and it is not an initial state. This routine
     3955            (possibly called by simplification) therefore erased this
     3956            prefix from all outbound transitions.
     3957            sage: C.transitions()
     3958            [Transition from 0 to 1: 0|-]
     3959
     3960        """
     3961        def find_common_output(state):
     3962            if len(filter(lambda transition: len(transition.word_out) == 0, self.transitions(state))) > 0:
     3963                return ()
     3964            first_letters = set(map(lambda transition: transition.word_out[0], self.transitions(state)))
     3965            if len(first_letters) == 1:
     3966                return (first_letters.pop(),)
     3967            return ()
     3968
     3969        changed = 1
     3970        iteration = 0
     3971        while changed > 0:
     3972            changed = 0
     3973            iteration += 1
     3974            for state in self.states():
     3975                if state.is_initial or state.is_final:
     3976                    continue
     3977                assert len(state.word_out) == 0, \
     3978                    "prepone_output assumes that all states have empty output word, but state %s has output word %s" % \
     3979                    (state, state.word_out)
     3980                common_output = find_common_output(state)
     3981                if len(common_output) > 0:
     3982                    changed += 1
     3983                    for transition in self.transitions(state):
     3984                        assert transition.word_out[0] == common_output[0]
     3985                        transition.word_out = transition.word_out[1:]
     3986                    found_inbound_transition = False
     3987                    for transition in self.transitions():
     3988                        if transition.to_state == state:
     3989                            transition.word_out = transition.word_out + [common_output[0]]
     3990                            found_inbound_transition = True
     3991                    if not found_inbound_transition:
     3992                        print "prepone_output: All transitions leaving state %s have an output label with prefix %s. "\
     3993                            "However, there is no inbound transition and it is not an initial state. "\
     3994                            "This routine (possibly called by simplification) therefore erased this prefix from all "\
     3995                            "outbound transitions." % (state, common_output[0])
     3996
     3997
     3998
     3999    def equivalence_classes(self):
     4000        """
     4001        Returns a list of equivalence classes of states.
     4002
     4003        INPUT:
     4004
     4005        Nothing.
     4006
     4007        OUTPUT:
     4008
     4009        A list of equivalence classes of states.
     4010
     4011        Two states `a` and `b` are equivalent, if and only if for each
     4012        input label word_in the following holds:
     4013
     4014        For paths `p_a` from `a` to `a'` with input label ``word_in``
     4015        and output label ``word_out_a`` and `p_b` from `b` to `b'`
     4016        with input label ``word_in`` and output label ``word_out_b``,
     4017        we have ``word_out_a=word_out_b``, `a'` and `b'` have the same
     4018        output label and are both final or both non-final.
     4019
     4020        The function :meth:`.equivalence_classes` returns a list of
     4021        the equivalence classes to this equivalence relation.
     4022
     4023        This is one step of Moore's minimization algorithm.
     4024
     4025        .. SEEALSO::
     4026
     4027            :meth:`.minimization`
     4028
     4029        EXAMPLES::
     4030
     4031            sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0),
     4032            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     4033            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     4034            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     4035            sage: fsm.equivalence_classes()
     4036            [['A', 'C'], ['B', 'D']]
     4037        """
     4038
     4039        # Two states a and b are said to be 0-equivalent, if their output
     4040        # labels agree and if they are both final or non-final.
     4041        #
     4042        # For some j >= 1, two states a and b are said to be j-equivalent, if
     4043        # they are j-1 equivalent and if for each element letter letter_in of
     4044        # the input alphabet and transitions t_a from a with input label
     4045        # letter_in, output label word_out_a to a' and t_b from b with input
     4046        # label letter_in, output label word_out_b to b', we have
     4047        # word_out_a=word_out_b and a' and b' are j-1 equivalent.
     4048
     4049        # If for some j the relations j-1 equivalent and j-equivalent
     4050        # coincide, then they are equal to the equivalence relation described
     4051        # in the docstring.
     4052
     4053        # classes_current holds the equivalence classes of j-equivalence,
     4054        # classes_previous holds the equivalence classes of j-1 equivalence.
     4055
     4056        if not self.is_deterministic():
     4057            raise NotImplementedError, "Minimization via Moore's Algorithm is only implemented for deterministic finite state machines"
     4058
     4059        # initialize with 0-equivalence
     4060        classes_previous = []
     4061        key_0 = lambda state: (state.is_final, state.word_out)
     4062        states_grouped = full_group_by(self.states(), key=key_0)
     4063        classes_current = [equivalence_class for
     4064                           (key,equivalence_class) in states_grouped]
     4065
     4066        while len(classes_current) != len(classes_previous):
     4067            class_of = {}
     4068            classes_previous = classes_current
     4069            classes_current = []
     4070
     4071            for k in range(len(classes_previous)):
     4072                for state in classes_previous[k]:
     4073                    class_of[state] = k
     4074
     4075            key_current = lambda state: sorted(
     4076                [(transition.word_in,
     4077                  transition.word_out,
     4078                  class_of[transition.to_state])
     4079                 for transition in state.transitions])
     4080
     4081            for class_previous in classes_previous:
     4082                states_grouped = full_group_by(class_previous, key=key_current)
     4083                classes_current.extend([equivalence_class for
     4084                                       (key,equivalence_class) in states_grouped])
     4085
     4086        return classes_current
     4087
     4088
     4089    def quotient(self, classes):
     4090        """
     4091        Constructs the quotient with respect to the equivalence
     4092        classes.
     4093
     4094        INPUT:
     4095
     4096        - ``classes`` is a list of equivalence classes of states.
     4097
     4098        OUTPUT:
     4099       
     4100        A finite state machine.
     4101
     4102        Assume that `c` is a class and `s`, `s'` are states in `c`. If
     4103        there is a transition from `s` to some `t` with input label
     4104        ``word_in`` and output label ``word_out``, then there has to
     4105        be a transition from `s'` to some `t'` with input label
     4106        ``word_in`` and output label ``word_out`` such that `s'` and
     4107        `t'` are states of the same class `c'`. Then there is a
     4108        transition from `c` to `c'` in the quotient with input label
     4109        ``word_in`` and output label ``word_out``.
     4110
     4111        Non-initial states may be merged with initial states, the
     4112        resulting state is an initial state.
     4113
     4114        All states in a class must have the same ``is_final`` and
     4115        ``word_out`` values.
     4116
     4117        EXAMPLES::
     4118
     4119            sage: fsm = FiniteStateMachine([("A", "B", 0, 1), ("A", "B", 1, 0),
     4120            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     4121            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     4122            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     4123            sage: fsmq = fsm.quotient([[fsm.state("A"), fsm.state("C")],
     4124            ....:                      [fsm.state("B"), fsm.state("D")]])
     4125            sage: fsmq.transitions()
     4126            [Transition from ('A', 'C')
     4127                          to ('B', 'D'): 0|1,
     4128             Transition from ('A', 'C')
     4129                          to ('B', 'D'): 1|0,
     4130             Transition from ('B', 'D')
     4131                          to ('A', 'C'): 0|0,
     4132             Transition from ('B', 'D')
     4133                          to ('A', 'C'): 1|1]
     4134            sage: fsmq.relabeled().transitions()
     4135            [Transition from 0 to 1: 0|1,
     4136             Transition from 0 to 1: 1|0,
     4137             Transition from 1 to 0: 0|0,
     4138             Transition from 1 to 0: 1|1]
     4139            sage: fsmq1 = fsm.quotient(fsm.equivalence_classes())
     4140            sage: fsmq1 == fsmq
     4141            True
     4142            sage: fsm.quotient([[fsm.state("A"), fsm.state("B"), fsm.state("C"), fsm.state("D")]])
     4143            Traceback (most recent call last):
     4144                ...
     4145            ValueError: There is a transition Transition from 'B' to 'C': 0|0 in the original transducer, but no corresponding transition in the new transducer.
     4146        """
     4147        new = self.empty_copy()
     4148        state_mapping = {}
     4149
     4150        # Create new states and build state_mapping
     4151        for c in classes:
     4152            new_state = new.add_state(tuple(c))
     4153            for state in c:
     4154                state_mapping[state] = new_state
     4155
     4156        # Copy data from old transducer
     4157        for c in classes:
     4158            new_state = state_mapping[c[0]]
     4159            # copy all data from first class member
     4160            new_state.is_initial = c[0].is_initial
     4161            new_state.is_final = c[0].is_final
     4162            new_state.word_out = c[0].word_out
     4163            for transition in self.iter_transitions(c[0]):
     4164                new.add_transition(
     4165                    from_state=new_state,
     4166                    to_state = state_mapping[transition.to_state],
     4167                    word_in  = transition.word_in,
     4168                    word_out = transition.word_out)
     4169
     4170            # check that all class members have the same information (modulo classes)
     4171            for state in c:
     4172                new_state.is_initial = new_state.is_initial or state.is_initial
     4173                assert new_state.is_final == state.is_final, "Class %s mixes final and non-final states" % (c,)
     4174                assert new_state.word_out == state.word_out, "Class %s mixes different word_out" % (c,)
     4175                assert len(self.transitions(state)) == len(new.transitions(new_state)), \
     4176                    "Class %s has %d outgoing transitions, but class member %s has %d outgoing transitions" %  \
     4177                    (c, len(new.transitions(new_state)), state, len(self.transitions(state)))
     4178                for transition in self.transitions(state):
     4179                    try:
     4180                        new.transition((new_state, state_mapping[transition.to_state], transition.word_in, transition.word_out))
     4181                    except LookupError:
     4182                        raise ValueError, "There is a transition %s in the original transducer, but no corresponding transition in the new transducer." % transition
     4183        return new
     4184
     4185
     4186    # *************************************************************************
     4187    # other
     4188    # *************************************************************************
     4189
     4190
     4191    def graph(self, edge_labels='words_in_out'):
     4192        """
     4193        Returns the graph of the finite state machine with labeled
     4194        vertices and labeled edges.
     4195
     4196        INPUT:
     4197
     4198        - ``edge_label``: (default: ``'words_in_out'``) can be
     4199             - ``'words_in_out'`` (labels will be strings ``'i|o'``)
     4200             - a function with which takes as input a transition
     4201               and outputs (returns) the label
     4202
     4203        OUTPUT:
     4204
     4205        A graph.
     4206
     4207        EXAMPLES::
     4208
     4209            sage: from sage.combinat.finite_state_machine import FSMState
     4210            sage: A = FSMState('A')
     4211            sage: T = Transducer()
     4212            sage: T.graph()
     4213            Digraph on 0 vertices
     4214            sage: T.add_state(A)
     4215            'A'
     4216            sage: T.graph()
     4217            Digraph on 1 vertex
     4218            sage: T.add_transition(('A', 'A', 0, 1))
     4219            Transition from 'A' to 'A': 0|1
     4220            sage: T.graph()
     4221            Looped digraph on 1 vertex
     4222        """
     4223        if edge_labels == 'words_in_out':
     4224            label_fct = lambda t:t._in_out_label_()
     4225        elif hasattr(edge_labels, '__call__'):
     4226            label_fct = edge_labels
     4227        else:
     4228            raise TypeError, 'Wrong argument for edge_labels.'
     4229
     4230        graph_data = []
     4231        isolated_vertices = []
     4232        for state in self.iter_states():
     4233            transitions = state.transitions
     4234            if len(transitions) == 0:
     4235                isolated_vertices.append(state.label())
     4236            for t in transitions:
     4237                graph_data.append((t.from_state.label(), t.to_state.label(),
     4238                                   label_fct(t)))
     4239
     4240        G = DiGraph(graph_data)
     4241        G.add_vertices(isolated_vertices)
     4242        return G
     4243
     4244
     4245    digraph = graph
     4246
     4247
     4248    def plot(self):
     4249        """
     4250        Plots a graph of the finite state machine with labeled
     4251        vertices and labeled edges.
     4252
     4253        INPUT:
     4254
     4255        Nothing.
     4256
     4257        OUTPUT:
     4258
     4259        A plot of the graph of the finite state machine.
     4260
     4261        TESTS::
     4262
     4263            sage: FiniteStateMachine([('A', 'A', 0)]).plot()
     4264        """
     4265        return self.graph(edge_labels='words_in_out').plot()
     4266
     4267
     4268    def predecessors(self, state, valid_input=None):
     4269        """
     4270        Lists all predecessors of a state.
     4271
     4272        INPUT:
     4273
     4274        - ``state`` -- the state from which the predecessors should be
     4275          listed.
     4276
     4277        - ``valid_input`` -- If ``valid_input`` is a list, then we
     4278          only consider transitions whose input labels are contained
     4279          in ``valid_input``. ``state`` has to be a :class:`FSMState`
     4280          (not a label of a state). If input labels of length larger
     4281          than `1` are used, then ``valid_input`` has to be a list of
     4282          lists.
     4283
     4284        OUTPUT:
     4285
     4286        A list of states.
     4287
     4288        EXAMPLES::
     4289
     4290            sage: A = Transducer([('I', 'A', 'a', 'b'), ('I', 'B', 'b', 'c'),
     4291            ....:                 ('I', 'C', 'c', 'a'), ('A', 'F', 'b', 'a'),
     4292            ....:                 ('B', 'F', ['c', 'b'], 'b'), ('C', 'F', 'a', 'c')],
     4293            ....:                initial_states=['I'], final_states=['F'])
     4294            sage: A.predecessors(A.state('A'))
     4295            ['A', 'I']
     4296            sage: A.predecessors(A.state('F'), valid_input=['b', 'a'])
     4297            ['F', 'C', 'A', 'I']
     4298            sage: A.predecessors(A.state('F'), valid_input=[['c', 'b'], 'a'])
     4299            ['F', 'C', 'B']
     4300        """
     4301        if valid_input != None:
     4302            valid_list = list()
     4303            for input in valid_input:
     4304                input_list = input
     4305                if not isinstance(input_list, list):
     4306                    input_list = [input]
     4307                valid_list.append(input_list)
     4308            valid_input = valid_list
     4309
     4310        unhandeled_direct_predecessors = {s:[] for s in self.states() }
     4311        for t in self.transitions():
     4312            if valid_input is None or t.word_in in valid_input:
     4313                unhandeled_direct_predecessors[t.to_state].append(t.from_state)
     4314        done = []
     4315        open = [state]
     4316        while len(open) > 0:
     4317            s = open.pop()
     4318            candidates = unhandeled_direct_predecessors[s]
     4319            if candidates is not None:
     4320                open.extend(candidates)
     4321                unhandeled_direct_predecessors[s] = None
     4322                done.append(s)
     4323        return(done)
     4324
     4325
     4326#*****************************************************************************
     4327
     4328
     4329def is_Automaton(FSM):
     4330    """
     4331    Tests whether or not ``FSM`` inherits from :class:`Automaton`.
     4332
     4333    TESTS::
     4334
     4335        sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine, is_Automaton
     4336        sage: is_Automaton(FiniteStateMachine())
     4337        False
     4338        sage: is_Automaton(Automaton())
     4339        True
     4340        sage: is_FiniteStateMachine(Automaton())
     4341        True
     4342    """
     4343    return isinstance(FSM, Automaton)
     4344
     4345
     4346class Automaton(FiniteStateMachine):
     4347    """
     4348    This creates an automaton, which is a finite state machine, whose
     4349    transitions have input labels.
     4350
     4351    An automaton has additional features like creating a deterministic
     4352    and a minimized automaton.
     4353
     4354    See class :class:`FiniteStateMachine` for more information.
     4355
     4356    EXAMPLES:
     4357
     4358    We can create an automaton recognizing even numbers (given in
     4359    binary and read from left to right) in the following way::
     4360
     4361        sage: A = Automaton([('P', 'Q', 0), ('P', 'P', 1),
     4362        ....:                ('Q', 'P', 1), ('Q', 'Q', 0)],
     4363        ....:               initial_states=['P'], final_states=['Q'])
     4364        sage: A
     4365        Automaton with 2 states
     4366        sage: A([0])[0]
     4367        True
     4368        sage: A([1,1,0])[0]
     4369        True
     4370        sage: A([1,0,1])[0]
     4371        False
     4372
     4373    Note that the full output of the commands above gives more
     4374    information and looks like this::
     4375
     4376        sage: A([1,0,1])
     4377        (False, 'P', [])
     4378
     4379    TESTS::
     4380
     4381        sage: Automaton()
     4382        Automaton with 0 states
     4383    """
     4384
     4385    def _repr_(self):
     4386        """
     4387        Represents the finite state machine as "Automaton with n
     4388        states" where n is the number of states.
     4389
     4390        INPUT:
     4391
     4392        Nothing.
     4393
     4394        OUTPUT:
     4395
     4396        A string.
     4397
     4398        EXAMPLES::
     4399
     4400            sage: Automaton()._repr_()
     4401            'Automaton with 0 states'
     4402        """
     4403        return "Automaton with %s states" % len(self._states_)
     4404
     4405    def _latex_transition_label_(self, transition, format_function=latex):
     4406        r"""
     4407        Returns the proper transition label.
     4408
     4409        INPUT:
     4410
     4411        - ``transition`` - a transition
     4412
     4413        - ``format_function'' - a function formatting the labels
     4414
     4415        OUTPUT:
     4416
     4417        A string.
     4418
     4419        EXAMPLES::
     4420
     4421            sage: F = Automaton([('A', 'B', 1)])
     4422            sage: F._latex_()
     4423            '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\text{\\texttt{A}}}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\text{\\texttt{B}}}\n;\\path[->] (v0) edge node {$\\left[1\\right]$} (v1);\n\\end{tikzpicture}'
     4424
     4425        TESTS::
     4426       
     4427            sage: F = Automaton([('A', 'B', 0, 1)])
     4428            sage: t = F.transitions()[0]
     4429            sage: F._latex_transition_label_(t)
     4430            \left[0\right]
     4431        """
     4432        return format_function(transition.word_in)
     4433
     4434    def determinisation(self):
     4435        """
     4436        Returns a deterministic automaton which accepts the same input
     4437        words as the original one.
     4438
     4439        INPUT:
     4440
     4441        Nothing.
     4442
     4443        OUTPUT:
     4444
     4445        A new automaton, which is deterministic.
     4446
     4447        The labels of the states of the new automaton are frozensets of
     4448        states of ``self``.
     4449
     4450        The input alphabet must be specified. It is restricted to nice
     4451        cases: input words have to have length at most `1`.
     4452
     4453        EXAMPLES::
     4454
     4455            sage: aut = Automaton([('A', 'A', 0), ('A', 'B', 1), ('B', 'B', 1)],
     4456            ....:                 initial_states=['A'], final_states=['B'])
     4457            sage: aut.determinisation().transitions()
     4458            [Transition from frozenset(['A'])
     4459                          to frozenset(['A']): 0|-,
     4460             Transition from frozenset(['A'])
     4461                          to frozenset(['B']): 1|-,
     4462             Transition from frozenset(['B'])
     4463                          to frozenset([]): 0|-,
     4464             Transition from frozenset(['B'])
     4465                          to frozenset(['B']): 1|-,
     4466             Transition from frozenset([])
     4467                          to frozenset([]): 0|-,
     4468             Transition from frozenset([])
     4469                          to frozenset([]): 1|-]
     4470
     4471        ::
     4472
     4473            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     4474            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     4475            ....:               initial_states=['A'], final_states=['C'])
     4476            sage: A.determinisation().states()
     4477            [frozenset(['A']), frozenset(['A', 'B']),
     4478            frozenset(['A', 'C']), frozenset(['A', 'C', 'B'])]
     4479
     4480        TESTS:
     4481
     4482        This is from #15078, comment 13.
     4483
     4484        ::
     4485
     4486            sage: D = {'A': [('A', 'a'), ('B', 'a'), ('A', 'b')],
     4487            ....:      'C': [], 'B': [('C', 'b')]}
     4488            sage: auto = Automaton(D, initial_states=['A'], final_states=['C'])
     4489            sage: auto.is_deterministic()
     4490            False
     4491            sage: auto.process(list('aaab'))
     4492            (False, 'A', [])
     4493            sage: auto.states()
     4494            ['A', 'C', 'B']
     4495            sage: auto.determinisation()
     4496            Automaton with 3 states
     4497        """
     4498        for transition in self.transitions():
     4499            assert len(transition.word_in) <= 1, "%s has input label of length > 1, which we cannot handle" % (transition,)
     4500
     4501        epsilon_successors = {}
     4502        direct_epsilon_successors = {}
     4503        for state in self.states():
     4504            direct_epsilon_successors[state] = set(map(lambda t:t.to_state,
     4505                                                       filter(lambda transition: len(transition.word_in) == 0,
     4506                                                              self.transitions(state)
     4507                                                              )
     4508                                                       )
     4509                                                   )
     4510            epsilon_successors[state] = set([state])
     4511
     4512        old_count_epsilon_successors = 0
     4513        count_epsilon_successors = len(epsilon_successors)
     4514
     4515        while old_count_epsilon_successors < count_epsilon_successors:
     4516            old_count_epsilon_successors = count_epsilon_successors
     4517            count_epsilon_successors = 0
     4518            for state in self.states():
     4519                for direct_successor in direct_epsilon_successors[state]:
     4520                    epsilon_successors[state] = epsilon_successors[state].union(epsilon_successors[direct_successor])
     4521                count_epsilon_successors += len(epsilon_successors[state])
     4522
     4523
     4524        def set_transition(states, letter):
     4525            result = set()
     4526            for state in states:
     4527                for transition in self.transitions(state):
     4528                    if transition.word_in == [letter]:
     4529                        result.add(transition.to_state)
     4530            result = result.union(*map(lambda s:epsilon_successors[s], result))
     4531            return (frozenset(result), [])
     4532
     4533        result = self.empty_copy()
     4534        new_initial_states = [frozenset([state for state in self.initial_states()])]
     4535        result.add_from_transition_function(set_transition,
     4536                                            initial_states=new_initial_states)
     4537
     4538        for state in result.states():
     4539            if any(map(lambda s: s.is_final, state.label())):
     4540                state.is_final = True
     4541
     4542
     4543        return result
     4544
     4545
     4546    def minimization(self, algorithm=None):
     4547        """
     4548        Returns the minimization of the input automaton as a new automaton.
     4549
     4550        INPUT:
     4551
     4552        - ``algorithm`` -- Either Moore's algorithm is used (default
     4553          or ``algorithm='Moore'``), or Brzozowski's algorithm when
     4554          ``algorithm='Brzozowski'``.
     4555
     4556        OUTPUT:
     4557
     4558        A new automaton.
     4559
     4560        The resulting automaton is deterministic and has a minimal
     4561        number of states.
     4562
     4563        EXAMPLES::
     4564
     4565            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     4566            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     4567            ....:               initial_states=['A'], final_states=['C'])
     4568            sage: B = A.minimization(algorithm='Brzozowski')
     4569            sage: B.transitions(B.states()[1])
     4570            [Transition from frozenset([frozenset(['A', 'C', 'B']),
     4571            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to
     4572            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     4573            frozenset(['A', 'C']), frozenset(['C'])]): 0|-,
     4574            Transition from frozenset([frozenset(['A', 'C', 'B']),
     4575            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to
     4576            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     4577            frozenset(['A', 'C'])]): 1|-]
     4578            sage: len(B.states())
     4579            3
     4580            sage: C = A.minimization(algorithm='Brzozowski')
     4581            sage: C.transitions(C.states()[1])
     4582            [Transition from frozenset([frozenset(['A', 'C', 'B']),
     4583            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to
     4584            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     4585            frozenset(['A', 'C']), frozenset(['C'])]): 0|-,
     4586            Transition from frozenset([frozenset(['A', 'C', 'B']),
     4587            frozenset(['C', 'B']), frozenset(['A', 'C'])]) to
     4588            frozenset([frozenset(['A', 'C', 'B']), frozenset(['C', 'B']),
     4589            frozenset(['A', 'C'])]): 1|-]
     4590            sage: len(C.states())
     4591            3
     4592
     4593        ::
     4594
     4595            sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'),
     4596            ....:                  ('3', '2', 'a'), ('2', '1', 'b'),
     4597            ....:                  ('3', '4', 'a'), ('4', '3', 'b')],
     4598            ....:                  initial_states=['1'], final_states=['1'])
     4599            sage: min = aut.minimization(algorithm='Brzozowski')
     4600            sage: [len(min.states()), len(aut.states())]
     4601            [3, 4]
     4602            sage: min = aut.minimization(algorithm='Moore')
     4603            Traceback (most recent call last):
     4604            ...
     4605            NotImplementedError: Minimization via Moore's Algorithm is only
     4606            implemented for deterministic finite state machines
     4607        """
     4608        if algorithm is None or algorithm == "Moore":
     4609            return self._minimization_Moore_()
     4610        elif algorithm == "Brzozowski":
     4611            return self._minimization_Brzozowski_()
     4612        else:
     4613            raise NotImplementedError, "Algorithm '%s' is not implemented. Choose 'Moore' or 'Brzozowski'" % algorithm
     4614
     4615
     4616    def _minimization_Brzozowski_(self):
     4617        """
     4618        Returns a minimized automaton by using Brzozowski's algorithm.
     4619
     4620        See also :meth:`.minimization`.
     4621
     4622        TESTS::
     4623
     4624            sage: A = Automaton([('A', 'A', 1), ('A', 'A', 0), ('A', 'B', 1),
     4625            ....:                ('B', 'C', 0), ('C', 'C', 1), ('C', 'C', 0)],
     4626            ....:               initial_states=['A'], final_states=['C'])
     4627            sage: B = A._minimization_Brzozowski_()
     4628            sage: len(B.states())
     4629            3
     4630        """
     4631        return self.transposition().determinisation().transposition().determinisation()
     4632
     4633
     4634    def _minimization_Moore_(self):
     4635        """
     4636        Returns a minimized automaton by using Brzozowski's algorithm.
     4637
     4638        See also :meth:`.minimization`.
     4639
     4640        TESTS::
     4641
     4642            sage: aut = Automaton([('1', '2', 'a'), ('2', '3', 'b'),
     4643            ....:                  ('3', '2', 'a'), ('2', '1', 'b'),
     4644            ....:                  ('3', '4', 'a'), ('4', '3', 'b')],
     4645            ....:                  initial_states=['1'], final_states=['1'])
     4646            sage: min = aut._minimization_Moore_()
     4647            Traceback (most recent call last):
     4648            ...
     4649            NotImplementedError: Minimization via Moore's Algorithm is only
     4650            implemented for deterministic finite state machines
     4651        """
     4652        return self.quotient(self.equivalence_classes())
     4653
     4654
     4655#*****************************************************************************
     4656
     4657
     4658def is_Transducer(FSM):
     4659    """
     4660    Tests whether or not ``FSM`` inherits from :class:`Transducer`.
     4661
     4662    TESTS::
     4663
     4664        sage: from sage.combinat.finite_state_machine import is_FiniteStateMachine, is_Transducer
     4665        sage: is_Transducer(FiniteStateMachine())
     4666        False
     4667        sage: is_Transducer(Transducer())
     4668        True
     4669        sage: is_FiniteStateMachine(Transducer())
     4670        True
     4671    """
     4672    return isinstance(FSM, Transducer)
     4673
     4674
     4675class Transducer(FiniteStateMachine):
     4676    """
     4677    This creates a transducer, which is a finite state machine, whose
     4678    transitions have input and output labels.
     4679
     4680    An transducer has additional features like creating a simplified
     4681    transducer.
     4682
     4683    See class :class:`FiniteStateMachine` for more information.
     4684
     4685    EXAMPLES:
     4686
     4687    We can create a transducer performing the addition of 1 (for
     4688    numbers given in binary and read from right to left) in the
     4689    following way::
     4690
     4691        sage: T = Transducer([('C', 'C', 1, 0), ('C', 'N', 0, 1),
     4692        ....:                 ('N', 'N', 0, 0), ('N', 'N', 1, 1)],
     4693        ....:                initial_states=['C'], final_states=['N'])
     4694        sage: T
     4695        Transducer with 2 states
     4696        sage: T([0])
     4697        (True, 'N', [1])
     4698        sage: T([1,1,0])
     4699        (True, 'N', [0, 0, 1])
     4700        sage: ZZ(T(15.digits(base=2)+[0])[2], base=2)
     4701        16
     4702
     4703    Note that we have padded the binary input sequence by a `0` so
     4704    that the transducer can reach its final state.
     4705
     4706    TESTS::
     4707
     4708        sage: Transducer()
     4709        Transducer with 0 states
     4710    """
     4711
     4712    def _repr_(self):
     4713        """
     4714        Represents the transducer as "Transducer with n states" where
     4715        n is the number of states.
     4716
     4717        INPUT:
     4718
     4719        Nothing.
     4720
     4721        OUTPUT:
     4722
     4723        A string.
     4724
     4725        EXAMPLES::
     4726
     4727            sage: Transducer()._repr_()
     4728            'Transducer with 0 states'
     4729        """
     4730        return "Transducer with %s states" % len(self._states_)
     4731
     4732    def _latex_transition_label_(self, transition, format_function=latex):
     4733        r"""
     4734        Returns the proper transition label.
     4735
     4736        INPUT:
     4737
     4738        - ``transition`` - a transition
     4739
     4740        - ``format_function'' - a function formatting the labels
     4741
     4742        OUTPUT:
     4743
     4744        A string.
     4745
     4746            sage: F = Transducer([('A', 'B', 1, 2)])
     4747            sage: F._latex_()
     4748            '\\begin{tikzpicture}[auto]\n\\node[state] (v0) at (3.000000,0.000000) {\\text{\\texttt{A}}}\n;\\node[state] (v1) at (-3.000000,0.000000) {\\text{\\texttt{B}}}\n;\\path[->] (v0) edge node {$\\left[1\\right] \\mid \\left[2\\right]$} (v1);\n\\end{tikzpicture}'
     4749
     4750        TESTS::
     4751       
     4752            sage: F = Transducer([('A', 'B', 0, 1)])
     4753            sage: t = F.transitions()[0]
     4754            sage: F._latex_transition_label_(t)
     4755            \left[0\right] \mid \left[1\right]
     4756        """
     4757        return (format_function(transition.word_in) + "\\mid"
     4758                + format_function(transition.word_out))
     4759
     4760
     4761    def simplification(self):
     4762        """
     4763        Returns a simplified transducer.
     4764
     4765        INPUT:
     4766
     4767        Nothing.
     4768
     4769        OUTPUT:
     4770
     4771        A new transducer.
     4772
     4773        This function simplifies a transducer by Moore's algorithm,
     4774        first moving common output labels of transitions leaving a
     4775        state to output labels of transitions entering the state
     4776        (cf. :meth:`.prepone_output`).
     4777
     4778        The resulting transducer implements the same function as the
     4779        original transducer.
     4780
     4781        EXAMPLES::
     4782
     4783            sage: fsm = Transducer([("A", "B", 0, 1), ("A", "B", 1, 0),
     4784            ....:                           ("B", "C", 0, 0), ("B", "C", 1, 1),
     4785            ....:                           ("C", "D", 0, 1), ("C", "D", 1, 0),
     4786            ....:                           ("D", "A", 0, 0), ("D", "A", 1, 1)])
     4787            sage: fsms = fsm.simplification()
     4788            sage: fsms
     4789            Transducer with 2 states
     4790            sage: fsms.transitions()
     4791            [Transition from ('A', 'C')
     4792                          to ('B', 'D'): 0|1,
     4793             Transition from ('A', 'C')
     4794                          to ('B', 'D'): 1|0,
     4795             Transition from ('B', 'D')
     4796                          to ('A', 'C'): 0|0,
     4797             Transition from ('B', 'D')
     4798                          to ('A', 'C'): 1|1]
     4799            sage: fsms.relabeled().transitions()
     4800            [Transition from 0 to 1: 0|1,
     4801             Transition from 0 to 1: 1|0,
     4802             Transition from 1 to 0: 0|0,
     4803             Transition from 1 to 0: 1|1]
     4804        """
     4805        fsm = deepcopy(self)
     4806        fsm.prepone_output()
     4807        return fsm.quotient(fsm.equivalence_classes())
     4808
     4809
     4810#*****************************************************************************
     4811
     4812
     4813def is_FSMProcessIterator(PI):
     4814    """
     4815    Tests whether or not ``PI`` inherits from :class:`FSMProcessIterator`.
     4816
     4817    TESTS::
     4818
     4819        sage: from sage.combinat.finite_state_machine import is_FSMProcessIterator, FSMProcessIterator
     4820        sage: is_FSMProcessIterator(FSMProcessIterator(FiniteStateMachine()))
     4821        Traceback (most recent call last):
     4822        ...
     4823        ValueError: No state is initial.
     4824    """
     4825    return isinstance(PI, FSMProcessIterator)
     4826
     4827
     4828class FSMProcessIterator:
     4829    """
     4830    This class is for processing an input string on a finite state
     4831    machine.
     4832
     4833    An instance of this class is generated when
     4834    :meth:`FiniteStateMachine.process` or
     4835    :meth:`FiniteStateMachine.iter_process` of the finite state
     4836    machine is invoked. It behaves like an iterator which, in each
     4837    step, takes one letter of the input and runs (one step on) the
     4838    finite state machine with this input. More precisely, in each
     4839    step, the process iterator takes an outgoing transition of the
     4840    current state, whose input label equals the input letter of the
     4841    tape. The output label of the transition, if present, is written
     4842    on the output tape.
     4843
     4844    INPUT:
     4845
     4846    - ``fsm`` -- The finite state machine on which the input should be
     4847      processed.
     4848
     4849    - ``input_tape`` -- The input tape. It can be anything that is
     4850      iterable.
     4851
     4852    - ``initial_state`` -- The initial state in which the machine
     4853      starts. If this is ``None``, the unique inital state of the
     4854      finite state machine is takes. If there are several, an error is
     4855      reported.
     4856
     4857    The process (iteration) stops if there are no more input letters
     4858    on the tape. In this case a StopIteration exception is thrown. As
     4859    result the following attributes are available:
     4860
     4861    - ``accept_input`` -- Is True if the reached state is a final state.
     4862
     4863    - ``current_state`` -- The current/reached state in the process.
     4864
     4865    - ``output_tape`` -- The written output.
     4866
     4867    Current values of those attribtes (except ``accept_input``) are
     4868    (also) available during the iteration.
     4869
     4870    OUTPUT:
     4871
     4872    An iterator.
     4873
     4874    EXAMPLES:
     4875
     4876    The following transducer reads binary words and outputs a word,
     4877    where blocks of ones are replaced by just a single one. Further
     4878    only words that end with a zero are accepted.
     4879
     4880    ::
     4881
     4882        sage: T = Transducer({'A': [('A', 0, 0), ('B', 1, None)],
     4883        ....:                 'B': [('B', 1, None), ('A', 0, [1, 0])]},
     4884        ....:     initial_states=['A'], final_states=['A'])
     4885        sage: input = [1, 1, 0, 0, 1, 0, 1, 1, 1, 0]
     4886        sage: T.process(input)
     4887        (True, 'A', [1, 0, 0, 1, 0, 1, 0])
     4888
     4889    The function :meth:`FiniteStateMachine.process` created a new
     4890    ``FSMProcessIterator``. We can do that manually, too, and get full
     4891    access to the iteration process::
     4892
     4893        sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     4894        sage: it = FSMProcessIterator(T, input_tape=input)
     4895        sage: for _ in it:
     4896        ....:     print (it.current_state, it.output_tape)
     4897        ('B', [])
     4898        ('B', [])
     4899        ('A', [1, 0])
     4900        ('A', [1, 0, 0])
     4901        ('B', [1, 0, 0])
     4902        ('A', [1, 0, 0, 1, 0])
     4903        ('B', [1, 0, 0, 1, 0])
     4904        ('B', [1, 0, 0, 1, 0])
     4905        ('B', [1, 0, 0, 1, 0])
     4906        ('A', [1, 0, 0, 1, 0, 1, 0])
     4907        sage: it.accept_input
     4908        True
     4909    """
     4910    def __init__(self, fsm, input_tape=None, initial_state=None):
     4911        """
     4912        See :class:`FSMProcessIterator` for more information.
     4913
     4914        EXAMPLES::
     4915
     4916            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     4917            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     4918            ....:     initial_states=['A'], final_states=['A'])
     4919            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     4920            sage: for _ in it:
     4921            ....:     pass
     4922            sage: it.output_tape
     4923            [1, 0]
     4924        """
     4925        self.fsm = fsm
     4926        if initial_state is None:
     4927            fsm_initial_states = self.fsm.initial_states()
     4928            try:
     4929                self.current_state = fsm_initial_states[0]
     4930            except IndexError:
     4931                raise ValueError, "No state is initial."
     4932            if len(fsm_initial_states) > 1:
     4933                raise ValueError, "Several initial states."
     4934        else:
     4935            self.current_state = initial_state
     4936        self.output_tape = []
     4937        if input_tape is None:
     4938            self._input_tape_iter_ = iter([])
     4939        else:
     4940            if hasattr(input_tape, '__iter__'):
     4941                self._input_tape_iter_ = iter(input_tape)
     4942            else:
     4943                raise ValueError, "Given input tape is not iterable."
     4944
     4945    def __iter__(self):
     4946        """
     4947        Returns ``self``.
     4948
     4949        TESTS::
     4950
     4951            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     4952            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     4953            ....:     initial_states=['A'], final_states=['A'])
     4954            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     4955            sage: id(it) == id(iter(it))
     4956            True
     4957        """
     4958        return self
     4959
     4960    def next(self):
     4961        """
     4962        Makes one step in processing the input tape.
     4963
     4964        INPUT:
     4965       
     4966        Nothing.
     4967
     4968        OUTPUT:
     4969
     4970        It returns the taken transition. A ``StopIteration`` exception is
     4971        thrown when there is nothing more to read.
     4972
     4973        EXAMPLES::
     4974
     4975            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     4976            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     4977            ....:     initial_states=['A'], final_states=['A'])
     4978            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     4979            sage: it.next()
     4980            Transition from 'A' to 'A': 0|1
     4981            sage: it.next()
     4982            Transition from 'A' to 'A': 1|0
     4983            sage: it.next()
     4984            Traceback (most recent call last):
     4985            ...
     4986            StopIteration
     4987        """
     4988        if hasattr(self, 'accept_input'):
     4989            raise StopIteration
     4990        try:
     4991            # process current state
     4992            transition = None
     4993            try:
     4994                transition = self.current_state.hook(
     4995                    self.current_state, self)
     4996            except AttributeError:
     4997                pass
     4998            self.write_word(self.current_state.word_out)
     4999
     5000            # get next
     5001            if not isinstance(transition, FSMTransition):
     5002                next_word = []
     5003                found = False
     5004
     5005                try:
     5006                    while not found:
     5007                        next_word.append(self.read_letter())
     5008                        try:
     5009                            transition = self.get_next_transition(
     5010                                next_word)
     5011                            found = True
     5012                        except ValueError:
     5013                            pass
     5014                except StopIteration:
     5015                    # this means input tape is finished
     5016                    if len(next_word) > 0:
     5017                        self.accept_input = False
     5018                    raise StopIteration
     5019
     5020            # process transition
     5021            try:
     5022                transition.hook(transition, self)
     5023            except AttributeError:
     5024                pass
     5025            self.write_word(transition.word_out)
     5026
     5027            # go to next state
     5028            self.current_state = transition.to_state
     5029
     5030        except StopIteration:
     5031            # this means, either input tape is finished or
     5032            # someone has thrown StopIteration manually (in one
     5033            # of the hooks)
     5034            if not self.current_state.is_final:
     5035                self.accept_input = False
     5036            if not hasattr(self, 'accept_input'):
     5037                self.accept_input = True
     5038            raise StopIteration
     5039
     5040        # return
     5041        return transition
     5042
     5043    def read_letter(self):
     5044        """
     5045        Reads a letter from the input tape.
     5046
     5047        INPUT:
     5048
     5049        Nothing.
     5050       
     5051        OUTPUT:
     5052
     5053        A letter.
     5054
     5055        Exception ``StopIteration`` is thrown if tape has reached
     5056        the end.
     5057
     5058        EXAMPLES::
     5059
     5060            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     5061            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     5062            ....:     initial_states=['A'], final_states=['A'])
     5063            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     5064            sage: it.read_letter()
     5065            0
     5066        """
     5067        return self._input_tape_iter_.next()
     5068
     5069    def write_letter(self, letter):
     5070        """
     5071        Writes a letter on the output tape.
     5072
     5073        INPUT:
     5074
     5075        - ``letter`` -- the letter to be written.
     5076
     5077        OUTPUT:
     5078       
     5079        Nothing.
     5080
     5081        EXAMPLES::
     5082
     5083            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     5084            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     5085            ....:     initial_states=['A'], final_states=['A'])
     5086            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     5087            sage: it.write_letter(42)
     5088            sage: it.output_tape
     5089            [42]
     5090        """
     5091        self.output_tape.append(letter)
     5092
     5093    def write_word(self, word):
     5094        """
     5095        Writes a word on the output tape.
     5096
     5097        INPUT:
     5098
     5099        - ``word`` -- the word to be written.
     5100
     5101        OUTPUT:
     5102       
     5103        Nothing.
     5104
     5105        EXAMPLES::
     5106
     5107            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     5108            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     5109            ....:     initial_states=['A'], final_states=['A'])
     5110            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     5111            sage: it.write_word([4, 2])
     5112            sage: it.output_tape
     5113            [4, 2]
     5114        """
     5115        for letter in word:
     5116            self.write_letter(letter)
     5117
     5118    def get_next_transition(self, word_in):
     5119        """
     5120        Returns the next transition according to ``word_in``. It is
     5121        assumed that we are in state ``self.current_state``.
     5122
     5123        INPUT:
     5124
     5125        - ``word_in`` -- the input word.
     5126
     5127        OUTPUT:
     5128
     5129        The next transition according to ``word_in``. It is assumed
     5130        that we are in state ``self.current_state``.
     5131
     5132        EXAMPLES::
     5133
     5134            sage: from sage.combinat.finite_state_machine import FSMProcessIterator
     5135            sage: inverter = Transducer({'A': [('A', 0, 1), ('A', 1, 0)]},
     5136            ....:     initial_states=['A'], final_states=['A'])
     5137            sage: it = FSMProcessIterator(inverter, input_tape=[0, 1])
     5138            sage: it.get_next_transition([0])
     5139            Transition from 'A' to 'A': 0|1
     5140        """
     5141        for transition in self.current_state.transitions:
     5142            if transition.word_in == word_in:
     5143                return transition
     5144        raise ValueError
     5145
     5146
     5147#*****************************************************************************
     5148
     5149
     5150def setup_latex_preamble():
     5151    """
     5152    This function adds the package ``tikz`` with support for automata
     5153    to the preamble of Latex so that the finite state machines can be
     5154    drawn nicely.
     5155
     5156    INPUT:
     5157
     5158    Nothing.
     5159
     5160    OUTPUT:
     5161   
     5162    Nothing.
     5163
     5164    TESTS::
     5165
     5166        sage: from sage.combinat.finite_state_machine import setup_latex_preamble
     5167        sage: setup_latex_preamble()
     5168    """
     5169    latex.add_package_to_preamble_if_available('tikz')
     5170    latex.add_to_preamble('\\usetikzlibrary{automata}')
     5171
     5172
     5173#*****************************************************************************