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.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. 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()