def Wait(self, input_alphabet, threshold=1): r""" Writes ``False`` until reading the ``threshold``-th occurrence of a true input letter; then writes ``True``. INPUT: - ``input_alphabet`` -- a list or other iterable. - ``threshold`` -- a positive integer specifying how many occurrences of ``True`` inputs are waited for. OUTPUT: A transducer writing ``False`` until the ``threshold``-th true (Python's standard conversion to boolean is used to convert the actual input to boolean) input is read. Subsequently, the transducer writes ``True``. EXAMPLES:: sage: T = transducers.Wait([0, 1]) sage: T([0, 0, 1, 0, 1, 0]) [False, False, True, True, True, True] sage: T2 = transducers.Wait([0, 1], threshold=2) sage: T2([0, 0, 1, 0, 1, 0]) [False, False, False, False, True, True] """ def transition(state, input): if state == threshold: return (threshold, True) if not input: return (state, False) return (state + 1, state + 1 == threshold) T = Transducer(transition, input_alphabet=input_alphabet, initial_states=[0]) for s in T.iter_states(): s.is_final = True return T
def GrayCode(self): """ Returns a transducer converting the standard binary expansion to Gray code. INPUT: Nothing. OUTPUT: A transducer. Cf. the :wikipedia:`Gray_code` for a description of the Gray code. EXAMPLE:: sage: G = transducers.GrayCode() sage: G Transducer with 3 states sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: for v in srange(0, 10): ....: print v, G(v.digits(base=2)) 0 [] 1 [1] 2 [1, 1] 3 [0, 1] 4 [0, 1, 1] 5 [1, 1, 1] 6 [1, 0, 1] 7 [0, 0, 1] 8 [0, 0, 1, 1] 9 [1, 0, 1, 1] In the example :ref:`Gray Code <finite_state_machine_gray_code_example>` in the documentation of the :mod:`~sage.combinat.finite_state_machine` module, the Gray code transducer is derived from the algorithm converting the binary expansion to the Gray code. The result is the same as the one given here. """ z = ZZ(0) o = ZZ(1) return Transducer([[0, 1, z, None], [0, 2, o, None], [1, 1, z, z], [1, 2, o, o], [2, 1, z, o], [2, 2, o, z]], initial_states=[0], final_states=[1], with_final_word_out=[0])
def Identity(self, input_alphabet): """ Returns the identity transducer realizing the identity map. INPUT: - ``input_alphabet`` -- a list or other iterable. OUTPUT: A transducer mapping each word over ``input_alphabet`` to itself. EXAMPLES:: sage: T = transducers.Identity([0, 1]) sage: sorted(T.transitions()) [Transition from 0 to 0: 0|0, Transition from 0 to 0: 1|1] sage: T.initial_states() [0] sage: T.final_states() [0] sage: T.input_alphabet [0, 1] sage: T.output_alphabet [0, 1] sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T([0, 1, 0, 1, 1]) [0, 1, 0, 1, 1] """ return Transducer( [(0, 0, d, d) for d in input_alphabet], input_alphabet=input_alphabet, output_alphabet=input_alphabet, initial_states=[0], final_states=[0])
def abs(self, input_alphabet): """ Returns a transducer which realizes the letter-wise absolute value of an input word over the given input alphabet. INPUT: - ``input_alphabet`` -- a list or other iterable. OUTPUT: A transducer mapping `i_0\ldots i_k` to `|i_0|\ldots |i_k|`. EXAMPLE: The following transducer realizes letter-wise absolute value:: sage: T = transducers.abs([-1, 0, 1]) sage: T.transitions() [Transition from 0 to 0: -1|1, Transition from 0 to 0: 0|0, Transition from 0 to 0: 1|1] sage: T.initial_states() [0] sage: T.final_states() [0] sage: T([-1, -1, 0, 1]) [1, 1, 0, 1] """ return Transducer(lambda state, input: (0, abs(input)), input_alphabet=input_alphabet, initial_states=[0], final_states=[0])
def CountSubblockOccurrences(self, block, input_alphabet): """ Returns a transducer counting the number of (possibly overlapping) occurrences of a block in the input. INPUT: - ``block`` -- a list (or other iterable) of letters. - ``input_alphabet`` -- a list or other iterable. OUTPUT: A transducer counting (in unary) the number of occurrences of the given block in the input. Overlapping occurrences are counted several times. Denoting the block by `b_0\ldots b_{k-1}`, the input word by `i_0\ldots i_L` and the output word by `o_0\ldots o_L`, we have `o_j = 1` if and only if `i_{j-k+1}\ldots i_{j} = b_0\ldots b_{k-1}`. Otherwise, `o_j = 0`. EXAMPLES: #. Counting the number of ``10`` blocks over the alphabet ``[0, 1]``:: sage: T = transducers.CountSubblockOccurrences( ....: [1, 0], ....: [0, 1]) sage: sorted(T.transitions()) [Transition from () to (): 0|0, Transition from () to (1,): 1|0, Transition from (1,) to (): 0|1, Transition from (1,) to (1,): 1|0] sage: T.input_alphabet [0, 1] sage: T.output_alphabet [0, 1] sage: T.initial_states() [()] sage: T.final_states() [(), (1,)] Check some sequence:: sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T([0, 1, 0, 1, 1, 0]) [0, 0, 1, 0, 0, 1] #. Counting the number of ``11`` blocks over the alphabet ``[0, 1]``:: sage: T = transducers.CountSubblockOccurrences( ....: [1, 1], ....: [0, 1]) sage: sorted(T.transitions()) [Transition from () to (): 0|0, Transition from () to (1,): 1|0, Transition from (1,) to (): 0|0, Transition from (1,) to (1,): 1|1] Check some sequence:: sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T([0, 1, 0, 1, 1, 0]) [0, 0, 0, 0, 1, 0] #. Counting the number of ``1010`` blocks over the alphabet ``[0, 1, 2]``:: sage: T = transducers.CountSubblockOccurrences( ....: [1, 0, 1, 0], ....: [0, 1, 2]) sage: sorted(T.transitions()) [Transition from () to (): 0|0, Transition from () to (1,): 1|0, Transition from () to (): 2|0, Transition from (1,) to (1, 0): 0|0, Transition from (1,) to (1,): 1|0, Transition from (1,) to (): 2|0, Transition from (1, 0) to (): 0|0, Transition from (1, 0) to (1, 0, 1): 1|0, Transition from (1, 0) to (): 2|0, Transition from (1, 0, 1) to (1, 0): 0|1, Transition from (1, 0, 1) to (1,): 1|0, Transition from (1, 0, 1) to (): 2|0] sage: input = [0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 2] sage: output = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0] sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T(input) == output True """ block_as_tuple = tuple(block) def starts_with(what, pattern): return len(what) >= len(pattern) \ and what[:len(pattern)] == pattern def transition_function(read, input): current = read + (input, ) if starts_with(block_as_tuple, current) \ and len(block_as_tuple) > len(current): return (current, 0) else: k = 1 while not starts_with(block_as_tuple, current[k:]): k += 1 return (current[k:], int(block_as_tuple == current)) T = Transducer( transition_function, input_alphabet=input_alphabet, output_alphabet=[0, 1], initial_states=[()]) for s in T.iter_states(): s.is_final = True return T
def weight(self, input_alphabet, zero=0): r""" Returns a transducer which realizes the Hamming weight of the input over the given input alphabet. INPUT: - ``input_alphabet`` -- a list or other iterable. - ``zero`` -- the zero symbol in the alphabet used OUTPUT: A transducer mapping `i_0\ldots i_k` to `(i_0\neq 0)\ldots(i_k\neq 0)`. The Hamming weight is defined as the number of non-zero digits in the input sequence over the alphabet ``input_alphabet`` (see :wikipedia:`Hamming_weight`). The output sequence of the transducer is a unary encoding of the Hamming weight. Thus the sum of the output sequence is the Hamming weight of the input. EXAMPLES:: sage: W = transducers.weight([-1, 0, 2]) sage: W.transitions() [Transition from 0 to 0: -1|1, Transition from 0 to 0: 0|0, Transition from 0 to 0: 2|1] sage: unary_weight = W([-1, 0, 0, 2, -1]) sage: unary_weight [1, 0, 0, 1, 1] sage: weight = add(unary_weight) sage: weight 3 Also the joint Hamming weight can be computed:: sage: v1 = vector([-1, 0]) sage: v0 = vector([0, 0]) sage: W = transducers.weight([v1, v0]) sage: unary_weight = W([v1, v0, v1, v0]) sage: add(unary_weight) 2 For the input alphabet ``[-1, 0, 1]`` the weight transducer is the same as the absolute value transducer :meth:`~TransducerGenerators.abs`:: sage: W = transducers.weight([-1, 0, 1]) sage: A = transducers.abs([-1, 0, 1]) sage: W == A True For other input alphabets, we can specify the zero symbol:: sage: W = transducers.weight(['a', 'b'], zero='a') sage: add(W(['a', 'b', 'b'])) 2 """ def weight(state, input): weight = int(input != zero) return (0, weight) return Transducer(weight, input_alphabet=input_alphabet, initial_states=[0], final_states=[0])
def operator(self, operator, input_alphabet, number_of_operands=2): r""" Returns a transducer which realizes an operation on tuples over the given input alphabet. INPUT: - ``operator`` -- operator to realize. It is a function which takes ``number_of_operands`` input arguments (each out of ``input_alphabet``). - ``input_alphabet`` -- a list or other iterable. - ``number_of_operands`` -- (default: `2`) it specifies the number of input arguments the operator takes. OUTPUT: A transducer mapping an input letter `(i_1, \dots, i_n)` to `\mathrm{operator}(i_1, \dots, i_n)`. Here, `n` equals ``number_of_operands``. The input alphabet of the generated transducer is the cartesian product of ``number_of_operands`` copies of ``input_alphabet``. EXAMPLE: The following binary transducer realizes component-wise addition (this transducer is also available as :meth:`.add`):: sage: import operator sage: T = transducers.operator(operator.add, [0, 1]) sage: T.transitions() [Transition from 0 to 0: (0, 0)|0, Transition from 0 to 0: (0, 1)|1, Transition from 0 to 0: (1, 0)|1, Transition from 0 to 0: (1, 1)|2] sage: T.input_alphabet [(0, 0), (0, 1), (1, 0), (1, 1)] sage: T.initial_states() [0] sage: T.final_states() [0] sage: T([(0, 0), (0, 1), (1, 0), (1, 1)]) [0, 1, 1, 2] Note that for a unary operator the input letters of the new transducer are tuples of length `1`:: sage: T = transducers.operator(abs, ....: [-1, 0, 1], ....: number_of_operands=1) sage: T([-1, 1, 0]) Traceback (most recent call last): ... ValueError: Invalid input sequence. sage: T([(-1,), (1,), (0,)]) [1, 1, 0] Compare this with the transducer generated by :meth:`.abs`:: sage: T = transducers.abs([-1, 0, 1]) sage: T([-1, 1, 0]) [1, 1, 0] """ from itertools import product def transition_function(state, operands): return (0, operator(*operands)) pairs = list(product(input_alphabet, repeat=number_of_operands)) return Transducer(transition_function, input_alphabet=pairs, initial_states=[0], final_states=[0])
def CountSubblockOccurrences(self, block, input_alphabet): """ Returns a transducer counting the number of (possibly overlapping) occurrences of a block in the input. INPUT: - ``block`` -- a list (or other iterable) of letters. - ``input_alphabet`` -- a list or other iterable. OUTPUT: A transducer counting (in unary) the number of occurrences of the given block in the input. Overlapping occurrences are counted several times. Denoting the block by `b_0\ldots b_{k-1}`, the input word by `i_0\ldots i_L` and the output word by `o_0\ldots o_L`, we have `o_j = 1` if and only if `i_{j-k+1}\ldots i_{j} = b_0\ldots b_{k-1}`. Otherwise, `o_j = 0`. EXAMPLES: #. Counting the number of ``10`` blocks over the alphabet ``[0, 1]``:: sage: T = transducers.CountSubblockOccurrences( ....: [1, 0], ....: [0, 1]) sage: sorted(T.transitions()) [Transition from () to (): 0|0, Transition from () to (1,): 1|0, Transition from (1,) to (): 0|1, Transition from (1,) to (1,): 1|0] sage: T.input_alphabet [0, 1] sage: T.output_alphabet [0, 1] sage: T.initial_states() [()] sage: T.final_states() [(), (1,)] Check some sequence:: sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T([0, 1, 0, 1, 1, 0]) [0, 0, 1, 0, 0, 1] #. Counting the number of ``11`` blocks over the alphabet ``[0, 1]``:: sage: T = transducers.CountSubblockOccurrences( ....: [1, 1], ....: [0, 1]) sage: sorted(T.transitions()) [Transition from () to (): 0|0, Transition from () to (1,): 1|0, Transition from (1,) to (): 0|0, Transition from (1,) to (1,): 1|1] Check some sequence:: sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T([0, 1, 0, 1, 1, 0]) [0, 0, 0, 0, 1, 0] #. Counting the number of ``1010`` blocks over the alphabet ``[0, 1, 2]``:: sage: T = transducers.CountSubblockOccurrences( ....: [1, 0, 1, 0], ....: [0, 1, 2]) sage: sorted(T.transitions()) [Transition from () to (): 0|0, Transition from () to (1,): 1|0, Transition from () to (): 2|0, Transition from (1,) to (1, 0): 0|0, Transition from (1,) to (1,): 1|0, Transition from (1,) to (): 2|0, Transition from (1, 0) to (): 0|0, Transition from (1, 0) to (1, 0, 1): 1|0, Transition from (1, 0) to (): 2|0, Transition from (1, 0, 1) to (1, 0): 0|1, Transition from (1, 0, 1) to (1,): 1|0, Transition from (1, 0, 1) to (): 2|0] sage: input = [0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 2] sage: output = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0] sage: sage.combinat.finite_state_machine.FSMOldProcessOutput = False sage: T(input) == output True """ block_as_tuple = tuple(block) def starts_with(what, pattern): return len(what) >= len(pattern) \ and what[:len(pattern)] == pattern def transition_function(read, input): current = read + (input, ) if starts_with(block_as_tuple, current) \ and len(block_as_tuple) > len(current): return (current, 0) else: k = 1 while not starts_with(block_as_tuple, current[k:]): k += 1 return (current[k:], int(block_as_tuple == current)) T = Transducer(transition_function, input_alphabet=input_alphabet, output_alphabet=[0, 1], initial_states=[()]) for s in T.iter_states(): s.is_final = True return T