passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
- passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
- passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
- passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
- passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
- passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
- passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
- sage/all__sagemath_graphs.py +39 -0
- sage/combinat/abstract_tree.py +2723 -0
- sage/combinat/all__sagemath_graphs.py +34 -0
- sage/combinat/binary_tree.py +5306 -0
- sage/combinat/cluster_algebra_quiver/all.py +22 -0
- sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
- sage/combinat/cluster_algebra_quiver/interact.py +124 -0
- sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
- sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
- sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
- sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
- sage/combinat/designs/MOLS_handbook_data.py +570 -0
- sage/combinat/designs/all.py +58 -0
- sage/combinat/designs/bibd.py +1655 -0
- sage/combinat/designs/block_design.py +1071 -0
- sage/combinat/designs/covering_array.py +269 -0
- sage/combinat/designs/covering_design.py +530 -0
- sage/combinat/designs/database.py +5615 -0
- sage/combinat/designs/design_catalog.py +122 -0
- sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/designs_pyx.pxd +21 -0
- sage/combinat/designs/designs_pyx.pyx +993 -0
- sage/combinat/designs/difference_family.py +3951 -0
- sage/combinat/designs/difference_matrices.py +279 -0
- sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
- sage/combinat/designs/ext_rep.py +1064 -0
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
- sage/combinat/designs/group_divisible_designs.py +361 -0
- sage/combinat/designs/incidence_structures.py +2357 -0
- sage/combinat/designs/latin_squares.py +581 -0
- sage/combinat/designs/orthogonal_arrays.py +2244 -0
- sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
- sage/combinat/designs/resolvable_bibd.py +815 -0
- sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
- sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +530 -0
- sage/combinat/designs/twographs.py +306 -0
- sage/combinat/finite_state_machine.py +14874 -0
- sage/combinat/finite_state_machine_generators.py +2006 -0
- sage/combinat/graph_path.py +448 -0
- sage/combinat/interval_posets.py +3908 -0
- sage/combinat/nu_tamari_lattice.py +269 -0
- sage/combinat/ordered_tree.py +1446 -0
- sage/combinat/posets/all.py +46 -0
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/cartesian_product.py +493 -0
- sage/combinat/posets/d_complete.py +182 -0
- sage/combinat/posets/elements.py +273 -0
- sage/combinat/posets/forest.py +30 -0
- sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +174 -0
- sage/combinat/posets/hasse_diagram.py +3672 -0
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +794 -0
- sage/combinat/posets/lattices.py +5117 -0
- sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/combinat/posets/linear_extension_iterator.pyx +292 -0
- sage/combinat/posets/linear_extensions.py +1037 -0
- sage/combinat/posets/mobile.py +275 -0
- sage/combinat/posets/moebius_algebra.py +776 -0
- sage/combinat/posets/poset_examples.py +2178 -0
- sage/combinat/posets/posets.py +9360 -0
- sage/combinat/rooted_tree.py +1070 -0
- sage/combinat/shard_order.py +239 -0
- sage/combinat/tamari_lattices.py +384 -0
- sage/combinat/yang_baxter_graph.py +923 -0
- sage/databases/all__sagemath_graphs.py +1 -0
- sage/databases/knotinfo_db.py +1231 -0
- sage/ext_data/all__sagemath_graphs.py +1 -0
- sage/ext_data/graphs/graph_plot_js.html +330 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/graphs/all.py +42 -0
- sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/asteroidal_triples.pyx +320 -0
- sage/graphs/base/all.py +1 -0
- sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/boost_graph.pxd +106 -0
- sage/graphs/base/boost_graph.pyx +3045 -0
- sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/c_graph.pxd +106 -0
- sage/graphs/base/c_graph.pyx +5096 -0
- sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/dense_graph.pxd +28 -0
- sage/graphs/base/dense_graph.pyx +801 -0
- sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/graph_backends.pxd +5 -0
- sage/graphs/base/graph_backends.pyx +797 -0
- sage/graphs/base/overview.py +85 -0
- sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/sparse_graph.pxd +90 -0
- sage/graphs/base/sparse_graph.pyx +1653 -0
- sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_dense_graph.pxd +5 -0
- sage/graphs/base/static_dense_graph.pyx +1032 -0
- sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_backend.pxd +27 -0
- sage/graphs/base/static_sparse_backend.pyx +1583 -0
- sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/base/static_sparse_graph.pxd +37 -0
- sage/graphs/base/static_sparse_graph.pyx +1375 -0
- sage/graphs/bipartite_graph.py +2732 -0
- sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/centrality.pyx +1038 -0
- sage/graphs/cographs.py +519 -0
- sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/comparability.pyx +851 -0
- sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/connectivity.pxd +157 -0
- sage/graphs/connectivity.pyx +4813 -0
- sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/convexity_properties.pxd +16 -0
- sage/graphs/convexity_properties.pyx +870 -0
- sage/graphs/digraph.py +4754 -0
- sage/graphs/digraph_generators.py +1993 -0
- sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/distances_all_pairs.pxd +12 -0
- sage/graphs/distances_all_pairs.pyx +2938 -0
- sage/graphs/domination.py +1363 -0
- sage/graphs/dot2tex_utils.py +100 -0
- sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/edge_connectivity.pyx +1215 -0
- sage/graphs/generators/all.py +1 -0
- sage/graphs/generators/basic.py +1769 -0
- sage/graphs/generators/chessboard.py +538 -0
- sage/graphs/generators/classical_geometries.py +1611 -0
- sage/graphs/generators/degree_sequence.py +235 -0
- sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generators/distance_regular.pyx +2846 -0
- sage/graphs/generators/families.py +4759 -0
- sage/graphs/generators/intersection.py +565 -0
- sage/graphs/generators/platonic_solids.py +262 -0
- sage/graphs/generators/random.py +2623 -0
- sage/graphs/generators/smallgraphs.py +5741 -0
- sage/graphs/generators/world_map.py +724 -0
- sage/graphs/generic_graph.py +26867 -0
- sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/generic_graph_pyx.pxd +34 -0
- sage/graphs/generic_graph_pyx.pyx +1673 -0
- sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/genus.pyx +622 -0
- sage/graphs/graph.py +9645 -0
- sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_coloring.pyx +2284 -0
- sage/graphs/graph_database.py +1177 -0
- sage/graphs/graph_decompositions/all.py +1 -0
- sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
- sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
- sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +508 -0
- sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
- sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
- sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
- sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
- sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
- sage/graphs/graph_editor.py +82 -0
- sage/graphs/graph_generators.py +3314 -0
- sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +95 -0
- sage/graphs/graph_input.py +812 -0
- sage/graphs/graph_latex.py +2064 -0
- sage/graphs/graph_list.py +410 -0
- sage/graphs/graph_plot.py +1756 -0
- sage/graphs/graph_plot_js.py +338 -0
- sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/hyperbolicity.pyx +1704 -0
- sage/graphs/hypergraph_generators.py +364 -0
- sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/independent_sets.pxd +13 -0
- sage/graphs/independent_sets.pyx +402 -0
- sage/graphs/isgci.py +1033 -0
- sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +489 -0
- sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/line_graph.pyx +743 -0
- sage/graphs/lovasz_theta.py +77 -0
- sage/graphs/matching.py +1633 -0
- sage/graphs/matching_covered_graph.py +3590 -0
- sage/graphs/orientations.py +1489 -0
- sage/graphs/partial_cube.py +459 -0
- sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/path_enumeration.pyx +2040 -0
- sage/graphs/pq_trees.py +1129 -0
- sage/graphs/print_graphs.py +201 -0
- sage/graphs/schnyder.py +865 -0
- sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/spanning_tree.pyx +1457 -0
- sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/strongly_regular_db.pyx +3340 -0
- sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/traversals.pxd +9 -0
- sage/graphs/traversals.pyx +1872 -0
- sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/trees.pxd +15 -0
- sage/graphs/trees.pyx +310 -0
- sage/graphs/tutte_polynomial.py +713 -0
- sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/views.pyx +794 -0
- sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/graphs/weakly_chordal.pyx +604 -0
- sage/groups/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
- sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
- sage/knots/all.py +6 -0
- sage/knots/free_knotinfo_monoid.py +507 -0
- sage/knots/gauss_code.py +291 -0
- sage/knots/knot.py +682 -0
- sage/knots/knot_table.py +284 -0
- sage/knots/knotinfo.py +2900 -0
- sage/knots/link.py +4715 -0
- sage/sandpiles/all.py +13 -0
- sage/sandpiles/examples.py +225 -0
- sage/sandpiles/sandpile.py +6365 -0
- sage/topology/all.py +22 -0
- sage/topology/cell_complex.py +1214 -0
- sage/topology/cubical_complex.py +1976 -0
- sage/topology/delta_complex.py +1806 -0
- sage/topology/filtered_simplicial_complex.py +744 -0
- sage/topology/moment_angle_complex.py +823 -0
- sage/topology/simplicial_complex.py +5160 -0
- sage/topology/simplicial_complex_catalog.py +92 -0
- sage/topology/simplicial_complex_examples.py +1680 -0
- sage/topology/simplicial_complex_homset.py +205 -0
- sage/topology/simplicial_complex_morphism.py +836 -0
- sage/topology/simplicial_set.py +4102 -0
- sage/topology/simplicial_set_catalog.py +55 -0
- sage/topology/simplicial_set_constructions.py +2954 -0
- sage/topology/simplicial_set_examples.py +865 -0
- sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,2006 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-graphs
|
2
|
+
# sage.doctest: needs sage.graphs sage.modules
|
3
|
+
r"""
|
4
|
+
Common automata and transducers (finite state machines generators)
|
5
|
+
|
6
|
+
Automata and transducers in Sage can be built through the
|
7
|
+
:class:`automata <AutomatonGenerators>`
|
8
|
+
and :class:`transducers <TransducerGenerators>` objects, respectively.
|
9
|
+
It contains generators for
|
10
|
+
common finite state machines. For example,
|
11
|
+
|
12
|
+
::
|
13
|
+
|
14
|
+
sage: I = transducers.Identity([0, 1, 2])
|
15
|
+
|
16
|
+
generates an identity transducer on the alphabet `\{0, 1, 2\}`.
|
17
|
+
|
18
|
+
To construct automata and transducers manually, you can use the
|
19
|
+
classes :class:`Automaton` and :class:`Transducer`, respectively. See
|
20
|
+
:doc:`finite_state_machine` for more details and a lot
|
21
|
+
of :ref:`examples <finite_state_machine_examples>`.
|
22
|
+
|
23
|
+
**Automata**
|
24
|
+
|
25
|
+
.. csv-table::
|
26
|
+
:class: contentstable
|
27
|
+
:widths: 30, 70
|
28
|
+
:delim: |
|
29
|
+
|
30
|
+
:meth:`~AutomatonGenerators.AnyLetter` | Return an automaton recognizing any letter.
|
31
|
+
:meth:`~AutomatonGenerators.AnyWord` | Return an automaton recognizing any word.
|
32
|
+
:meth:`~AutomatonGenerators.EmptyWord` | Return an automaton recognizing the empty word.
|
33
|
+
:meth:`~AutomatonGenerators.Word` | Return an automaton recognizing the given word.
|
34
|
+
:meth:`~AutomatonGenerators.ContainsWord` | Return an automaton recognizing words containing the given word.
|
35
|
+
|
36
|
+
**Transducers**
|
37
|
+
|
38
|
+
.. csv-table::
|
39
|
+
:class: contentstable
|
40
|
+
:widths: 30, 70
|
41
|
+
:delim: |
|
42
|
+
|
43
|
+
:meth:`~TransducerGenerators.Identity` | Return a transducer realizing the identity map.
|
44
|
+
:meth:`~TransducerGenerators.abs` | Return a transducer realizing absolute value.
|
45
|
+
:meth:`~TransducerGenerators.map` | Return a transducer realizing a function.
|
46
|
+
:meth:`~TransducerGenerators.operator` | Return a transducer realizing a binary operation.
|
47
|
+
:meth:`~TransducerGenerators.all` | Return a transducer realizing logical ``and``.
|
48
|
+
:meth:`~TransducerGenerators.any` | Return a transducer realizing logical ``or``.
|
49
|
+
:meth:`~TransducerGenerators.add` | Return a transducer realizing addition.
|
50
|
+
:meth:`~TransducerGenerators.sub` | Return a transducer realizing subtraction.
|
51
|
+
:meth:`~TransducerGenerators.CountSubblockOccurrences` | Return a transducer counting the occurrences of a subblock.
|
52
|
+
:meth:`~TransducerGenerators.Wait` | Return a transducer writing ``False`` until first (or `k`-th) true input is read.
|
53
|
+
:meth:`~TransducerGenerators.weight` | Return a transducer realizing the Hamming weight.
|
54
|
+
:meth:`~TransducerGenerators.GrayCode` | Return a transducer realizing binary Gray code.
|
55
|
+
:meth:`~TransducerGenerators.Recursion` | Return a transducer defined by recursions.
|
56
|
+
|
57
|
+
AUTHORS:
|
58
|
+
|
59
|
+
- Clemens Heuberger (2014-04-07): initial version
|
60
|
+
- Sara Kropf (2014-04-10): some changes in TransducerGenerator
|
61
|
+
- Daniel Krenn (2014-04-15): improved common docstring during review
|
62
|
+
- Clemens Heuberger, Daniel Krenn, Sara Kropf (2014-04-16--2014-05-02):
|
63
|
+
A couple of improvements. Details see
|
64
|
+
:issue:`16141`, :issue:`16142`, :issue:`16143`, :issue:`16186`.
|
65
|
+
- Sara Kropf (2014-04-29): weight transducer
|
66
|
+
- Clemens Heuberger, Daniel Krenn (2014-07-18): transducers Wait, all,
|
67
|
+
any
|
68
|
+
- Clemens Heuberger (2014-08-10): transducer Recursion
|
69
|
+
- Clemens Heuberger (2015-07-31): automaton word
|
70
|
+
- Daniel Krenn (2015-09-14): cleanup :issue:`18227`
|
71
|
+
|
72
|
+
ACKNOWLEDGEMENT:
|
73
|
+
|
74
|
+
- Clemens Heuberger, Daniel Krenn and Sara Kropf are supported by the
|
75
|
+
Austrian Science Fund (FWF): P 24644-N26.
|
76
|
+
|
77
|
+
Functions and methods
|
78
|
+
---------------------
|
79
|
+
"""
|
80
|
+
# ****************************************************************************
|
81
|
+
# Copyright (C) 2014--2015 Clemens Heuberger <clemens.heuberger@aau.at>
|
82
|
+
# 2014--2015 Daniel Krenn <dev@danielkrenn.at>
|
83
|
+
# 2014 Sara Kropf <sara.kropf@aau.at>
|
84
|
+
#
|
85
|
+
# This program is free software: you can redistribute it and/or modify
|
86
|
+
# it under the terms of the GNU General Public License as published by
|
87
|
+
# the Free Software Foundation, either version 2 of the License, or
|
88
|
+
# (at your option) any later version.
|
89
|
+
# https://www.gnu.org/licenses/
|
90
|
+
# ****************************************************************************
|
91
|
+
|
92
|
+
from collections import namedtuple
|
93
|
+
import operator
|
94
|
+
|
95
|
+
from sage.combinat.finite_state_machine import Automaton, Transducer
|
96
|
+
from sage.rings.integer_ring import ZZ
|
97
|
+
from sage.rings.rational_field import QQ
|
98
|
+
|
99
|
+
|
100
|
+
class AutomatonGenerators:
|
101
|
+
r"""
|
102
|
+
A collection of constructors for several common automata.
|
103
|
+
|
104
|
+
A list of all automata in this database is available via tab
|
105
|
+
completion. Type "``automata.``" and then hit tab to see which
|
106
|
+
automata are available.
|
107
|
+
|
108
|
+
The automata currently in this class include:
|
109
|
+
|
110
|
+
- :meth:`~AnyLetter`
|
111
|
+
- :meth:`~AnyWord`
|
112
|
+
- :meth:`~EmptyWord`
|
113
|
+
- :meth:`~Word`
|
114
|
+
- :meth:`~ContainsWord`
|
115
|
+
"""
|
116
|
+
|
117
|
+
def AnyLetter(self, input_alphabet):
|
118
|
+
r"""
|
119
|
+
Return an automaton recognizing any letter of the given
|
120
|
+
input alphabet.
|
121
|
+
|
122
|
+
INPUT:
|
123
|
+
|
124
|
+
- ``input_alphabet`` -- list; the input alphabet
|
125
|
+
|
126
|
+
OUTPUT: an :class:`~Automaton`
|
127
|
+
|
128
|
+
EXAMPLES::
|
129
|
+
|
130
|
+
sage: A = automata.AnyLetter([0, 1])
|
131
|
+
sage: A([])
|
132
|
+
False
|
133
|
+
sage: A([0])
|
134
|
+
True
|
135
|
+
sage: A([1])
|
136
|
+
True
|
137
|
+
sage: A([0, 0])
|
138
|
+
False
|
139
|
+
|
140
|
+
.. SEEALSO::
|
141
|
+
|
142
|
+
:meth:`AnyWord`
|
143
|
+
"""
|
144
|
+
z = ZZ(0)
|
145
|
+
o = ZZ(1)
|
146
|
+
return Automaton([(z, o, _) for _ in input_alphabet],
|
147
|
+
initial_states=[z],
|
148
|
+
final_states=[o])
|
149
|
+
|
150
|
+
def AnyWord(self, input_alphabet):
|
151
|
+
r"""
|
152
|
+
Return an automaton recognizing any word of the given
|
153
|
+
input alphabet.
|
154
|
+
|
155
|
+
INPUT:
|
156
|
+
|
157
|
+
- ``input_alphabet`` -- list; the input alphabet
|
158
|
+
|
159
|
+
OUTPUT: an :class:`~Automaton`
|
160
|
+
|
161
|
+
EXAMPLES::
|
162
|
+
|
163
|
+
sage: A = automata.AnyWord([0, 1])
|
164
|
+
sage: A([0])
|
165
|
+
True
|
166
|
+
sage: A([1])
|
167
|
+
True
|
168
|
+
sage: A([0, 1])
|
169
|
+
True
|
170
|
+
sage: A([0, 2])
|
171
|
+
False
|
172
|
+
|
173
|
+
This is equivalent to taking the :meth:`~FiniteStateMachine.kleene_star`
|
174
|
+
of :meth:`AnyLetter` and minimizing the result. This method
|
175
|
+
immediately gives a minimized version::
|
176
|
+
|
177
|
+
sage: B = automata.AnyLetter([0, 1]).kleene_star().minimization().relabeled()
|
178
|
+
sage: B == A
|
179
|
+
True
|
180
|
+
|
181
|
+
.. SEEALSO::
|
182
|
+
|
183
|
+
:meth:`AnyLetter`,
|
184
|
+
:meth:`Word`.
|
185
|
+
"""
|
186
|
+
z = ZZ(0)
|
187
|
+
return Automaton([(z, z, _) for _ in input_alphabet],
|
188
|
+
initial_states=[z],
|
189
|
+
final_states=[z])
|
190
|
+
|
191
|
+
def EmptyWord(self, input_alphabet=None):
|
192
|
+
r"""
|
193
|
+
Return an automaton recognizing the empty word.
|
194
|
+
|
195
|
+
INPUT:
|
196
|
+
|
197
|
+
- ``input_alphabet`` -- iterable or ``None`` (default: ``None``)
|
198
|
+
|
199
|
+
OUTPUT: an :class:`~Automaton`
|
200
|
+
|
201
|
+
EXAMPLES::
|
202
|
+
|
203
|
+
sage: A = automata.EmptyWord()
|
204
|
+
sage: A([])
|
205
|
+
True
|
206
|
+
sage: A([0])
|
207
|
+
False
|
208
|
+
|
209
|
+
.. SEEALSO::
|
210
|
+
|
211
|
+
:meth:`AnyLetter`,
|
212
|
+
:meth:`AnyWord`.
|
213
|
+
"""
|
214
|
+
z = ZZ(0)
|
215
|
+
return Automaton(initial_states=[z],
|
216
|
+
final_states=[z],
|
217
|
+
input_alphabet=input_alphabet)
|
218
|
+
|
219
|
+
def Word(self, word, input_alphabet=None):
|
220
|
+
r"""
|
221
|
+
Return an automaton recognizing the given word.
|
222
|
+
|
223
|
+
INPUT:
|
224
|
+
|
225
|
+
- ``word`` -- an iterable
|
226
|
+
|
227
|
+
- ``input_alphabet`` -- list or ``None``; if ``None``,
|
228
|
+
then the letters occurring in the word are used
|
229
|
+
|
230
|
+
OUTPUT: an :class:`~Automaton`
|
231
|
+
|
232
|
+
EXAMPLES::
|
233
|
+
|
234
|
+
sage: A = automata.Word([0])
|
235
|
+
sage: A.transitions()
|
236
|
+
[Transition from 0 to 1: 0|-]
|
237
|
+
sage: [A(w) for w in ([], [0], [1])]
|
238
|
+
[False, True, False]
|
239
|
+
sage: A = automata.Word([0, 1, 0])
|
240
|
+
sage: A.transitions()
|
241
|
+
[Transition from 0 to 1: 0|-,
|
242
|
+
Transition from 1 to 2: 1|-,
|
243
|
+
Transition from 2 to 3: 0|-]
|
244
|
+
sage: [A(w) for w in ([], [0], [0, 1], [0, 1, 1], [0, 1, 0])]
|
245
|
+
[False, False, False, False, True]
|
246
|
+
|
247
|
+
If the input alphabet is not given, it is derived from the given
|
248
|
+
word. ::
|
249
|
+
|
250
|
+
sage: A.input_alphabet
|
251
|
+
[0, 1]
|
252
|
+
sage: A = automata.Word([0, 1, 0], input_alphabet=[0, 1, 2])
|
253
|
+
sage: A.input_alphabet
|
254
|
+
[0, 1, 2]
|
255
|
+
|
256
|
+
.. SEEALSO::
|
257
|
+
|
258
|
+
:meth:`AnyWord`,
|
259
|
+
:meth:`ContainsWord`.
|
260
|
+
|
261
|
+
TESTS::
|
262
|
+
|
263
|
+
sage: from sage.rings.integer import Integer
|
264
|
+
sage: all(isinstance(s.label(), Integer) for s in A.states())
|
265
|
+
True
|
266
|
+
"""
|
267
|
+
letters = list(word)
|
268
|
+
length = len(letters)
|
269
|
+
from sage.rings.integer_ring import ZZ
|
270
|
+
return Automaton([(ZZ(i), ZZ(i + 1), letter)
|
271
|
+
for i, letter in enumerate(letters)],
|
272
|
+
initial_states=[ZZ(0)],
|
273
|
+
final_states=[ZZ(length)],
|
274
|
+
input_alphabet=input_alphabet)
|
275
|
+
|
276
|
+
def ContainsWord(self, word, input_alphabet):
|
277
|
+
r"""
|
278
|
+
Return an automaton recognizing the words containing
|
279
|
+
the given word as a factor.
|
280
|
+
|
281
|
+
INPUT:
|
282
|
+
|
283
|
+
- ``word`` -- list (or other iterable) of letters; the
|
284
|
+
word we are looking for
|
285
|
+
|
286
|
+
- ``input_alphabet`` -- list or other iterable; the input
|
287
|
+
alphabet
|
288
|
+
|
289
|
+
OUTPUT: an :class:`~Automaton`
|
290
|
+
|
291
|
+
EXAMPLES::
|
292
|
+
|
293
|
+
sage: A = automata.ContainsWord([0, 1, 0, 1, 1],
|
294
|
+
....: input_alphabet=[0, 1])
|
295
|
+
sage: A([1, 0, 1, 0, 1, 0, 1, 1, 0, 0])
|
296
|
+
True
|
297
|
+
sage: A([1, 0, 1, 0, 1, 0, 1, 0])
|
298
|
+
False
|
299
|
+
|
300
|
+
This is equivalent to taking the concatenation of :meth:`AnyWord`,
|
301
|
+
:meth:`Word` and :meth:`AnyWord` and minimizing the result. This
|
302
|
+
method immediately gives a minimized version::
|
303
|
+
|
304
|
+
sage: B = (automata.AnyWord([0, 1]) *
|
305
|
+
....: automata.Word([0, 1, 0, 1, 1], [0, 1]) *
|
306
|
+
....: automata.AnyWord([0, 1])).minimization()
|
307
|
+
sage: B.is_equivalent(A)
|
308
|
+
True
|
309
|
+
|
310
|
+
.. SEEALSO::
|
311
|
+
|
312
|
+
:meth:`~TransducerGenerators.CountSubblockOccurrences`,
|
313
|
+
:meth:`AnyWord`,
|
314
|
+
:meth:`Word`.
|
315
|
+
"""
|
316
|
+
word = tuple(word)
|
317
|
+
|
318
|
+
def starts_with(what, pattern):
|
319
|
+
return len(what) >= len(pattern) \
|
320
|
+
and what[:len(pattern)] == pattern
|
321
|
+
|
322
|
+
def transition_function(read, input):
|
323
|
+
if read == word:
|
324
|
+
return (word, None)
|
325
|
+
current = read + (input,)
|
326
|
+
k = 0
|
327
|
+
while not starts_with(word, current[k:]):
|
328
|
+
k += 1
|
329
|
+
return (current[k:], None)
|
330
|
+
|
331
|
+
return Automaton(
|
332
|
+
transition_function,
|
333
|
+
input_alphabet=input_alphabet,
|
334
|
+
initial_states=[()],
|
335
|
+
final_states=[word])
|
336
|
+
|
337
|
+
|
338
|
+
class TransducerGenerators:
|
339
|
+
r"""
|
340
|
+
A collection of constructors for several common transducers.
|
341
|
+
|
342
|
+
A list of all transducers in this database is available via tab
|
343
|
+
completion. Type "``transducers.``" and then hit tab to see which
|
344
|
+
transducers are available.
|
345
|
+
|
346
|
+
The transducers currently in this class include:
|
347
|
+
|
348
|
+
- :meth:`~Identity`
|
349
|
+
- :meth:`~abs`
|
350
|
+
- :meth:`~TransducerGenerators.operator`
|
351
|
+
- :meth:`~all`
|
352
|
+
- :meth:`~any`
|
353
|
+
- :meth:`~add`
|
354
|
+
- :meth:`~sub`
|
355
|
+
- :meth:`~CountSubblockOccurrences`
|
356
|
+
- :meth:`~Wait`
|
357
|
+
- :meth:`~GrayCode`
|
358
|
+
- :meth:`~Recursion`
|
359
|
+
"""
|
360
|
+
|
361
|
+
def Identity(self, input_alphabet):
|
362
|
+
"""
|
363
|
+
Return the identity transducer realizing the identity map.
|
364
|
+
|
365
|
+
INPUT:
|
366
|
+
|
367
|
+
- ``input_alphabet`` -- list or other iterable
|
368
|
+
|
369
|
+
OUTPUT: a transducer mapping each word over ``input_alphabet`` to
|
370
|
+
itself
|
371
|
+
|
372
|
+
EXAMPLES::
|
373
|
+
|
374
|
+
sage: T = transducers.Identity([0, 1])
|
375
|
+
sage: sorted(T.transitions())
|
376
|
+
[Transition from 0 to 0: 0|0,
|
377
|
+
Transition from 0 to 0: 1|1]
|
378
|
+
sage: T.initial_states()
|
379
|
+
[0]
|
380
|
+
sage: T.final_states()
|
381
|
+
[0]
|
382
|
+
sage: T.input_alphabet
|
383
|
+
[0, 1]
|
384
|
+
sage: T.output_alphabet
|
385
|
+
[0, 1]
|
386
|
+
sage: T([0, 1, 0, 1, 1])
|
387
|
+
[0, 1, 0, 1, 1]
|
388
|
+
"""
|
389
|
+
return Transducer(
|
390
|
+
[(0, 0, d, d) for d in input_alphabet],
|
391
|
+
input_alphabet=input_alphabet,
|
392
|
+
output_alphabet=input_alphabet,
|
393
|
+
initial_states=[0],
|
394
|
+
final_states=[0])
|
395
|
+
|
396
|
+
def CountSubblockOccurrences(self, block, input_alphabet):
|
397
|
+
r"""
|
398
|
+
Return a transducer counting the number of (possibly
|
399
|
+
overlapping) occurrences of a block in the input.
|
400
|
+
|
401
|
+
INPUT:
|
402
|
+
|
403
|
+
- ``block`` -- list (or other iterable) of letters
|
404
|
+
|
405
|
+
- ``input_alphabet`` -- list or other iterable
|
406
|
+
|
407
|
+
OUTPUT:
|
408
|
+
|
409
|
+
A transducer counting (in unary) the number of occurrences of the given
|
410
|
+
block in the input. Overlapping occurrences are counted several
|
411
|
+
times.
|
412
|
+
|
413
|
+
Denoting the block by `b_0\ldots b_{k-1}`, the input word by
|
414
|
+
`i_0\ldots i_L` and the output word by `o_0\ldots o_L`, we
|
415
|
+
have `o_j = 1` if and only if `i_{j-k+1}\ldots i_{j} = b_0\ldots
|
416
|
+
b_{k-1}`. Otherwise, `o_j = 0`.
|
417
|
+
|
418
|
+
EXAMPLES:
|
419
|
+
|
420
|
+
#. Counting the number of ``10`` blocks over the alphabet
|
421
|
+
``[0, 1]``::
|
422
|
+
|
423
|
+
sage: T = transducers.CountSubblockOccurrences(
|
424
|
+
....: [1, 0],
|
425
|
+
....: [0, 1])
|
426
|
+
sage: sorted(T.transitions())
|
427
|
+
[Transition from () to (): 0|0,
|
428
|
+
Transition from () to (1,): 1|0,
|
429
|
+
Transition from (1,) to (): 0|1,
|
430
|
+
Transition from (1,) to (1,): 1|0]
|
431
|
+
sage: T.input_alphabet
|
432
|
+
[0, 1]
|
433
|
+
sage: T.output_alphabet
|
434
|
+
[0, 1]
|
435
|
+
sage: T.initial_states()
|
436
|
+
[()]
|
437
|
+
sage: T.final_states()
|
438
|
+
[(), (1,)]
|
439
|
+
|
440
|
+
Check some sequence::
|
441
|
+
|
442
|
+
sage: T([0, 1, 0, 1, 1, 0])
|
443
|
+
[0, 0, 1, 0, 0, 1]
|
444
|
+
|
445
|
+
#. Counting the number of ``11`` blocks over the alphabet
|
446
|
+
``[0, 1]``::
|
447
|
+
|
448
|
+
sage: T = transducers.CountSubblockOccurrences(
|
449
|
+
....: [1, 1],
|
450
|
+
....: [0, 1])
|
451
|
+
sage: sorted(T.transitions())
|
452
|
+
[Transition from () to (): 0|0,
|
453
|
+
Transition from () to (1,): 1|0,
|
454
|
+
Transition from (1,) to (): 0|0,
|
455
|
+
Transition from (1,) to (1,): 1|1]
|
456
|
+
|
457
|
+
Check some sequence::
|
458
|
+
|
459
|
+
sage: T([0, 1, 0, 1, 1, 0])
|
460
|
+
[0, 0, 0, 0, 1, 0]
|
461
|
+
|
462
|
+
#. Counting the number of ``1010`` blocks over the
|
463
|
+
alphabet ``[0, 1, 2]``::
|
464
|
+
|
465
|
+
sage: T = transducers.CountSubblockOccurrences(
|
466
|
+
....: [1, 0, 1, 0],
|
467
|
+
....: [0, 1, 2])
|
468
|
+
sage: sorted(T.transitions())
|
469
|
+
[Transition from () to (): 0|0,
|
470
|
+
Transition from () to (1,): 1|0,
|
471
|
+
Transition from () to (): 2|0,
|
472
|
+
Transition from (1,) to (1, 0): 0|0,
|
473
|
+
Transition from (1,) to (1,): 1|0,
|
474
|
+
Transition from (1,) to (): 2|0,
|
475
|
+
Transition from (1, 0) to (): 0|0,
|
476
|
+
Transition from (1, 0) to (1, 0, 1): 1|0,
|
477
|
+
Transition from (1, 0) to (): 2|0,
|
478
|
+
Transition from (1, 0, 1) to (1, 0): 0|1,
|
479
|
+
Transition from (1, 0, 1) to (1,): 1|0,
|
480
|
+
Transition from (1, 0, 1) to (): 2|0]
|
481
|
+
sage: input = [0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 2]
|
482
|
+
sage: output = [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0]
|
483
|
+
sage: T(input) == output
|
484
|
+
True
|
485
|
+
|
486
|
+
.. SEEALSO::
|
487
|
+
|
488
|
+
:meth:`~AutomatonGenerators.ContainsWord`
|
489
|
+
"""
|
490
|
+
block_as_tuple = tuple(block)
|
491
|
+
|
492
|
+
def starts_with(what, pattern):
|
493
|
+
return len(what) >= len(pattern) \
|
494
|
+
and what[:len(pattern)] == pattern
|
495
|
+
|
496
|
+
def transition_function(read, input):
|
497
|
+
current = read + (input, )
|
498
|
+
if starts_with(block_as_tuple, current) \
|
499
|
+
and len(block_as_tuple) > len(current):
|
500
|
+
return (current, 0)
|
501
|
+
else:
|
502
|
+
k = 1
|
503
|
+
while not starts_with(block_as_tuple, current[k:]):
|
504
|
+
k += 1
|
505
|
+
return (current[k:], int(block_as_tuple == current))
|
506
|
+
|
507
|
+
T = Transducer(
|
508
|
+
transition_function,
|
509
|
+
input_alphabet=input_alphabet,
|
510
|
+
output_alphabet=[0, 1],
|
511
|
+
initial_states=[()])
|
512
|
+
for s in T.iter_states():
|
513
|
+
s.is_final = True
|
514
|
+
return T
|
515
|
+
|
516
|
+
def Wait(self, input_alphabet, threshold=1):
|
517
|
+
r"""
|
518
|
+
Writes ``False`` until reading the ``threshold``-th occurrence
|
519
|
+
of a true input letter; then writes ``True``.
|
520
|
+
|
521
|
+
INPUT:
|
522
|
+
|
523
|
+
- ``input_alphabet`` -- list or other iterable
|
524
|
+
|
525
|
+
- ``threshold`` -- positive integer specifying how many
|
526
|
+
occurrences of ``True`` inputs are waited for
|
527
|
+
|
528
|
+
OUTPUT:
|
529
|
+
|
530
|
+
A transducer writing ``False`` until the ``threshold``-th true
|
531
|
+
(Python's standard conversion to boolean is used to convert the
|
532
|
+
actual input to boolean) input is read. Subsequently, the
|
533
|
+
transducer writes ``True``.
|
534
|
+
|
535
|
+
EXAMPLES::
|
536
|
+
|
537
|
+
sage: T = transducers.Wait([0, 1])
|
538
|
+
sage: T([0, 0, 1, 0, 1, 0])
|
539
|
+
[False, False, True, True, True, True]
|
540
|
+
sage: T2 = transducers.Wait([0, 1], threshold=2)
|
541
|
+
sage: T2([0, 0, 1, 0, 1, 0])
|
542
|
+
[False, False, False, False, True, True]
|
543
|
+
"""
|
544
|
+
def transition(state, input):
|
545
|
+
if state == threshold:
|
546
|
+
return (threshold, True)
|
547
|
+
if not input:
|
548
|
+
return (state, False)
|
549
|
+
return (state + 1, state + 1 == threshold)
|
550
|
+
|
551
|
+
T = Transducer(transition,
|
552
|
+
input_alphabet=input_alphabet,
|
553
|
+
initial_states=[0])
|
554
|
+
for s in T.iter_states():
|
555
|
+
s.is_final = True
|
556
|
+
|
557
|
+
return T
|
558
|
+
|
559
|
+
def map(self, f, input_alphabet):
|
560
|
+
r"""
|
561
|
+
Return a transducer which realizes a function
|
562
|
+
on the alphabet.
|
563
|
+
|
564
|
+
INPUT:
|
565
|
+
|
566
|
+
- ``f`` -- function to realize
|
567
|
+
|
568
|
+
- ``input_alphabet`` -- list or other iterable
|
569
|
+
|
570
|
+
OUTPUT:
|
571
|
+
|
572
|
+
A transducer mapping an input letter `x` to
|
573
|
+
`f(x)`.
|
574
|
+
|
575
|
+
EXAMPLES:
|
576
|
+
|
577
|
+
The following binary transducer realizes component-wise
|
578
|
+
absolute value (this transducer is also available as :meth:`.abs`)::
|
579
|
+
|
580
|
+
sage: T = transducers.map(abs, [-1, 0, 1])
|
581
|
+
sage: T.transitions()
|
582
|
+
[Transition from 0 to 0: -1|1,
|
583
|
+
Transition from 0 to 0: 0|0,
|
584
|
+
Transition from 0 to 0: 1|1]
|
585
|
+
sage: T.input_alphabet
|
586
|
+
[-1, 0, 1]
|
587
|
+
sage: T.initial_states()
|
588
|
+
[0]
|
589
|
+
sage: T.final_states()
|
590
|
+
[0]
|
591
|
+
sage: T([-1, 1, 0, 1])
|
592
|
+
[1, 1, 0, 1]
|
593
|
+
|
594
|
+
.. SEEALSO::
|
595
|
+
|
596
|
+
:meth:`Automaton.with_output()
|
597
|
+
<sage.combinat.finite_state_machine.Automaton.with_output>`.
|
598
|
+
"""
|
599
|
+
return Transducer(lambda state, input: (0, f(input)),
|
600
|
+
input_alphabet=input_alphabet,
|
601
|
+
initial_states=[0],
|
602
|
+
final_states=[0])
|
603
|
+
|
604
|
+
def operator(self, operator, input_alphabet, number_of_operands=2):
|
605
|
+
r"""
|
606
|
+
Return a transducer which realizes an operation
|
607
|
+
on tuples over the given input alphabet.
|
608
|
+
|
609
|
+
INPUT:
|
610
|
+
|
611
|
+
- ``operator`` -- operator to realize; it is a function which
|
612
|
+
takes ``number_of_operands`` input arguments (each out of
|
613
|
+
``input_alphabet``)
|
614
|
+
|
615
|
+
- ``input_alphabet`` -- list or other iterable
|
616
|
+
|
617
|
+
- ``number_of_operands`` -- (default: `2`) it specifies the number
|
618
|
+
of input arguments the operator takes
|
619
|
+
|
620
|
+
OUTPUT:
|
621
|
+
|
622
|
+
A transducer mapping an input letter `(i_1, \dots, i_n)` to
|
623
|
+
`\mathrm{operator}(i_1, \dots, i_n)`. Here, `n` equals
|
624
|
+
``number_of_operands``.
|
625
|
+
|
626
|
+
The input alphabet of the generated transducer is the Cartesian
|
627
|
+
product of ``number_of_operands`` copies of ``input_alphabet``.
|
628
|
+
|
629
|
+
EXAMPLES:
|
630
|
+
|
631
|
+
The following binary transducer realizes component-wise
|
632
|
+
addition (this transducer is also available as :meth:`add`)::
|
633
|
+
|
634
|
+
sage: import operator
|
635
|
+
sage: T = transducers.operator(operator.add, [0, 1])
|
636
|
+
sage: T.transitions()
|
637
|
+
[Transition from 0 to 0: (0, 0)|0,
|
638
|
+
Transition from 0 to 0: (0, 1)|1,
|
639
|
+
Transition from 0 to 0: (1, 0)|1,
|
640
|
+
Transition from 0 to 0: (1, 1)|2]
|
641
|
+
sage: T.input_alphabet
|
642
|
+
[(0, 0), (0, 1), (1, 0), (1, 1)]
|
643
|
+
sage: T.initial_states()
|
644
|
+
[0]
|
645
|
+
sage: T.final_states()
|
646
|
+
[0]
|
647
|
+
sage: T([(0, 0), (0, 1), (1, 0), (1, 1)])
|
648
|
+
[0, 1, 1, 2]
|
649
|
+
|
650
|
+
Note that for a unary operator the input letters of the
|
651
|
+
new transducer are tuples of length `1`::
|
652
|
+
|
653
|
+
sage: T = transducers.operator(abs,
|
654
|
+
....: [-1, 0, 1],
|
655
|
+
....: number_of_operands=1)
|
656
|
+
sage: T([-1, 1, 0])
|
657
|
+
Traceback (most recent call last):
|
658
|
+
...
|
659
|
+
ValueError: Invalid input sequence.
|
660
|
+
sage: T([(-1,), (1,), (0,)])
|
661
|
+
[1, 1, 0]
|
662
|
+
|
663
|
+
Compare this with the transducer generated by :meth:`.map`::
|
664
|
+
|
665
|
+
sage: T = transducers.map(abs,
|
666
|
+
....: [-1, 0, 1])
|
667
|
+
sage: T([-1, 1, 0])
|
668
|
+
[1, 1, 0]
|
669
|
+
|
670
|
+
In fact, this transducer is also available as :meth:`.abs`::
|
671
|
+
|
672
|
+
sage: T = transducers.abs([-1, 0, 1])
|
673
|
+
sage: T([-1, 1, 0])
|
674
|
+
[1, 1, 0]
|
675
|
+
"""
|
676
|
+
from itertools import product
|
677
|
+
|
678
|
+
def transition_function(state, operands):
|
679
|
+
return (0, operator(*operands))
|
680
|
+
pairs = list(product(input_alphabet, repeat=number_of_operands))
|
681
|
+
return Transducer(transition_function,
|
682
|
+
input_alphabet=pairs,
|
683
|
+
initial_states=[0],
|
684
|
+
final_states=[0])
|
685
|
+
|
686
|
+
def all(self, input_alphabet, number_of_operands=2):
|
687
|
+
r"""
|
688
|
+
Return a transducer which realizes logical ``and`` over the given
|
689
|
+
input alphabet.
|
690
|
+
|
691
|
+
INPUT:
|
692
|
+
|
693
|
+
- ``input_alphabet`` -- list or other iterable
|
694
|
+
|
695
|
+
- ``number_of_operands`` -- (default: `2`) specifies the number
|
696
|
+
of input arguments for the ``and`` operation
|
697
|
+
|
698
|
+
OUTPUT:
|
699
|
+
|
700
|
+
A transducer mapping an input word
|
701
|
+
`(i_{01}, \ldots, i_{0d})\ldots (i_{k1}, \ldots, i_{kd})` to the word
|
702
|
+
`(i_{01} \land \cdots \land i_{0d})\ldots (i_{k1} \land \cdots \land i_{kd})`.
|
703
|
+
|
704
|
+
The input alphabet of the generated transducer is the Cartesian
|
705
|
+
product of ``number_of_operands`` copies of ``input_alphabet``.
|
706
|
+
|
707
|
+
EXAMPLES:
|
708
|
+
|
709
|
+
The following transducer realizes letter-wise
|
710
|
+
logical ``and``::
|
711
|
+
|
712
|
+
sage: T = transducers.all([False, True])
|
713
|
+
sage: T.transitions()
|
714
|
+
[Transition from 0 to 0: (False, False)|False,
|
715
|
+
Transition from 0 to 0: (False, True)|False,
|
716
|
+
Transition from 0 to 0: (True, False)|False,
|
717
|
+
Transition from 0 to 0: (True, True)|True]
|
718
|
+
sage: T.input_alphabet
|
719
|
+
[(False, False), (False, True), (True, False), (True, True)]
|
720
|
+
sage: T.initial_states()
|
721
|
+
[0]
|
722
|
+
sage: T.final_states()
|
723
|
+
[0]
|
724
|
+
sage: T([(False, False), (False, True), (True, False), (True, True)])
|
725
|
+
[False, False, False, True]
|
726
|
+
|
727
|
+
More than two operands and other input alphabets (with
|
728
|
+
conversion to boolean) are also possible::
|
729
|
+
|
730
|
+
sage: T3 = transducers.all([0, 1], number_of_operands=3)
|
731
|
+
sage: T3([(0, 0, 0), (1, 0, 0), (1, 1, 1)])
|
732
|
+
[False, False, True]
|
733
|
+
"""
|
734
|
+
return self.operator(lambda *args: all(args),
|
735
|
+
input_alphabet, number_of_operands)
|
736
|
+
|
737
|
+
def any(self, input_alphabet, number_of_operands=2):
|
738
|
+
r"""
|
739
|
+
Return a transducer which realizes logical ``or`` over the given
|
740
|
+
input alphabet.
|
741
|
+
|
742
|
+
INPUT:
|
743
|
+
|
744
|
+
- ``input_alphabet`` -- list or other iterable
|
745
|
+
|
746
|
+
- ``number_of_operands`` -- (default: `2`) specifies the number
|
747
|
+
of input arguments for the ``or`` operation
|
748
|
+
|
749
|
+
OUTPUT:
|
750
|
+
|
751
|
+
A transducer mapping an input word
|
752
|
+
`(i_{01}, \ldots, i_{0d})\ldots (i_{k1}, \ldots, i_{kd})` to the word
|
753
|
+
`(i_{01} \lor \cdots \lor i_{0d})\ldots (i_{k1} \lor \cdots \lor i_{kd})`.
|
754
|
+
|
755
|
+
The input alphabet of the generated transducer is the Cartesian
|
756
|
+
product of ``number_of_operands`` copies of ``input_alphabet``.
|
757
|
+
|
758
|
+
EXAMPLES:
|
759
|
+
|
760
|
+
The following transducer realizes letter-wise
|
761
|
+
logical ``or``::
|
762
|
+
|
763
|
+
sage: T = transducers.any([False, True])
|
764
|
+
sage: T.transitions()
|
765
|
+
[Transition from 0 to 0: (False, False)|False,
|
766
|
+
Transition from 0 to 0: (False, True)|True,
|
767
|
+
Transition from 0 to 0: (True, False)|True,
|
768
|
+
Transition from 0 to 0: (True, True)|True]
|
769
|
+
sage: T.input_alphabet
|
770
|
+
[(False, False), (False, True), (True, False), (True, True)]
|
771
|
+
sage: T.initial_states()
|
772
|
+
[0]
|
773
|
+
sage: T.final_states()
|
774
|
+
[0]
|
775
|
+
sage: T([(False, False), (False, True), (True, False), (True, True)])
|
776
|
+
[False, True, True, True]
|
777
|
+
|
778
|
+
More than two operands and other input alphabets (with
|
779
|
+
conversion to boolean) are also possible::
|
780
|
+
|
781
|
+
sage: T3 = transducers.any([0, 1], number_of_operands=3)
|
782
|
+
sage: T3([(0, 0, 0), (1, 0, 0), (1, 1, 1)])
|
783
|
+
[False, True, True]
|
784
|
+
"""
|
785
|
+
return self.operator(lambda *args: any(args),
|
786
|
+
input_alphabet, number_of_operands)
|
787
|
+
|
788
|
+
def add(self, input_alphabet, number_of_operands=2):
|
789
|
+
r"""
|
790
|
+
Return a transducer which realizes addition on pairs over the
|
791
|
+
given input alphabet.
|
792
|
+
|
793
|
+
INPUT:
|
794
|
+
|
795
|
+
- ``input_alphabet`` -- list or other iterable
|
796
|
+
|
797
|
+
- ``number_of_operands`` -- (default: `2`) it specifies the number
|
798
|
+
of input arguments the operator takes
|
799
|
+
|
800
|
+
OUTPUT:
|
801
|
+
|
802
|
+
A transducer mapping an input word
|
803
|
+
`(i_{01}, \ldots, i_{0d})\ldots (i_{k1}, \ldots, i_{kd})` to the word
|
804
|
+
`(i_{01} + \cdots + i_{0d})\ldots (i_{k1} + \cdots + i_{kd})`.
|
805
|
+
|
806
|
+
The input alphabet of the generated transducer is the Cartesian
|
807
|
+
product of ``number_of_operands`` copies of ``input_alphabet``.
|
808
|
+
|
809
|
+
EXAMPLES:
|
810
|
+
|
811
|
+
The following transducer realizes letter-wise
|
812
|
+
addition::
|
813
|
+
|
814
|
+
sage: T = transducers.add([0, 1])
|
815
|
+
sage: T.transitions()
|
816
|
+
[Transition from 0 to 0: (0, 0)|0,
|
817
|
+
Transition from 0 to 0: (0, 1)|1,
|
818
|
+
Transition from 0 to 0: (1, 0)|1,
|
819
|
+
Transition from 0 to 0: (1, 1)|2]
|
820
|
+
sage: T.input_alphabet
|
821
|
+
[(0, 0), (0, 1), (1, 0), (1, 1)]
|
822
|
+
sage: T.initial_states()
|
823
|
+
[0]
|
824
|
+
sage: T.final_states()
|
825
|
+
[0]
|
826
|
+
sage: T([(0, 0), (0, 1), (1, 0), (1, 1)])
|
827
|
+
[0, 1, 1, 2]
|
828
|
+
|
829
|
+
More than two operands can also be handled::
|
830
|
+
|
831
|
+
sage: T3 = transducers.add([0, 1], number_of_operands=3)
|
832
|
+
sage: T3.input_alphabet
|
833
|
+
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1),
|
834
|
+
(1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
|
835
|
+
sage: T3([(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)])
|
836
|
+
[0, 1, 2, 3]
|
837
|
+
"""
|
838
|
+
return self.operator(lambda *args: sum(args),
|
839
|
+
input_alphabet,
|
840
|
+
number_of_operands=number_of_operands)
|
841
|
+
|
842
|
+
def sub(self, input_alphabet):
|
843
|
+
r"""
|
844
|
+
Return a transducer which realizes subtraction on pairs over
|
845
|
+
the given input alphabet.
|
846
|
+
|
847
|
+
INPUT:
|
848
|
+
|
849
|
+
- ``input_alphabet`` -- list or other iterable
|
850
|
+
|
851
|
+
OUTPUT:
|
852
|
+
|
853
|
+
A transducer mapping an input word `(i_0, i'_0)\ldots (i_k, i'_k)`
|
854
|
+
to the word `(i_0 - i'_0)\ldots (i_k - i'_k)`.
|
855
|
+
|
856
|
+
The input alphabet of the generated transducer is the Cartesian
|
857
|
+
product of two copies of ``input_alphabet``.
|
858
|
+
|
859
|
+
EXAMPLES:
|
860
|
+
|
861
|
+
The following transducer realizes letter-wise
|
862
|
+
subtraction::
|
863
|
+
|
864
|
+
sage: T = transducers.sub([0, 1])
|
865
|
+
sage: T.transitions()
|
866
|
+
[Transition from 0 to 0: (0, 0)|0,
|
867
|
+
Transition from 0 to 0: (0, 1)|-1,
|
868
|
+
Transition from 0 to 0: (1, 0)|1,
|
869
|
+
Transition from 0 to 0: (1, 1)|0]
|
870
|
+
sage: T.input_alphabet
|
871
|
+
[(0, 0), (0, 1), (1, 0), (1, 1)]
|
872
|
+
sage: T.initial_states()
|
873
|
+
[0]
|
874
|
+
sage: T.final_states()
|
875
|
+
[0]
|
876
|
+
sage: T([(0, 0), (0, 1), (1, 0), (1, 1)])
|
877
|
+
[0, -1, 1, 0]
|
878
|
+
"""
|
879
|
+
return self.operator(operator.sub, input_alphabet)
|
880
|
+
|
881
|
+
def weight(self, input_alphabet, zero=0):
|
882
|
+
r"""
|
883
|
+
Return a transducer which realizes the Hamming weight of the input
|
884
|
+
over the given input alphabet.
|
885
|
+
|
886
|
+
INPUT:
|
887
|
+
|
888
|
+
- ``input_alphabet`` -- list or other iterable
|
889
|
+
|
890
|
+
- ``zero`` -- the zero symbol in the alphabet used
|
891
|
+
|
892
|
+
OUTPUT:
|
893
|
+
|
894
|
+
A transducer mapping `i_0\ldots i_k` to `(i_0\neq 0)\ldots(i_k\neq 0)`.
|
895
|
+
|
896
|
+
The Hamming weight is defined as the number of nonzero digits in the
|
897
|
+
input sequence over the alphabet ``input_alphabet`` (see
|
898
|
+
:wikipedia:`Hamming_weight`). The output sequence of the transducer is
|
899
|
+
a unary encoding of the Hamming weight. Thus the sum of the output
|
900
|
+
sequence is the Hamming weight of the input.
|
901
|
+
|
902
|
+
EXAMPLES::
|
903
|
+
|
904
|
+
sage: W = transducers.weight([-1, 0, 2])
|
905
|
+
sage: W.transitions()
|
906
|
+
[Transition from 0 to 0: -1|1,
|
907
|
+
Transition from 0 to 0: 0|0,
|
908
|
+
Transition from 0 to 0: 2|1]
|
909
|
+
sage: unary_weight = W([-1, 0, 0, 2, -1])
|
910
|
+
sage: unary_weight
|
911
|
+
[1, 0, 0, 1, 1]
|
912
|
+
sage: weight = add(unary_weight)
|
913
|
+
sage: weight
|
914
|
+
3
|
915
|
+
|
916
|
+
Also the joint Hamming weight can be computed::
|
917
|
+
|
918
|
+
sage: v1 = vector([-1, 0])
|
919
|
+
sage: v0 = vector([0, 0])
|
920
|
+
sage: W = transducers.weight([v1, v0])
|
921
|
+
sage: unary_weight = W([v1, v0, v1, v0])
|
922
|
+
sage: add(unary_weight)
|
923
|
+
2
|
924
|
+
|
925
|
+
For the input alphabet ``[-1, 0, 1]`` the weight transducer is the
|
926
|
+
same as the absolute value transducer
|
927
|
+
:meth:`~TransducerGenerators.abs`::
|
928
|
+
|
929
|
+
sage: W = transducers.weight([-1, 0, 1])
|
930
|
+
sage: A = transducers.abs([-1, 0, 1])
|
931
|
+
sage: W == A
|
932
|
+
True
|
933
|
+
|
934
|
+
For other input alphabets, we can specify the zero symbol::
|
935
|
+
|
936
|
+
sage: W = transducers.weight(['a', 'b'], zero='a')
|
937
|
+
sage: add(W(['a', 'b', 'b']))
|
938
|
+
2
|
939
|
+
"""
|
940
|
+
def weight(state, input):
|
941
|
+
weight = int(input != zero)
|
942
|
+
return (0, weight)
|
943
|
+
return Transducer(weight, input_alphabet=input_alphabet,
|
944
|
+
initial_states=[0],
|
945
|
+
final_states=[0])
|
946
|
+
|
947
|
+
def abs(self, input_alphabet):
|
948
|
+
r"""
|
949
|
+
Return a transducer which realizes the letter-wise
|
950
|
+
absolute value of an input word over the given input alphabet.
|
951
|
+
|
952
|
+
INPUT:
|
953
|
+
|
954
|
+
- ``input_alphabet`` -- list or other iterable
|
955
|
+
|
956
|
+
OUTPUT:
|
957
|
+
|
958
|
+
A transducer mapping `i_0\ldots i_k`
|
959
|
+
to `|i_0|\ldots |i_k|`.
|
960
|
+
|
961
|
+
EXAMPLES:
|
962
|
+
|
963
|
+
The following transducer realizes letter-wise
|
964
|
+
absolute value::
|
965
|
+
|
966
|
+
sage: T = transducers.abs([-1, 0, 1])
|
967
|
+
sage: T.transitions()
|
968
|
+
[Transition from 0 to 0: -1|1,
|
969
|
+
Transition from 0 to 0: 0|0,
|
970
|
+
Transition from 0 to 0: 1|1]
|
971
|
+
sage: T.initial_states()
|
972
|
+
[0]
|
973
|
+
sage: T.final_states()
|
974
|
+
[0]
|
975
|
+
sage: T([-1, -1, 0, 1])
|
976
|
+
[1, 1, 0, 1]
|
977
|
+
"""
|
978
|
+
return self.map(abs, input_alphabet)
|
979
|
+
|
980
|
+
def GrayCode(self):
|
981
|
+
"""
|
982
|
+
Return a transducer converting the standard binary
|
983
|
+
expansion to Gray code.
|
984
|
+
|
985
|
+
OUTPUT: a transducer
|
986
|
+
|
987
|
+
Cf. the :wikipedia:`Gray_code` for a description of the Gray code.
|
988
|
+
|
989
|
+
EXAMPLES::
|
990
|
+
|
991
|
+
sage: G = transducers.GrayCode()
|
992
|
+
sage: G
|
993
|
+
Transducer with 3 states
|
994
|
+
sage: for v in srange(10):
|
995
|
+
....: print("{} {}".format(v, G(v.digits(base=2))))
|
996
|
+
0 []
|
997
|
+
1 [1]
|
998
|
+
2 [1, 1]
|
999
|
+
3 [0, 1]
|
1000
|
+
4 [0, 1, 1]
|
1001
|
+
5 [1, 1, 1]
|
1002
|
+
6 [1, 0, 1]
|
1003
|
+
7 [0, 0, 1]
|
1004
|
+
8 [0, 0, 1, 1]
|
1005
|
+
9 [1, 0, 1, 1]
|
1006
|
+
|
1007
|
+
In the example :ref:`Gray Code <finite_state_machine_gray_code_example>`
|
1008
|
+
in the documentation of the
|
1009
|
+
:doc:`finite_state_machine` module, the Gray code
|
1010
|
+
transducer is derived from the algorithm converting the binary
|
1011
|
+
expansion to the Gray code. The result is the same as the one
|
1012
|
+
given here.
|
1013
|
+
"""
|
1014
|
+
z = ZZ(0)
|
1015
|
+
o = ZZ(1)
|
1016
|
+
return Transducer([[0, 1, z, None],
|
1017
|
+
[0, 2, o, None],
|
1018
|
+
[1, 1, z, z],
|
1019
|
+
[1, 2, o, o],
|
1020
|
+
[2, 1, z, o],
|
1021
|
+
[2, 2, o, z]],
|
1022
|
+
initial_states=[0],
|
1023
|
+
final_states=[1],
|
1024
|
+
with_final_word_out=[0])
|
1025
|
+
|
1026
|
+
RecursionRule = namedtuple('RecursionRule', ['K', 'r', 'k', 's', 't'])
|
1027
|
+
|
1028
|
+
def _parse_recursion_equation_(self, equation, base, function, var,
|
1029
|
+
word_function=None, output_rings=[ZZ, QQ]):
|
1030
|
+
"""
|
1031
|
+
Parse one equation as admissible in :meth:`~.Recursion`.
|
1032
|
+
|
1033
|
+
INPUT:
|
1034
|
+
|
1035
|
+
- ``equation`` -- an equation of the form
|
1036
|
+
|
1037
|
+
- ``f(base^K * n + r) == f(base^k * n + s) + t`` for some
|
1038
|
+
integers ``0 <= k < K``, ``r`` and some ``t``---valid for
|
1039
|
+
all ``n`` such that the arguments on both sides are
|
1040
|
+
nonnegative---
|
1041
|
+
|
1042
|
+
or the form
|
1043
|
+
|
1044
|
+
- ``f(r) == t`` for some integer ``r`` and some ``t``.
|
1045
|
+
|
1046
|
+
- ``base`` -- see :meth:`~Recursion`
|
1047
|
+
|
1048
|
+
- ``function`` -- see :meth:`~Recursion`
|
1049
|
+
|
1050
|
+
- ``var`` -- see :meth:`~Recursion`
|
1051
|
+
|
1052
|
+
- ``output_rings`` -- see :meth:`~Recursion`
|
1053
|
+
|
1054
|
+
OUTPUT:
|
1055
|
+
|
1056
|
+
A ``RecursionRule`` if the equation is of the first form
|
1057
|
+
described above and a dictionary ``{r: [t]}`` otherwise.
|
1058
|
+
|
1059
|
+
EXAMPLES::
|
1060
|
+
|
1061
|
+
sage: # needs sage.symbolic
|
1062
|
+
sage: var('n')
|
1063
|
+
n
|
1064
|
+
sage: function('f')
|
1065
|
+
f
|
1066
|
+
sage: transducers._parse_recursion_equation_(
|
1067
|
+
....: f(8*n + 7) == f(2*n + 3) + 5,
|
1068
|
+
....: 2, f, n)
|
1069
|
+
RecursionRule(K=3, r=7, k=1, s=3, t=[5])
|
1070
|
+
sage: transducers._parse_recursion_equation_(
|
1071
|
+
....: f(42) == 5,
|
1072
|
+
....: 2, f, n)
|
1073
|
+
{42: [5]}
|
1074
|
+
|
1075
|
+
TESTS:
|
1076
|
+
|
1077
|
+
The following tests check that the equations are well-formed::
|
1078
|
+
|
1079
|
+
sage: transducers._parse_recursion_equation_(f(4*n + 1), 2, f, n) # needs sage.symbolic
|
1080
|
+
Traceback (most recent call last):
|
1081
|
+
...
|
1082
|
+
ValueError: f(4*n + 1) is not an equation with ==.
|
1083
|
+
|
1084
|
+
::
|
1085
|
+
|
1086
|
+
sage: transducers._parse_recursion_equation_(f(n) + 1 == f(2*n), # needs sage.symbolic
|
1087
|
+
....: 2, f, n)
|
1088
|
+
Traceback (most recent call last):
|
1089
|
+
...
|
1090
|
+
ValueError: f(n) + 1 is not an evaluation of f.
|
1091
|
+
|
1092
|
+
::
|
1093
|
+
|
1094
|
+
sage: transducers._parse_recursion_equation_(f(2*n, 5) == 3, # needs sage.symbolic
|
1095
|
+
....: 2, f, n)
|
1096
|
+
Traceback (most recent call last):
|
1097
|
+
...
|
1098
|
+
ValueError: f(2*n, 5) does not have one argument.
|
1099
|
+
|
1100
|
+
::
|
1101
|
+
|
1102
|
+
sage: transducers._parse_recursion_equation_(f(1/n) == f(n) + 3, # needs sage.symbolic
|
1103
|
+
....: 2, f, n)
|
1104
|
+
Traceback (most recent call last):
|
1105
|
+
...
|
1106
|
+
ValueError: 1/n is not a polynomial in n.
|
1107
|
+
|
1108
|
+
::
|
1109
|
+
|
1110
|
+
sage: transducers._parse_recursion_equation_(f(n^2 + 5) == 3, # needs sage.symbolic
|
1111
|
+
....: 2, f, n)
|
1112
|
+
Traceback (most recent call last):
|
1113
|
+
...
|
1114
|
+
ValueError: n^2 + 5 is not a polynomial of degree 1.
|
1115
|
+
|
1116
|
+
::
|
1117
|
+
|
1118
|
+
sage: transducers._parse_recursion_equation_(f(3*n + 5) == f(n) + 7, # needs sage.symbolic
|
1119
|
+
....: 2, f, n)
|
1120
|
+
Traceback (most recent call last):
|
1121
|
+
...
|
1122
|
+
ValueError: 3 is not a power of 2.
|
1123
|
+
|
1124
|
+
::
|
1125
|
+
|
1126
|
+
sage: transducers._parse_recursion_equation_(f(n + 5) == f(n) + 7, # needs sage.symbolic
|
1127
|
+
....: 2, f, n)
|
1128
|
+
Traceback (most recent call last):
|
1129
|
+
...
|
1130
|
+
ValueError: 1 is less than 2.
|
1131
|
+
|
1132
|
+
::
|
1133
|
+
|
1134
|
+
sage: transducers._parse_recursion_equation_( # needs sage.symbolic
|
1135
|
+
....: f(2*n + 1) == f(n + 1) + f(n) + 2,
|
1136
|
+
....: 2, f, n)
|
1137
|
+
Traceback (most recent call last):
|
1138
|
+
...
|
1139
|
+
ValueError: f(n + 1) + f(n) + 2 does not contain
|
1140
|
+
exactly one summand which is an evaluation of f.
|
1141
|
+
|
1142
|
+
::
|
1143
|
+
|
1144
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == sin(n) + 2, # needs sage.symbolic
|
1145
|
+
....: 2, f, n)
|
1146
|
+
Traceback (most recent call last):
|
1147
|
+
...
|
1148
|
+
ValueError: sin(n) + 2 does not contain exactly one
|
1149
|
+
summand which is an evaluation of f.
|
1150
|
+
|
1151
|
+
::
|
1152
|
+
|
1153
|
+
sage: transducers._parse_recursion_equation_( # needs sage.symbolic
|
1154
|
+
....: f(2*n + 1) == f(n) + n + 2,
|
1155
|
+
....: 2, f, n)
|
1156
|
+
Traceback (most recent call last):
|
1157
|
+
...
|
1158
|
+
ValueError: n + 2 contains n.
|
1159
|
+
|
1160
|
+
::
|
1161
|
+
|
1162
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == sin(n), # needs sage.symbolic
|
1163
|
+
....: 2, f, n)
|
1164
|
+
Traceback (most recent call last):
|
1165
|
+
...
|
1166
|
+
ValueError: sin(n) is not an evaluation of f.
|
1167
|
+
|
1168
|
+
::
|
1169
|
+
|
1170
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == f(n, 2), # needs sage.symbolic
|
1171
|
+
....: 2, f, n)
|
1172
|
+
Traceback (most recent call last):
|
1173
|
+
...
|
1174
|
+
ValueError: f(n, 2) does not have exactly one argument.
|
1175
|
+
|
1176
|
+
::
|
1177
|
+
|
1178
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == f(1/n), # needs sage.symbolic
|
1179
|
+
....: 2, f, n)
|
1180
|
+
Traceback (most recent call last):
|
1181
|
+
...
|
1182
|
+
ValueError: 1/n is not a polynomial in n.
|
1183
|
+
|
1184
|
+
::
|
1185
|
+
|
1186
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == f(n^2 + 5), # needs sage.symbolic
|
1187
|
+
....: 2, f, n)
|
1188
|
+
Traceback (most recent call last):
|
1189
|
+
...
|
1190
|
+
ValueError: n^2 + 5 is not a polynomial of degree 1.
|
1191
|
+
|
1192
|
+
::
|
1193
|
+
|
1194
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == f(3*n + 5), # needs sage.symbolic
|
1195
|
+
....: 2, f, n)
|
1196
|
+
Traceback (most recent call last):
|
1197
|
+
...
|
1198
|
+
ValueError: 3 is not a power of 2.
|
1199
|
+
|
1200
|
+
::
|
1201
|
+
|
1202
|
+
sage: transducers._parse_recursion_equation_( # needs sage.symbolic
|
1203
|
+
....: f(2*n + 1) == f((1/2)*n + 5),
|
1204
|
+
....: QQ(2), f, n)
|
1205
|
+
Traceback (most recent call last):
|
1206
|
+
...
|
1207
|
+
ValueError: 1/2 is less than 1.
|
1208
|
+
|
1209
|
+
::
|
1210
|
+
|
1211
|
+
sage: transducers._parse_recursion_equation_(f(2*n + 1) == f(2*n + 5), # needs sage.symbolic
|
1212
|
+
....: 2, f, n)
|
1213
|
+
Traceback (most recent call last):
|
1214
|
+
...
|
1215
|
+
ValueError: 2 is greater or equal than 2.
|
1216
|
+
"""
|
1217
|
+
from sage.functions.log import log
|
1218
|
+
|
1219
|
+
def is_scalar(expression):
|
1220
|
+
return var not in expression.variables()
|
1221
|
+
|
1222
|
+
def convert_output(output):
|
1223
|
+
for ring in output_rings:
|
1224
|
+
try:
|
1225
|
+
return ring(output)
|
1226
|
+
except (ValueError, TypeError):
|
1227
|
+
pass
|
1228
|
+
return output
|
1229
|
+
|
1230
|
+
def to_list(output):
|
1231
|
+
if output == 0:
|
1232
|
+
return []
|
1233
|
+
elif word_function is not None and output.operator() == word_function:
|
1234
|
+
return [convert_output(_) for _ in output.operands()]
|
1235
|
+
else:
|
1236
|
+
return [convert_output(output)]
|
1237
|
+
|
1238
|
+
base_ring = base.parent()
|
1239
|
+
|
1240
|
+
if equation.operator() != operator.eq:
|
1241
|
+
raise ValueError("%s is not an equation with ==."
|
1242
|
+
% equation)
|
1243
|
+
assert len(equation.operands()) == 2, \
|
1244
|
+
"%s is not an equation with two operands." % equation
|
1245
|
+
left_side, right_side = equation.operands()
|
1246
|
+
|
1247
|
+
if left_side.operator() != function:
|
1248
|
+
raise ValueError("%s is not an evaluation of %s."
|
1249
|
+
% (left_side, function))
|
1250
|
+
if len(left_side.operands()) != 1:
|
1251
|
+
raise ValueError("%s does not have one argument." %
|
1252
|
+
(left_side,))
|
1253
|
+
|
1254
|
+
try:
|
1255
|
+
polynomial_left = base_ring[var](left_side.operands()[0])
|
1256
|
+
except Exception:
|
1257
|
+
raise ValueError("%s is not a polynomial "
|
1258
|
+
"in %s." % (left_side.operands()[0], var))
|
1259
|
+
if polynomial_left in base_ring and is_scalar(right_side):
|
1260
|
+
return {polynomial_left: to_list(right_side)}
|
1261
|
+
|
1262
|
+
if polynomial_left.degree() != 1:
|
1263
|
+
raise ValueError("%s is not a polynomial of degree 1."
|
1264
|
+
% (polynomial_left,))
|
1265
|
+
|
1266
|
+
[r, base_power_K] = list(polynomial_left)
|
1267
|
+
try:
|
1268
|
+
K = log(base_power_K, base=base)
|
1269
|
+
except RuntimeError:
|
1270
|
+
K = 1
|
1271
|
+
try:
|
1272
|
+
K = K.simplify()
|
1273
|
+
except AttributeError:
|
1274
|
+
pass
|
1275
|
+
if K not in ZZ:
|
1276
|
+
raise ValueError("%s is not a power of %s."
|
1277
|
+
% (base_power_K, base))
|
1278
|
+
if K < 1:
|
1279
|
+
raise ValueError("%d is less than %d."
|
1280
|
+
% (base_power_K, base))
|
1281
|
+
|
1282
|
+
from sage.symbolic.operators import add_vararg
|
1283
|
+
if right_side.operator() == add_vararg:
|
1284
|
+
function_calls = [o for o in right_side.operands()
|
1285
|
+
if o.operator() == function]
|
1286
|
+
other_terms = [o for o in right_side.operands()
|
1287
|
+
if o.operator() != function]
|
1288
|
+
if len(function_calls) != 1:
|
1289
|
+
raise ValueError(
|
1290
|
+
"%s does not contain exactly one summand which "
|
1291
|
+
"is an evaluation of %s."
|
1292
|
+
% (right_side, function))
|
1293
|
+
next_function = function_calls[0]
|
1294
|
+
t = sum(other_terms)
|
1295
|
+
if not is_scalar(t):
|
1296
|
+
raise ValueError("%s contains %s."
|
1297
|
+
% (t, var))
|
1298
|
+
else:
|
1299
|
+
next_function = right_side
|
1300
|
+
t = 0
|
1301
|
+
|
1302
|
+
if next_function.operator() != function:
|
1303
|
+
raise ValueError("%s is not an evaluation of %s."
|
1304
|
+
% (next_function, function))
|
1305
|
+
if len(next_function.operands()) != 1:
|
1306
|
+
raise ValueError("%s does not have exactly one argument."
|
1307
|
+
% (next_function,))
|
1308
|
+
|
1309
|
+
try:
|
1310
|
+
polynomial_right = base_ring[var](next_function.operands()[0])
|
1311
|
+
except Exception:
|
1312
|
+
raise ValueError("%s is not a polynomial in %s."
|
1313
|
+
% (next_function.operands()[0], var))
|
1314
|
+
if polynomial_right.degree() != 1:
|
1315
|
+
raise ValueError("%s is not a polynomial of degree 1."
|
1316
|
+
% (polynomial_right,))
|
1317
|
+
[s, base_power_k] = list(polynomial_right)
|
1318
|
+
k = log(base_power_k, base=base)
|
1319
|
+
try:
|
1320
|
+
k = k.simplify()
|
1321
|
+
except AttributeError:
|
1322
|
+
pass
|
1323
|
+
if k not in ZZ:
|
1324
|
+
raise ValueError("%s is not a power of %s."
|
1325
|
+
% (base_power_k, base))
|
1326
|
+
if k < 0:
|
1327
|
+
raise ValueError("%s is less than 1."
|
1328
|
+
% (base_power_k,))
|
1329
|
+
if k >= K:
|
1330
|
+
raise ValueError("%d is greater or equal than %d."
|
1331
|
+
% (base_power_k, base_power_K))
|
1332
|
+
|
1333
|
+
parsed_equation = function(base**K * var + r) == \
|
1334
|
+
function(base**k * var + s) + t
|
1335
|
+
assert equation == parsed_equation, \
|
1336
|
+
"Parsing of %s failed for unknown reasons." % (equation,)
|
1337
|
+
|
1338
|
+
rule = self.RecursionRule(K=K, r=r, k=k, s=s, t=to_list(t))
|
1339
|
+
return rule
|
1340
|
+
|
1341
|
+
def Recursion(self, recursions, base, function=None, var=None,
|
1342
|
+
input_alphabet=None, word_function=None,
|
1343
|
+
is_zero=None, output_rings=[ZZ, QQ]):
|
1344
|
+
r"""
|
1345
|
+
Return a transducer realizing the given recursion when reading
|
1346
|
+
the digit expansion with base ``base``.
|
1347
|
+
|
1348
|
+
INPUT:
|
1349
|
+
|
1350
|
+
- ``recursions`` -- list or iterable of equations. Each
|
1351
|
+
equation has either the form
|
1352
|
+
|
1353
|
+
- ``f(base^K * n + r) == f(base^k * n + s) + t`` for some
|
1354
|
+
integers ``0 <= k < K``, ``r`` and some ``t``---valid for
|
1355
|
+
all ``n`` such that the arguments on both sides are
|
1356
|
+
nonnegative---
|
1357
|
+
|
1358
|
+
or the form
|
1359
|
+
|
1360
|
+
- ``f(r) == t`` for some integer ``r`` and some ``t``.
|
1361
|
+
|
1362
|
+
Alternatively, an equation may be replaced by a
|
1363
|
+
``transducers.RecursionRule`` with the attributes ``K``,
|
1364
|
+
``r``, ``k``, ``s``, ``t`` as above or a tuple ``(r, t)``.
|
1365
|
+
Note that ``t`` *must* be a list in this case.
|
1366
|
+
|
1367
|
+
- ``base`` -- base of the digit expansion
|
1368
|
+
|
1369
|
+
- ``function`` -- symbolic function ``f`` occurring in the
|
1370
|
+
recursions
|
1371
|
+
|
1372
|
+
- ``var`` -- symbolic variable
|
1373
|
+
|
1374
|
+
- ``input_alphabet`` -- (default: ``None``) a list of digits
|
1375
|
+
to be used as the input alphabet. If ``None`` and the base
|
1376
|
+
is an integer, ``input_alphabet`` is chosen to be
|
1377
|
+
``srange(base.abs())``.
|
1378
|
+
|
1379
|
+
- ``word_function`` -- (default: ``None``) a symbolic function.
|
1380
|
+
If not ``None``, ``word_function(arg1, ..., argn)`` in a symbolic
|
1381
|
+
recurrence relation is interpreted as a transition with output
|
1382
|
+
``[arg1, ..., argn]``. This could not be entered in a symbolic
|
1383
|
+
recurrence relation because lists do not coerce into the
|
1384
|
+
:class:`~sage.symbolic.ring.SymbolicRing`.
|
1385
|
+
|
1386
|
+
- ``is_zero`` -- (default: ``None``) a callable. The recursion
|
1387
|
+
relations are only well-posed if there is no cycle with
|
1388
|
+
nonzero output and input consisting of zeros. This parameter
|
1389
|
+
is used to determine whether the output of such a cycle is
|
1390
|
+
nonzero. By default, the output must evaluate to ``False`` as
|
1391
|
+
a boolean.
|
1392
|
+
|
1393
|
+
- ``output_rings`` -- (default: ``[ZZ, QQ]``) a list of
|
1394
|
+
rings. The output labels are converted into the first ring of
|
1395
|
+
the list in which they are contained. If they are not
|
1396
|
+
contained in any ring, they remain in whatever ring they are
|
1397
|
+
after parsing the recursions, typically the symbolic ring.
|
1398
|
+
|
1399
|
+
OUTPUT: a transducer ``T``
|
1400
|
+
|
1401
|
+
The transducer is constructed such that ``T(expansion) == f(n)``
|
1402
|
+
if ``expansion`` is the digit expansion of ``n`` to the base
|
1403
|
+
``base`` with the given input alphabet as set of digits. Here,
|
1404
|
+
the ``+`` on the right hand side of the recurrence relation is
|
1405
|
+
interpreted as the concatenation of words.
|
1406
|
+
|
1407
|
+
The formal equations and initial conditions in the recursion
|
1408
|
+
have to be selected such that ``f`` is uniquely defined.
|
1409
|
+
|
1410
|
+
EXAMPLES:
|
1411
|
+
|
1412
|
+
- The following example computes the Hamming weight of the
|
1413
|
+
ternary expansion of integers. ::
|
1414
|
+
|
1415
|
+
sage: # needs sage.symbolic
|
1416
|
+
sage: function('f')
|
1417
|
+
f
|
1418
|
+
sage: var('n')
|
1419
|
+
n
|
1420
|
+
sage: T = transducers.Recursion([
|
1421
|
+
....: f(3*n + 1) == f(n) + 1,
|
1422
|
+
....: f(3*n + 2) == f(n) + 1,
|
1423
|
+
....: f(3*n) == f(n),
|
1424
|
+
....: f(0) == 0],
|
1425
|
+
....: 3, f, n)
|
1426
|
+
sage: T.transitions()
|
1427
|
+
[Transition from (0, 0) to (0, 0): 0|-,
|
1428
|
+
Transition from (0, 0) to (0, 0): 1|1,
|
1429
|
+
Transition from (0, 0) to (0, 0): 2|1]
|
1430
|
+
|
1431
|
+
To illustrate what this transducer does, we consider the
|
1432
|
+
example of `n=601`::
|
1433
|
+
|
1434
|
+
sage: # needs sage.symbolic
|
1435
|
+
sage: ternary_expansion = 601.digits(base=3)
|
1436
|
+
sage: ternary_expansion
|
1437
|
+
[1, 2, 0, 1, 1, 2]
|
1438
|
+
sage: weight_sequence = T(ternary_expansion)
|
1439
|
+
sage: weight_sequence
|
1440
|
+
[1, 1, 1, 1, 1]
|
1441
|
+
sage: sum(weight_sequence)
|
1442
|
+
5
|
1443
|
+
|
1444
|
+
Note that the digit zero does not show up in the output because
|
1445
|
+
the equation ``f(3*n) == f(n)`` means that no output is added to
|
1446
|
+
``f(n)``.
|
1447
|
+
|
1448
|
+
- The following example computes the Hamming weight of the
|
1449
|
+
non-adjacent form, cf. the :wikipedia:`Non-adjacent_form`. ::
|
1450
|
+
|
1451
|
+
sage: # needs sage.symbolic
|
1452
|
+
sage: function('f')
|
1453
|
+
f
|
1454
|
+
sage: var('n')
|
1455
|
+
n
|
1456
|
+
sage: T = transducers.Recursion([
|
1457
|
+
....: f(4*n + 1) == f(n) + 1,
|
1458
|
+
....: f(4*n - 1) == f(n) + 1,
|
1459
|
+
....: f(2*n) == f(n),
|
1460
|
+
....: f(0) == 0],
|
1461
|
+
....: 2, f, n)
|
1462
|
+
sage: T.transitions()
|
1463
|
+
[Transition from (0, 0) to (0, 0): 0|-,
|
1464
|
+
Transition from (0, 0) to (1, 1): 1|-,
|
1465
|
+
Transition from (1, 1) to (0, 0): 0|1,
|
1466
|
+
Transition from (1, 1) to (1, 0): 1|1,
|
1467
|
+
Transition from (1, 0) to (1, 1): 0|-,
|
1468
|
+
Transition from (1, 0) to (1, 0): 1|-]
|
1469
|
+
sage: [(s.label(), s.final_word_out)
|
1470
|
+
....: for s in T.iter_final_states()]
|
1471
|
+
[((0, 0), []),
|
1472
|
+
((1, 1), [1]),
|
1473
|
+
((1, 0), [1])]
|
1474
|
+
|
1475
|
+
As we are interested in the weight only, we also output `1`
|
1476
|
+
for numbers congruent to `3` mod `4`. The actual expansion
|
1477
|
+
is computed in the next example.
|
1478
|
+
|
1479
|
+
Consider the example of `29=(100\bar 101)_2` (as usual,
|
1480
|
+
the digit `-1` is denoted by `\bar 1` and digits are
|
1481
|
+
written from the most significant digit at the left to the
|
1482
|
+
least significant digit at the right; for the transducer,
|
1483
|
+
we have to give the digits in the reverse order)::
|
1484
|
+
|
1485
|
+
sage: NAF = [1, 0, -1, 0, 0, 1]
|
1486
|
+
sage: ZZ(NAF, base=2)
|
1487
|
+
29
|
1488
|
+
sage: binary_expansion = 29.digits(base=2)
|
1489
|
+
sage: binary_expansion
|
1490
|
+
[1, 0, 1, 1, 1]
|
1491
|
+
sage: T(binary_expansion) # needs sage.symbolic
|
1492
|
+
[1, 1, 1]
|
1493
|
+
sage: sum(T(binary_expansion)) # needs sage.symbolic
|
1494
|
+
3
|
1495
|
+
|
1496
|
+
Indeed, the given non-adjacent form has three nonzero
|
1497
|
+
digits.
|
1498
|
+
|
1499
|
+
- The following example computes the non-adjacent form from the
|
1500
|
+
binary expansion, cf. the :wikipedia:`Non-adjacent_form`. In
|
1501
|
+
contrast to the previous example, we actually compute the
|
1502
|
+
expansion, not only the weight.
|
1503
|
+
|
1504
|
+
We have to write the output `0` when converting an even number.
|
1505
|
+
This cannot be encoded directly by an equation in the symbolic
|
1506
|
+
ring, because ``f(2*n) == f(n) + 0`` would be equivalent to
|
1507
|
+
``f(2*n) == f(n)`` and an empty output would be written.
|
1508
|
+
Therefore, we wrap the output in the symbolic function ``w``
|
1509
|
+
and use the parameter ``word_function`` to announce this.
|
1510
|
+
|
1511
|
+
Similarly, we use ``w(-1, 0)`` to write an output word of
|
1512
|
+
length `2` in one iteration. Finally, we write ``f(0) == w()``
|
1513
|
+
to write an empty word upon completion.
|
1514
|
+
|
1515
|
+
Moreover, there is a cycle with output ``[0]`` which---from
|
1516
|
+
the point of view of this method---is a contradicting recursion.
|
1517
|
+
We override this by the parameter ``is_zero``. ::
|
1518
|
+
|
1519
|
+
sage: # needs sage.symbolic
|
1520
|
+
sage: var('n')
|
1521
|
+
n
|
1522
|
+
sage: function('f w')
|
1523
|
+
(f, w)
|
1524
|
+
sage: T = transducers.Recursion([
|
1525
|
+
....: f(2*n) == f(n) + w(0),
|
1526
|
+
....: f(4*n + 1) == f(n) + w(1, 0),
|
1527
|
+
....: f(4*n - 1) == f(n) + w(-1, 0),
|
1528
|
+
....: f(0) == w()],
|
1529
|
+
....: 2, f, n,
|
1530
|
+
....: word_function=w,
|
1531
|
+
....: is_zero=lambda x: sum(x).is_zero())
|
1532
|
+
sage: T.transitions()
|
1533
|
+
[Transition from (0, 0) to (0, 0): 0|0,
|
1534
|
+
Transition from (0, 0) to (1, 1): 1|-,
|
1535
|
+
Transition from (1, 1) to (0, 0): 0|1,0,
|
1536
|
+
Transition from (1, 1) to (1, 0): 1|-1,0,
|
1537
|
+
Transition from (1, 0) to (1, 1): 0|-,
|
1538
|
+
Transition from (1, 0) to (1, 0): 1|0]
|
1539
|
+
sage: for s in T.iter_states():
|
1540
|
+
....: print("{} {}".format(s, s.final_word_out))
|
1541
|
+
(0, 0) []
|
1542
|
+
(1, 1) [1, 0]
|
1543
|
+
(1, 0) [1, 0]
|
1544
|
+
|
1545
|
+
We again consider the example of `n=29`::
|
1546
|
+
|
1547
|
+
sage: T(29.digits(base=2)) # needs sage.symbolic
|
1548
|
+
[1, 0, -1, 0, 0, 1, 0]
|
1549
|
+
|
1550
|
+
The same transducer can also be entered bypassing the
|
1551
|
+
symbolic equations::
|
1552
|
+
|
1553
|
+
sage: R = transducers.RecursionRule
|
1554
|
+
sage: TR = transducers.Recursion([
|
1555
|
+
....: R(K=1, r=0, k=0, s=0, t=[0]),
|
1556
|
+
....: R(K=2, r=1, k=0, s=0, t=[1, 0]),
|
1557
|
+
....: R(K=2, r=-1, k=0, s=0, t=[-1, 0]),
|
1558
|
+
....: (0, [])],
|
1559
|
+
....: 2,
|
1560
|
+
....: is_zero=lambda x: sum(x).is_zero())
|
1561
|
+
sage: TR == T # needs sage.symbolic
|
1562
|
+
True
|
1563
|
+
|
1564
|
+
- Here is an artificial example where some of the `s` are
|
1565
|
+
negative::
|
1566
|
+
|
1567
|
+
sage: # needs sage.symbolic
|
1568
|
+
sage: function('f')
|
1569
|
+
f
|
1570
|
+
sage: var('n')
|
1571
|
+
n
|
1572
|
+
sage: T = transducers.Recursion([
|
1573
|
+
....: f(2*n + 1) == f(n-1) + 1,
|
1574
|
+
....: f(2*n) == f(n),
|
1575
|
+
....: f(1) == 1,
|
1576
|
+
....: f(0) == 0], 2, f, n)
|
1577
|
+
sage: T.transitions()
|
1578
|
+
[Transition from (0, 0) to (0, 0): 0|-,
|
1579
|
+
Transition from (0, 0) to (1, 1): 1|-,
|
1580
|
+
Transition from (1, 1) to (-1, 1): 0|1,
|
1581
|
+
Transition from (1, 1) to (0, 0): 1|1,
|
1582
|
+
Transition from (-1, 1) to (-1, 2): 0|-,
|
1583
|
+
Transition from (-1, 1) to (1, 2): 1|-,
|
1584
|
+
Transition from (-1, 2) to (-1, 1): 0|1,
|
1585
|
+
Transition from (-1, 2) to (0, 0): 1|1,
|
1586
|
+
Transition from (1, 2) to (-1, 2): 0|1,
|
1587
|
+
Transition from (1, 2) to (1, 2): 1|1]
|
1588
|
+
sage: [(s.label(), s.final_word_out)
|
1589
|
+
....: for s in T.iter_final_states()]
|
1590
|
+
[((0, 0), []),
|
1591
|
+
((1, 1), [1]),
|
1592
|
+
((-1, 1), [0]),
|
1593
|
+
((-1, 2), [0]),
|
1594
|
+
((1, 2), [1])]
|
1595
|
+
|
1596
|
+
- Abelian complexity of the paperfolding sequence
|
1597
|
+
(cf. [HKP2015]_, Example 2.8)::
|
1598
|
+
|
1599
|
+
sage: # needs sage.symbolic
|
1600
|
+
sage: T = transducers.Recursion([
|
1601
|
+
....: f(4*n) == f(2*n),
|
1602
|
+
....: f(4*n+2) == f(2*n+1)+1,
|
1603
|
+
....: f(16*n+1) == f(8*n+1),
|
1604
|
+
....: f(16*n+5) == f(4*n+1)+2,
|
1605
|
+
....: f(16*n+11) == f(4*n+3)+2,
|
1606
|
+
....: f(16*n+15) == f(2*n+2)+1,
|
1607
|
+
....: f(1) == 2, f(0) == 0]
|
1608
|
+
....: + [f(16*n+jj) == f(2*n+1)+2 for jj in [3,7,9,13]],
|
1609
|
+
....: 2, f, n)
|
1610
|
+
sage: T.transitions()
|
1611
|
+
[Transition from (0, 0) to (0, 1): 0|-,
|
1612
|
+
Transition from (0, 0) to (1, 1): 1|-,
|
1613
|
+
Transition from (0, 1) to (0, 1): 0|-,
|
1614
|
+
Transition from (0, 1) to (1, 1): 1|1,
|
1615
|
+
Transition from (1, 1) to (1, 2): 0|-,
|
1616
|
+
Transition from (1, 1) to (3, 2): 1|-,
|
1617
|
+
Transition from (1, 2) to (1, 3): 0|-,
|
1618
|
+
Transition from (1, 2) to (5, 3): 1|-,
|
1619
|
+
Transition from (3, 2) to (3, 3): 0|-,
|
1620
|
+
Transition from (3, 2) to (7, 3): 1|-,
|
1621
|
+
Transition from (1, 3) to (1, 3): 0|-,
|
1622
|
+
Transition from (1, 3) to (1, 1): 1|2,
|
1623
|
+
Transition from (5, 3) to (1, 2): 0|2,
|
1624
|
+
Transition from (5, 3) to (1, 1): 1|2,
|
1625
|
+
Transition from (3, 3) to (1, 1): 0|2,
|
1626
|
+
Transition from (3, 3) to (3, 2): 1|2,
|
1627
|
+
Transition from (7, 3) to (1, 1): 0|2,
|
1628
|
+
Transition from (7, 3) to (2, 1): 1|1,
|
1629
|
+
Transition from (2, 1) to (1, 1): 0|1,
|
1630
|
+
Transition from (2, 1) to (2, 1): 1|-]
|
1631
|
+
sage: for s in T.iter_states():
|
1632
|
+
....: print("{} {}".format(s, s.final_word_out))
|
1633
|
+
(0, 0) []
|
1634
|
+
(0, 1) []
|
1635
|
+
(1, 1) [2]
|
1636
|
+
(1, 2) [2]
|
1637
|
+
(3, 2) [2, 2]
|
1638
|
+
(1, 3) [2]
|
1639
|
+
(5, 3) [2, 2]
|
1640
|
+
(3, 3) [2, 2]
|
1641
|
+
(7, 3) [2, 2]
|
1642
|
+
(2, 1) [1, 2]
|
1643
|
+
sage: list(sum(T(n.bits())) for n in srange(1, 21))
|
1644
|
+
[2, 3, 4, 3, 4, 5, 4, 3, 4, 5, 6, 5, 4, 5, 4, 3, 4, 5, 6, 5]
|
1645
|
+
|
1646
|
+
- We now demonstrate the use of the ``output_rings``
|
1647
|
+
parameter. If no ``output_rings`` are specified, the
|
1648
|
+
output labels are converted into ``ZZ``::
|
1649
|
+
|
1650
|
+
sage: # needs sage.symbolic
|
1651
|
+
sage: function('f')
|
1652
|
+
f
|
1653
|
+
sage: var('n')
|
1654
|
+
n
|
1655
|
+
sage: T = transducers.Recursion([
|
1656
|
+
....: f(2*n + 1) == f(n) + 1,
|
1657
|
+
....: f(2*n) == f(n),
|
1658
|
+
....: f(0) == 2],
|
1659
|
+
....: 2, f, n)
|
1660
|
+
sage: for t in T.transitions():
|
1661
|
+
....: print([x.parent() for x in t.word_out])
|
1662
|
+
[]
|
1663
|
+
[Integer Ring]
|
1664
|
+
sage: [x.parent() for x in T.states()[0].final_word_out]
|
1665
|
+
[Integer Ring]
|
1666
|
+
|
1667
|
+
In contrast, if ``output_rings`` is set to the empty list, the
|
1668
|
+
results are not converted::
|
1669
|
+
|
1670
|
+
sage: T = transducers.Recursion([ # needs sage.symbolic
|
1671
|
+
....: f(2*n + 1) == f(n) + 1,
|
1672
|
+
....: f(2*n) == f(n),
|
1673
|
+
....: f(0) == 2],
|
1674
|
+
....: 2, f, n, output_rings=[])
|
1675
|
+
sage: for t in T.transitions(): # needs sage.symbolic
|
1676
|
+
....: print([x.parent() for x in t.word_out])
|
1677
|
+
[]
|
1678
|
+
[Symbolic Ring]
|
1679
|
+
sage: [x.parent() for x in T.states()[0].final_word_out] # needs sage.symbolic
|
1680
|
+
[Symbolic Ring]
|
1681
|
+
|
1682
|
+
Finally, we use a somewhat questionable conversion::
|
1683
|
+
|
1684
|
+
sage: T = transducers.Recursion([ # needs sage.rings.finite_rings sage.symbolic
|
1685
|
+
....: f(2*n + 1) == f(n) + 1,
|
1686
|
+
....: f(2*n) == f(n),
|
1687
|
+
....: f(0) == 0],
|
1688
|
+
....: 2, f, n, output_rings=[GF(5)])
|
1689
|
+
sage: for t in T.transitions(): # needs sage.rings.finite_rings sage.symbolic
|
1690
|
+
....: print([x.parent() for x in t.word_out])
|
1691
|
+
[]
|
1692
|
+
[Finite Field of size 5]
|
1693
|
+
|
1694
|
+
.. TODO::
|
1695
|
+
|
1696
|
+
Extend the method to
|
1697
|
+
|
1698
|
+
- non-integral bases,
|
1699
|
+
|
1700
|
+
- higher dimensions.
|
1701
|
+
|
1702
|
+
ALGORITHM:
|
1703
|
+
|
1704
|
+
See [HKP2015]_, Section 6. However, there are also recursion
|
1705
|
+
transitions for states of level `<\kappa` if the recursion rules
|
1706
|
+
allow such a transition. Furthermore, the intermediate step of a
|
1707
|
+
non-deterministic transducer is left out by implicitly using
|
1708
|
+
recursion transitions. The well-posedness is checked in a
|
1709
|
+
truncated version of the recursion digraph.
|
1710
|
+
|
1711
|
+
TESTS:
|
1712
|
+
|
1713
|
+
The following tests fail due to missing or superfluous recursions
|
1714
|
+
or initial conditions. ::
|
1715
|
+
|
1716
|
+
sage: var('n') # needs sage.symbolic
|
1717
|
+
n
|
1718
|
+
sage: function('f') # needs sage.symbolic
|
1719
|
+
f
|
1720
|
+
sage: transducers.Recursion([f(2*n) == f(n)], # needs sage.symbolic
|
1721
|
+
....: 2, f, n)
|
1722
|
+
Traceback (most recent call last):
|
1723
|
+
...
|
1724
|
+
ValueError: Missing recursions for input congruent to
|
1725
|
+
[1] modulo 2.
|
1726
|
+
|
1727
|
+
::
|
1728
|
+
|
1729
|
+
sage: transducers.Recursion([f(2*n + 1) == f(n), # needs sage.symbolic
|
1730
|
+
....: f(4*n) == f(2*n) + 1,
|
1731
|
+
....: f(2*n) == f(n) + 1],
|
1732
|
+
....: 2, f, n)
|
1733
|
+
Traceback (most recent call last):
|
1734
|
+
...
|
1735
|
+
ValueError: Conflicting rules congruent to 0 modulo 4.
|
1736
|
+
|
1737
|
+
::
|
1738
|
+
|
1739
|
+
sage: transducers.Recursion([f(2*n + 1) == f(n) + 1, # needs sage.symbolic
|
1740
|
+
....: f(2*n) == f(n),
|
1741
|
+
....: f(0) == 0,
|
1742
|
+
....: f(42) == 42], 2, f, n)
|
1743
|
+
Traceback (most recent call last):
|
1744
|
+
...
|
1745
|
+
ValueError: Superfluous initial values for [42].
|
1746
|
+
|
1747
|
+
::
|
1748
|
+
|
1749
|
+
sage: transducers.Recursion([f(2*n + 1) == f(n) + 1, # needs sage.symbolic
|
1750
|
+
....: f(2*n) == f(n - 2) + 4,
|
1751
|
+
....: f(0) == 0], 2, f, n)
|
1752
|
+
Traceback (most recent call last):
|
1753
|
+
...
|
1754
|
+
ValueError: Missing initial values for [2].
|
1755
|
+
|
1756
|
+
Here is an example of a transducer with a conflicting rule
|
1757
|
+
(it cannot hold for `n = 0`)::
|
1758
|
+
|
1759
|
+
sage: T = transducers.Recursion([ # needs sage.symbolic
|
1760
|
+
....: f(2*n + 1) == f(n - 1),
|
1761
|
+
....: f(2*n) == f(n) + 1,
|
1762
|
+
....: f(1) == 1,
|
1763
|
+
....: f(0) == 0], 2, f, n)
|
1764
|
+
Traceback (most recent call last):
|
1765
|
+
...
|
1766
|
+
ValueError: Conflicting recursion for [0].
|
1767
|
+
"""
|
1768
|
+
from sage.graphs.digraph import DiGraph
|
1769
|
+
from sage.arith.srange import srange
|
1770
|
+
|
1771
|
+
if is_zero is None:
|
1772
|
+
is_zero = lambda x: not x
|
1773
|
+
RuleRight = namedtuple('Rule', ['k', 's', 't'])
|
1774
|
+
initial_values = {}
|
1775
|
+
rules = []
|
1776
|
+
if input_alphabet is None and base in ZZ:
|
1777
|
+
input_alphabet = list(srange(base.abs()))
|
1778
|
+
|
1779
|
+
for equation in recursions:
|
1780
|
+
if isinstance(equation, self.RecursionRule):
|
1781
|
+
rules.append(equation)
|
1782
|
+
elif isinstance(equation, tuple) and len(equation) == 2:
|
1783
|
+
initial_values[equation[0]] = equation[1]
|
1784
|
+
else:
|
1785
|
+
parsed = self._parse_recursion_equation_(
|
1786
|
+
equation, base, function, var, word_function, output_rings)
|
1787
|
+
if isinstance(parsed, dict):
|
1788
|
+
initial_values.update(parsed)
|
1789
|
+
elif isinstance(parsed, self.RecursionRule):
|
1790
|
+
rules.append(parsed)
|
1791
|
+
else:
|
1792
|
+
assert False
|
1793
|
+
|
1794
|
+
max_K = max(rule.K for rule in rules)
|
1795
|
+
|
1796
|
+
residues = [[None for r in range(base**k)]
|
1797
|
+
for k in range(max_K + 1)]
|
1798
|
+
|
1799
|
+
# Aim: residues[K][R] = RuleRight(k, s, t)
|
1800
|
+
# if and only if
|
1801
|
+
# f(base^K n + R) = f(base^k n + s) + t
|
1802
|
+
|
1803
|
+
for given_rule in rules:
|
1804
|
+
q, remainder = given_rule.r.quo_rem(base**given_rule.K)
|
1805
|
+
rule = self.RecursionRule(K=given_rule.K,
|
1806
|
+
r=remainder,
|
1807
|
+
k=given_rule.k,
|
1808
|
+
s=given_rule.s - base**given_rule.k * q,
|
1809
|
+
t=given_rule.t)
|
1810
|
+
for m in range(max_K - rule.K + 1):
|
1811
|
+
for ell in range(base**m):
|
1812
|
+
R = rule.r + base**rule.K * ell
|
1813
|
+
if residues[rule.K + m][R] is not None:
|
1814
|
+
raise ValueError(
|
1815
|
+
"Conflicting rules congruent to %d modulo %d."
|
1816
|
+
% (R, base**(rule.K + m)))
|
1817
|
+
residues[rule.K + m][R] = RuleRight(k=rule.k + m,
|
1818
|
+
s=rule.s + ell * base**rule.k,
|
1819
|
+
t=rule.t)
|
1820
|
+
|
1821
|
+
missing_residues = [R
|
1822
|
+
for R, rule in enumerate(residues[max_K])
|
1823
|
+
if rule is None]
|
1824
|
+
if missing_residues:
|
1825
|
+
raise ValueError("Missing recursions for input congruent "
|
1826
|
+
"to %s modulo %s." % (missing_residues,
|
1827
|
+
base**max_K))
|
1828
|
+
|
1829
|
+
required_initial_values = set()
|
1830
|
+
|
1831
|
+
def recursion_transition(carry, level, force_nonnegative_target):
|
1832
|
+
"""
|
1833
|
+
Compute recursion transition leaving state ``(carry, level)``.
|
1834
|
+
|
1835
|
+
INPUT:
|
1836
|
+
|
1837
|
+
- ``carry`` -- integer
|
1838
|
+
|
1839
|
+
- ``level`` -- integer
|
1840
|
+
|
1841
|
+
- ``force_nonnegative_target`` -- boolean; if ``True``, only
|
1842
|
+
recursion transitions leading to a nonnegative carry are
|
1843
|
+
returned
|
1844
|
+
|
1845
|
+
OUTPUT:
|
1846
|
+
|
1847
|
+
A tuple ``((new_carry, new_level), output)`` if a recursion
|
1848
|
+
transition matching the specifications exists; otherwise,
|
1849
|
+
``None``.
|
1850
|
+
"""
|
1851
|
+
if level >= max_K:
|
1852
|
+
K = max_K
|
1853
|
+
else:
|
1854
|
+
K = level
|
1855
|
+
m, r = ZZ(carry).quo_rem(base**K)
|
1856
|
+
rule = residues[K][r]
|
1857
|
+
if rule is None:
|
1858
|
+
return None
|
1859
|
+
new_carry = base**rule.k * m + rule.s
|
1860
|
+
new_level = rule.k + level - K
|
1861
|
+
if new_carry + base**new_level < 0:
|
1862
|
+
return None
|
1863
|
+
if new_carry < 0 and force_nonnegative_target:
|
1864
|
+
return None
|
1865
|
+
if new_carry < 0 and carry >= 0:
|
1866
|
+
required_initial_values.add(carry)
|
1867
|
+
return (new_carry, new_level), rule.t
|
1868
|
+
|
1869
|
+
def recursion_transitions(carry, level, force_nonnegative_target):
|
1870
|
+
"""
|
1871
|
+
Compute the target and output of a maximal path of
|
1872
|
+
recursion transitions starting at state ``(carry, level)``.
|
1873
|
+
|
1874
|
+
INPUT:
|
1875
|
+
|
1876
|
+
- ``carry`` -- integer
|
1877
|
+
|
1878
|
+
- ``level`` -- integer
|
1879
|
+
|
1880
|
+
- ``force_nonnegative_target`` -- boolean; if ``True``, only
|
1881
|
+
recursion transitions leading to a nonnegative carry are
|
1882
|
+
allowed
|
1883
|
+
|
1884
|
+
OUTPUT:
|
1885
|
+
|
1886
|
+
A tuple ``((new_carry, new_level), output)``.
|
1887
|
+
"""
|
1888
|
+
(c, j) = (carry, level)
|
1889
|
+
output = []
|
1890
|
+
while True:
|
1891
|
+
transition = recursion_transition(
|
1892
|
+
c, j, force_nonnegative_target)
|
1893
|
+
if transition is None:
|
1894
|
+
break
|
1895
|
+
(c, j) = transition[0]
|
1896
|
+
output += transition[1]
|
1897
|
+
|
1898
|
+
return ((c, j), output)
|
1899
|
+
|
1900
|
+
def transition_function(states2, input):
|
1901
|
+
(state_carry, state_level) = states2
|
1902
|
+
((carry, level), output) = recursion_transitions(
|
1903
|
+
state_carry, state_level, False)
|
1904
|
+
# no more recursion transition is possible,
|
1905
|
+
# so this is now a storing transition
|
1906
|
+
carry += input * base**level
|
1907
|
+
level += 1
|
1908
|
+
# We now may proceed along recursion transitions
|
1909
|
+
# as long as the carries stay nonnegative.
|
1910
|
+
((carry, level), new_output) = recursion_transitions(
|
1911
|
+
carry, level, True)
|
1912
|
+
return ((carry, level), output + new_output)
|
1913
|
+
|
1914
|
+
T = Transducer(transition_function,
|
1915
|
+
initial_states=[(0, 0)],
|
1916
|
+
input_alphabet=input_alphabet)
|
1917
|
+
|
1918
|
+
def edge_recursion_digraph(n):
|
1919
|
+
r"""
|
1920
|
+
Compute the list of outgoing edges of ``n`` in the recursion digraph.
|
1921
|
+
|
1922
|
+
INPUT:
|
1923
|
+
|
1924
|
+
- ``n`` -- integer
|
1925
|
+
|
1926
|
+
OUTPUT:
|
1927
|
+
|
1928
|
+
A list ``[(A(n), label)]`` if `A(n)<\infty`; otherwise an empty list.
|
1929
|
+
"""
|
1930
|
+
m, r = ZZ(n).quo_rem(base**max_K)
|
1931
|
+
rule = residues[max_K][r]
|
1932
|
+
result = base**rule.k * m + rule.s
|
1933
|
+
if result >= 0:
|
1934
|
+
return [(result, rule.t)]
|
1935
|
+
else:
|
1936
|
+
return []
|
1937
|
+
|
1938
|
+
def f(n):
|
1939
|
+
"""
|
1940
|
+
Compute f(n) as defined by the recursion
|
1941
|
+
"""
|
1942
|
+
if n in initial_values:
|
1943
|
+
return initial_values[n]
|
1944
|
+
[(m, offset)] = edge_recursion_digraph(n)
|
1945
|
+
return offset + f(m)
|
1946
|
+
|
1947
|
+
carries = set(state.label()[0] for state in T.iter_states())
|
1948
|
+
|
1949
|
+
recursion_digraph = DiGraph(
|
1950
|
+
{carry: dict(edge_recursion_digraph(carry))
|
1951
|
+
for carry in carries
|
1952
|
+
if carry >= 0},
|
1953
|
+
multiedges=False)
|
1954
|
+
|
1955
|
+
initial_values_set = set(initial_values)
|
1956
|
+
|
1957
|
+
missing_initial_values = required_initial_values.difference(
|
1958
|
+
initial_values_set)
|
1959
|
+
|
1960
|
+
if missing_initial_values:
|
1961
|
+
raise ValueError(
|
1962
|
+
"Missing initial values for %s." %
|
1963
|
+
sorted(missing_initial_values))
|
1964
|
+
|
1965
|
+
for cycle in recursion_digraph.all_simple_cycles():
|
1966
|
+
assert cycle[0] is cycle[-1]
|
1967
|
+
cycle_set = set(cycle)
|
1968
|
+
intersection = cycle_set.intersection(initial_values_set)
|
1969
|
+
if not intersection:
|
1970
|
+
raise ValueError(
|
1971
|
+
"Missing initial condition for one of %s." %
|
1972
|
+
cycle[1:])
|
1973
|
+
if len(intersection) > 1:
|
1974
|
+
raise ValueError(
|
1975
|
+
"Too many initial conditions, only give one of %s." %
|
1976
|
+
cycle[1:])
|
1977
|
+
required_initial_values.update(intersection)
|
1978
|
+
output_sum = sum([e[2]
|
1979
|
+
for e in recursion_digraph.outgoing_edge_iterator(cycle[1:])],
|
1980
|
+
[])
|
1981
|
+
if not is_zero(output_sum):
|
1982
|
+
raise ValueError(
|
1983
|
+
"Conflicting recursion for %s." %
|
1984
|
+
cycle[1:])
|
1985
|
+
|
1986
|
+
superfluous_initial_values = initial_values_set.difference(
|
1987
|
+
required_initial_values)
|
1988
|
+
|
1989
|
+
if superfluous_initial_values:
|
1990
|
+
raise ValueError(
|
1991
|
+
"Superfluous initial values for %s." %
|
1992
|
+
sorted(superfluous_initial_values))
|
1993
|
+
|
1994
|
+
for state in T.iter_states():
|
1995
|
+
state.is_final = True
|
1996
|
+
if state.label()[0] >= 0:
|
1997
|
+
state.final_word_out = f(state.label()[0])
|
1998
|
+
else:
|
1999
|
+
state.final_word_out = ZZ(0)
|
2000
|
+
|
2001
|
+
return T
|
2002
|
+
|
2003
|
+
|
2004
|
+
# Easy access to the automaton and transducer generators from the command line:
|
2005
|
+
automata = AutomatonGenerators()
|
2006
|
+
transducers = TransducerGenerators()
|