Skip to content

Commit

Permalink
Trac #16758: FiniteStateMachine.composition: check types
Browse files Browse the repository at this point in the history
The output of an automaton should not be feedable into a transducer
(because there is none); when composing an automaton with a transducer,
the result should be an automaton.

The aim would be:

{{{
sage: from sage.combinat.finite_state_machine import (
....:     is_Automaton, is_Transducer)
sage: T = Transducer([(0, 0, 0, 0)], initial_states=[0])
sage: A = Automaton([(0, 0, 0)], initial_states=[0])
sage: T.composition(A, algorithm='direct')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: T.composition(A, algorithm='explorative')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: A.composition(A, algorithm='direct')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: A.composition(A, algorithm='explorative')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: is_Automaton(A.composition(T, algorithm='direct'))
True
sage: is_Automaton(A.composition(T, algorithm='explorative'))
True
}}}

This is now achieved by this patch.

URL: http://trac.sagemath.org/16758
Reported by: cheuberg
Ticket author(s): Clemens Heuberger
Reviewer(s): Daniel Krenn
  • Loading branch information
Release Manager authored and vbraun committed Aug 5, 2014
2 parents 0498b06 + 61f6acf commit bc7c91c
Showing 1 changed file with 92 additions and 8 deletions.
100 changes: 92 additions & 8 deletions src/sage/combinat/finite_state_machine.py
Expand Up @@ -2698,6 +2698,8 @@ def __init__(self,
if with_final_word_out is not None:
self.construct_final_word_out(with_final_word_out)

self._allow_composition_ = True


#*************************************************************************
# copy and hash
Expand Down Expand Up @@ -2729,7 +2731,7 @@ def __copy__(self):
copy = __copy__


def empty_copy(self, memo=None):
def empty_copy(self, memo=None, new_class=None):
"""
Returns an empty deep copy of the finite state machine, i.e.,
``input_alphabet``, ``output_alphabet``, ``on_duplicate_transition``
Expand All @@ -2739,6 +2741,9 @@ def empty_copy(self, memo=None):
- ``memo`` -- a dictionary storing already processed elements.
- ``new_class`` -- a class for the copy. By default
(``None``), the class of ``self`` is used.
OUTPUT:
A new finite state machine.
Expand All @@ -2758,8 +2763,19 @@ def empty_copy(self, memo=None):
[2, 3]
sage: FE.on_duplicate_transition == duplicate_transition_raise_error
True
TESTS::
sage: T = Transducer()
sage: type(T.empty_copy())
<class 'sage.combinat.finite_state_machine.Transducer'>
sage: type(T.empty_copy(new_class=Automaton))
<class 'sage.combinat.finite_state_machine.Automaton'>
"""
new = self.__class__()
if new_class is None:
new = self.__class__()
else:
new = new_class()
new.input_alphabet = deepcopy(self.input_alphabet, memo)
new.output_alphabet = deepcopy(self.output_alphabet, memo)
new.on_duplicate_transition = self.on_duplicate_transition
Expand Down Expand Up @@ -5691,7 +5707,8 @@ def intersection(self, other):
def product_FiniteStateMachine(self, other, function,
new_input_alphabet=None,
only_accessible_components=True,
final_function=None):
final_function=None,
new_class=None):
r"""
Returns a new finite state machine whose states are
`d`-tuples of states of the original finite state machines.
Expand Down Expand Up @@ -5722,6 +5739,9 @@ def product_FiniteStateMachine(self, other, function,
outputs of the constituent states are empty; otherwise, a
``ValueError`` is raised.
- ``new_class`` -- Class of the new finite state machine. By
default (``None``), the class of ``self`` is used.
OUTPUT:
A finite state machine whose states are `d`-tuples of states of the
Expand Down Expand Up @@ -5863,6 +5883,15 @@ def product_FiniteStateMachine(self, other, function,
...
ValueError: other must be a finite state machine or a list
of finite state machines.
Test whether ``new_class`` works::
sage: T = Transducer()
sage: type(T.product_FiniteStateMachine(T, None))
<class 'sage.combinat.finite_state_machine.Transducer'>
sage: type(T.product_FiniteStateMachine(T, None,
....: new_class=Automaton))
<class 'sage.combinat.finite_state_machine.Automaton'>
"""
def default_final_function(*args):
if any(s.final_word_out for s in args):
Expand All @@ -5872,7 +5901,7 @@ def default_final_function(*args):
if final_function is None:
final_function = default_final_function

result = self.empty_copy()
result = self.empty_copy(new_class=new_class)
if new_input_alphabet is not None:
result.input_alphabet = new_input_alphabet
else:
Expand Down Expand Up @@ -6096,7 +6125,43 @@ def composition(self, other, algorithm=None,
....: (2, 2, 0, 1), (2, 1, 1, 1)],
....: initial_states=[1], final_states=[1])
sage: Hd = G.composition(F, algorithm='direct')
In the following examples, we compose transducers and automata
and check whether the types are correct. ::
sage: from sage.combinat.finite_state_machine import (
....: is_Automaton, is_Transducer)
sage: T = Transducer([(0, 0, 0, 0)], initial_states=[0])
sage: A = Automaton([(0, 0, 0)], initial_states=[0])
sage: is_Transducer(T.composition(T, algorithm='direct'))
True
sage: is_Transducer(T.composition(T, algorithm='explorative'))
True
sage: T.composition(A, algorithm='direct')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: T.composition(A, algorithm='explorative')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: A.composition(A, algorithm='direct')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: A.composition(A, algorithm='explorative')
Traceback (most recent call last):
...
TypeError: Composition with automaton is not possible.
sage: is_Automaton(A.composition(T, algorithm='direct'))
True
sage: is_Automaton(A.composition(T, algorithm='explorative'))
True
"""
if not other._allow_composition_:
raise TypeError("Composition with automaton is not "
"possible.")

if algorithm is None:
algorithm = 'direct'
if algorithm == 'direct':
Expand Down Expand Up @@ -6138,7 +6203,8 @@ def function(transition1, transition2):
result = other.product_FiniteStateMachine(
self, function,
only_accessible_components=only_accessible_components,
final_function=lambda s1, s2: [])
final_function=lambda s1, s2: [],
new_class=self.__class__)

for state_result in result.iter_states():
state = state_result.label()[0]
Expand Down Expand Up @@ -6179,8 +6245,9 @@ def _composition_explorative_(self, other):
new colors have to be hashable such that
:meth:`Automaton.determinisation` does not fail::
sage: A = Automaton([[0, 0, 0]], initial_states=[0])
sage: B = A.composition(A, algorithm='explorative')
sage: T = Transducer([[0, 0, 0, 0]], initial_states=[0])
sage: A = T.input_projection()
sage: B = A.composition(T, algorithm='explorative')
sage: B.states()[0].color
(None, None)
sage: B.determinisation()
Expand Down Expand Up @@ -6227,7 +6294,7 @@ def composition_transition(state, input):
"currently not implemented for "
"non-deterministic transducers.")

F = other.empty_copy()
F = other.empty_copy(new_class=self.__class__)
new_initial_states = [(other.initial_states()[0], self.initial_states()[0])]
F.add_from_transition_function(composition_transition,
initial_states=new_initial_states)
Expand Down Expand Up @@ -8125,6 +8192,23 @@ class Automaton(FiniteStateMachine):
Automaton with 0 states
"""

def __init__(self, *args, **kwargs):
"""
Initialize an automaton. See :class:`Automaton` and its parent
:class:`FiniteStateMachine` for more information.
TESTS::
sage: Transducer()._allow_composition_
True
sage: Automaton()._allow_composition_
False
"""
super(Automaton, self).__init__(*args, **kwargs)
self._allow_composition_ = False


def _repr_(self):
"""
Represents the finite state machine as "Automaton with n
Expand Down

0 comments on commit bc7c91c

Please sign in to comment.