From 6e4951023f2fbc9e5ad99ef25ea8c67be37ff08d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 24 Jul 2022 17:38:41 -0700 Subject: [PATCH] src/sage/combinat/finite_state_machine.py: Mark some doctests # optional - sage.symbolic --- src/sage/combinat/finite_state_machine.py | 168 +++++++++++----------- 1 file changed, 85 insertions(+), 83 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 2cec4270cb5..6a7161d24ab 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -985,12 +985,12 @@ def full_group_by(l, key=lambda x: x): EXAMPLES:: sage: from sage.combinat.finite_state_machine import full_group_by - sage: t = [2/x, 1/x, 2/x] - sage: r = full_group_by([0, 1, 2], key=lambda i:t[i]) - sage: sorted(r, key=lambda p:p[1]) + sage: t = [2/x, 1/x, 2/x] # optional - sage.symbolic + sage: r = full_group_by([0, 1, 2], key=lambda i: t[i]) # optional - sage.symbolic + sage: sorted(r, key=lambda p: p[1]) # optional - sage.symbolic [(2/x, [0, 2]), (1/x, [1])] sage: from itertools import groupby - sage: for k, elements in groupby(sorted([0, 1, 2], + sage: for k, elements in groupby(sorted([0, 1, 2], # optional - sage.symbolic ....: key=lambda i:t[i]), ....: key=lambda i:t[i]): ....: print("{} {}".format(k, list(elements))) @@ -4144,17 +4144,17 @@ def is_Markov_chain(self, is_zero=None): If the probabilities are variables in the symbolic ring, :func:`~sage.symbolic.assumptions.assume` will do the trick:: - sage: var('p q') + sage: var('p q') # optional - sage.symbolic (p, q) - sage: F = Transducer([(0, 0, p, 1), (0, 0, q, 0)], + sage: F = Transducer([(0, 0, p, 1), (0, 0, q, 0)], # optional - sage.symbolic ....: on_duplicate_transition=duplicate_transition_add_input) - sage: assume(p + q == 1) - sage: (p + q - 1).is_zero() + sage: assume(p + q == 1) # optional - sage.symbolic + sage: (p + q - 1).is_zero() # optional - sage.symbolic True - sage: F.is_Markov_chain() + sage: F.is_Markov_chain() # optional - sage.symbolic True - sage: forget() - sage: del(p, q) + sage: forget() # optional - sage.symbolic + sage: del(p, q) # optional - sage.symbolic If the probabilities are variables in some polynomial ring, the parameter ``is_zero`` can be used:: @@ -4353,9 +4353,9 @@ def default_format_transition_label(self, word): #. In the example above, ``'a'`` and ``'alpha'`` should perhaps be symbols:: - sage: var('a alpha a_1') + sage: var('a alpha a_1') # optional - sage.symbolic (a, alpha, a_1) - sage: print(T.default_format_transition_label([a, alpha, a_1])) + sage: print(T.default_format_transition_label([a, alpha, a_1])) # optional - sage.symbolic a \alpha a_{1} #. Example of an empty word:: @@ -5069,7 +5069,7 @@ def _matrix_(self, R=None): ....: 3:{'a':(0, 1), 2:(1, 1)}, ....: 4:{4:(1, 1), 3:(0, 1)}}, ....: initial_states=[0]) - sage: B._matrix_() + sage: B._matrix_() # optional - sage.symbolic [1 1 0 0 0] [0 0 1 1 0] [x 0 0 0 1] @@ -5111,7 +5111,7 @@ def adjacency_matrix(self, input=None, ....: 3:{'a':(0, 1), 2:(1, 1)}, ....: 4:{4:(1, 1), 3:(0, 1)}}, ....: initial_states=[0]) - sage: B.adjacency_matrix() + sage: B.adjacency_matrix() # optional - sage.symbolic [1 1 0 0 0] [0 0 1 1 0] [x 0 0 0 1] @@ -5120,7 +5120,7 @@ def adjacency_matrix(self, input=None, This is equivalent to:: - sage: matrix(B) + sage: matrix(B) # optional - sage.symbolic [1 1 0 0 0] [0 0 1 1 0] [x 0 0 0 1] @@ -5135,8 +5135,10 @@ def adjacency_matrix(self, input=None, [1 0 0 0 1] [0 1 1 0 0] [0 0 0 1 1] - sage: B.adjacency_matrix(1, entry=(lambda transition: - ....: exp(I*transition.word_out[0]*var('t')))) + sage: var('t') # optional - sage.symbolic + t + sage: B.adjacency_matrix(1, entry=(lambda transition: # optional - sage.symbolic + ....: exp(I*transition.word_out[0]*t))) [ 0 1 0 0 0] [ 0 0 0 1 0] [e^(I*t) 0 0 0 0] @@ -5148,7 +5150,7 @@ def adjacency_matrix(self, input=None, ....: (2, 1, 0)], ....: initial_states=[0], ....: final_states=[0]) - sage: a.adjacency_matrix() + sage: a.adjacency_matrix() # optional - sage.symbolic [0 1 0] [0 0 1] [1 1 0] @@ -9853,9 +9855,9 @@ def number_of_words(self, variable=None, ....: (0, 1, -1), (1, 0, 0)], ....: initial_states=[0], ....: final_states=[0, 1]) - sage: N = NAFpm.number_of_words(); N + sage: N = NAFpm.number_of_words(); N # optional - sage.symbolic 4/3*2^n - 1/3*(-1)^n - sage: all(len(list(NAFpm.language(s))) + sage: all(len(list(NAFpm.language(s))) # optional - sage.symbolic ....: - len(list(NAFpm.language(s-1))) == N.subs(n=s) ....: for s in srange(1, 6)) True @@ -9867,10 +9869,10 @@ def number_of_words(self, variable=None, sage: NAFp = Automaton([(0, 0, 0), (0, 1, 1), (1, 0, 0)], ....: initial_states=[0], ....: final_states=[0, 1]) - sage: N = NAFp.number_of_words(); N + sage: N = NAFp.number_of_words(); N # optional - sage.symbolic 1.170820393249937?*1.618033988749895?^n - 0.1708203932499369?*(-0.618033988749895?)^n - sage: all(len(list(NAFp.language(s))) + sage: all(len(list(NAFp.language(s))) # optional - sage.symbolic ....: - len(list(NAFp.language(s-1))) == N.subs(n=s) ....: for s in srange(1, 6)) True @@ -9881,13 +9883,13 @@ def number_of_words(self, variable=None, roots. :: sage: M = NAFp.adjacency_matrix(entry=lambda t: 1) - sage: M.characteristic_polynomial() + sage: M.characteristic_polynomial() # optional - sage.symbolic x^2 - x - 1 - sage: R. = NumberField(x^2-x-1, embedding=1.6) - sage: N = NAFp.number_of_words(base_ring=R); N + sage: R. = NumberField(x^2-x-1, embedding=1.6) # optional - sage.symbolic + sage: N = NAFp.number_of_words(base_ring=R); N # optional - sage.symbolic 1/2*(1/2*sqrt(5) + 1/2)^n*(3*sqrt(1/5) + 1) - 1/2*(-1/2*sqrt(5) + 1/2)^n*(3*sqrt(1/5) - 1) - sage: all(len(list(NAFp.language(s))) + sage: all(len(list(NAFp.language(s))) # optional - sage.symbolic ....: - len(list(NAFp.language(s-1))) == N.subs(n=s) ....: for s in srange(1, 6)) True @@ -9895,11 +9897,11 @@ def number_of_words(self, variable=None, In this special case, we might also use the constant :class:`golden_ratio `:: - sage: R. = NumberField(x^2-x-1, embedding=golden_ratio) - sage: N = NAFp.number_of_words(base_ring=R); N + sage: R. = NumberField(x^2-x-1, embedding=golden_ratio) # optional - sage.symbolic + sage: N = NAFp.number_of_words(base_ring=R); N # optional - sage.symbolic 1/5*(3*golden_ratio + 1)*golden_ratio^n - 1/5*(3*golden_ratio - 4)*(-golden_ratio + 1)^n - sage: all(len(list(NAFp.language(s))) + sage: all(len(list(NAFp.language(s))) # optional - sage.symbolic ....: - len(list(NAFp.language(s-1))) == N.subs(n=s) ....: for s in srange(1, 6)) True @@ -9917,9 +9919,9 @@ def number_of_words(self, variable=None, [4 1 0] [0 4 1] [0 0 4] - sage: N = J3.number_of_words(); N + sage: N = J3.number_of_words(); N # optional - sage.symbolic 1/2*4^(n - 2)*(n - 1)*n + 4^(n - 1)*n + 4^n - sage: all(len(list(J3.language(s))) + sage: all(len(list(J3.language(s))) # optional - sage.symbolic ....: - len(list(J3.language(s-1))) == N.subs(n=s) ....: for s in range(1, 6)) True @@ -9929,14 +9931,14 @@ def number_of_words(self, variable=None, sage: A = Automaton([(j, j+1, 0) for j in range(3)], ....: initial_states=[0], ....: final_states=list(range(3))) - sage: A.number_of_words() + sage: A.number_of_words() # optional - sage.symbolic 1/2*0^(n - 2)*(n - 1)*n + 0^(n - 1)*n + 0^n TESTS:: sage: A = Automaton([(0, 0, 0), (0, 1, 0)], ....: initial_states=[0]) - sage: A.number_of_words() + sage: A.number_of_words() # optional - sage.symbolic Traceback (most recent call last): ... NotImplementedError: Finite State Machine must be deterministic. @@ -10027,12 +10029,12 @@ def asymptotic_moments(self, variable=None): ....: final_states=[0]) sage: T([0, 1, 1]) [0, -1, -1] - sage: moments = T.asymptotic_moments() - sage: moments['expectation'] + sage: moments = T.asymptotic_moments() # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic -1/2*n + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 1/4*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic -1/4*n + Order(1) #. For the case of the Hamming weight of the non-adjacent-form @@ -10095,12 +10097,12 @@ def asymptotic_moments(self, variable=None): Now, we actually compute the asymptotic moments:: - sage: moments = NAFweight.asymptotic_moments() - sage: moments['expectation'] + sage: moments = NAFweight.asymptotic_moments() # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic 1/3*n + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 2/27*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic Order(1) #. This is Example 3.16 in [HKW2015]_, where a transducer with @@ -10111,17 +10113,17 @@ def asymptotic_moments(self, variable=None): :: - sage: var('a_1, a_2, a_3, a_4') + sage: var('a_1, a_2, a_3, a_4') # optional - sage.symbolic (a_1, a_2, a_3, a_4) - sage: T = Transducer([[0, 0, 0, a_1], [0, 1, 1, a_3], + sage: T = Transducer([[0, 0, 0, a_1], [0, 1, 1, a_3], # optional - sage.symbolic ....: [1, 0, 0, a_4], [1, 1, 1, a_2]], ....: initial_states=[0], final_states=[0, 1]) - sage: moments = T.asymptotic_moments() + sage: moments = T.asymptotic_moments() # optional - sage.symbolic verbose 0 (...) Non-integer output weights lead to significant performance degradation. - sage: moments['expectation'] + sage: moments['expectation'] # optional - sage.symbolic 1/4*(a_1 + a_2 + a_3 + a_4)*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic -1/4*(a_1 - a_2)*n + Order(1) Therefore, the asymptotic covariance vanishes if and only if @@ -10133,12 +10135,12 @@ def asymptotic_moments(self, variable=None): :ref:`example on Gray code `):: - sage: moments = transducers.GrayCode().asymptotic_moments() - sage: moments['expectation'] + sage: moments = transducers.GrayCode().asymptotic_moments() # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic 1/2*n + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 1/4*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic Order(1) #. This is the first part of Example 4.4 in [HKW2015]_, @@ -10154,12 +10156,12 @@ def asymptotic_moments(self, variable=None): Transition from () to (1,): 1|0, Transition from (1,) to (): 0|1, Transition from (1,) to (1,): 1|0] - sage: moments = block10.asymptotic_moments() - sage: moments['expectation'] + sage: moments = block10.asymptotic_moments() # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic 1/4*n + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 1/16*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic Order(1) #. This is the second part of Example 4.4 in [HKW2015]_, @@ -10175,16 +10177,16 @@ def asymptotic_moments(self, variable=None): Transition from () to (1,): 1|0, Transition from (1,) to (): 0|0, Transition from (1,) to (1,): 1|1] - sage: var('N') + sage: var('N') # optional - sage.symbolic N - sage: moments = block11.asymptotic_moments(N) - sage: moments['expectation'] + sage: moments = block11.asymptotic_moments(N) # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic 1/4*N + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 5/16*N + Order(1) - sage: correlation = (moments['covariance'].coefficient(N) / + sage: correlation = (moments['covariance'].coefficient(N) / # optional - sage.symbolic ....: (1/2 * sqrt(moments['variance'].coefficient(N)))) - sage: correlation + sage: correlation # optional - sage.symbolic 2/5*sqrt(5) #. This is Example 4.5 in [HKW2015]_, counting the number of @@ -10205,12 +10207,12 @@ def asymptotic_moments(self, variable=None): Transition from 1 to 0: 1|0, Transition from 2 to 2: 0|0, Transition from 2 to 0: 1|1] - sage: moments = T.asymptotic_moments() - sage: moments['expectation'] + sage: moments = T.asymptotic_moments() # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic Order(1) #. The finite state machine must have a unique final component:: @@ -10285,26 +10287,26 @@ def asymptotic_moments(self, variable=None): sage: T = Transducer([[0, 0, 0, 0], [0, 0, 1, -1/2]], ....: initial_states=[0], final_states=[0]) - sage: moments = T.asymptotic_moments() + sage: moments = T.asymptotic_moments() # optional - sage.symbolic verbose 0 (...) Non-integer output weights lead to significant performance degradation. - sage: moments['expectation'] + sage: moments['expectation'] # optional - sage.symbolic -1/4*n + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 1/16*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic -1/8*n + Order(1) This warning can be silenced by :func:`~sage.misc.verbose.set_verbose`:: sage: from sage.misc.verbose import set_verbose sage: set_verbose(-1, "finite_state_machine.py") - sage: moments = T.asymptotic_moments() - sage: moments['expectation'] + sage: moments = T.asymptotic_moments() # optional - sage.symbolic + sage: moments['expectation'] # optional - sage.symbolic -1/4*n + Order(1) - sage: moments['variance'] + sage: moments['variance'] # optional - sage.symbolic 1/16*n + Order(1) - sage: moments['covariance'] + sage: moments['covariance'] # optional - sage.symbolic -1/8*n + Order(1) sage: set_verbose(0, "finite_state_machine.py") @@ -10319,7 +10321,7 @@ def asymptotic_moments(self, variable=None): ....: initial_states=[s], final_states=[s]) sage: T([0, 0]) [2, 1, 2, 1, 2] - sage: T.asymptotic_moments()['expectation'] + sage: T.asymptotic_moments()['expectation'] # optional - sage.symbolic 3*n + Order(1) The same test for non-integer output:: @@ -10328,7 +10330,7 @@ def asymptotic_moments(self, variable=None): sage: s = FSMState(0, word_out=2/3) sage: T = Transducer([(s, s, 0, 1/2)], ....: initial_states=[s], final_states=[s]) - sage: T.asymptotic_moments()['expectation'] + sage: T.asymptotic_moments()['expectation'] # optional - sage.symbolic verbose 0 (...) Non-integer output weights lead to significant performance degradation. 7/6*n + Order(1) @@ -10352,14 +10354,6 @@ def asymptotic_moments(self, variable=None): Periodica Mathematica Hungarica Vol. 55 (1), 2007, pp. 81--96, :doi:`10.1007/s10998-007-3081-z`. """ - from sage.calculus.functional import derivative - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.rings.rational_field import QQ - from sage.symbolic.ring import SR - - if variable is None: - variable = SR.symbol('n') - if self.input_alphabet is None: raise ValueError("No input alphabet is given. " "Try calling determine_alphabets().") @@ -10387,6 +10381,14 @@ def asymptotic_moments(self, variable=None): "whose unique final component is " "aperiodic.") + from sage.calculus.functional import derivative + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.rational_field import QQ + from sage.symbolic.ring import SR + + if variable is None: + variable = SR.symbol('n') + def get_matrix(fsm, x, y): return fsm.adjacency_matrix( entry=lambda transition: x**sum(transition.word_in) *